Integrate the WebOffice service into your web application so that multiple users can preview and edit documents stored in OSS in real time, with changes synchronized automatically.
Features
Multiple users can preview or edit the same document in real time. Key capabilities include:
-
Auto-sync and save -- Updates are automatically synchronized and saved to prevent content loss.
-
Version history -- Versions are saved in real time. You can restore a document to a specified historical version.
-
Cross-platform -- Works on all platforms including mobile phones, computers, and web pages.
-
Easy to use -- Anyone familiar with office software can get started immediately.
-
Custom configurations -- Configure component status, events, features for text documents, and features for table documents.
-
Cached preview -- Disables online collaborative editing during a preview to prevent content updates from appearing. In cached preview mode, ongoing edits do not affect the previewed content. For configuration details, see GenerateWebofficeToken API documentation.
Supported file formats
The following file formats are supported for preview and editing. For a complete list of document format limits, see Document limits.
| File type | Preview | Editing |
|---|---|---|
| Documents (Word) | .doc, .dot, .wps, .wpt, .docx, .dotx, .docm, .dotm, .rtf | .doc, .dot, .wps, .wpt, .docx, .dotx, .docm, .dotm |
| Presentations (PowerPoint) | .ppt, .pptx, .pptm, .ppsx, .ppsm, .pps, .potx, .potm, .dpt, .dps | .ppt, .pptx, .pptm, .ppsx, .ppsm, .pps, .potx, .potm, .dpt, .dps |
| Spreadsheets (Excel) | .xls, .xlt, .et, .xlsx, .xltx, .csv, .xlsm, .xltm | .xls, .xlt, .et, .xlsx, .xltx, .xlsm, .xltm |
| PDF files (PDF) | Not supported |
PDF files support only online preview and do not support editing. You must set the Readonly parameter in the Permission parameter to true.
Architecture
Integrating the WebOffice service of IMM requires changes to both the server-side and frontend code of your web application.
The source document must be stored in OSS. The frontend calls server-side operations to obtain the collaboration URL and AccessToken, then uses JS-SDK to initialize the editor (dynamically generating an iframe under the specified block element) and sets the AccessToken to enable document preview or editing.
Prerequisites
-
An AccessKey pair is created and obtained. For more information, see Obtain an AccessKey pair.
-
Object Storage Service (OSS) is activated, a bucket is created, and documents are uploaded to the bucket. For more information, see Upload objects.
-
Intelligent Media Management (IMM) is activated. For more information, see Activate IMM.
-
A project is created in the IMM console. For more information, see Create a project.
Note-
You can call the CreateProject operation to create a project. For more information, see CreateProject - Create a project.
-
You can call the ListProjects - List all project information operation to list all projects created in the specified region.
-
-
If the domain name of the OSS bucket differs from the domain name of the preview service, configure cross-origin resource sharing (CORS) for the bucket to allow access from the preview service domain. For more information, see Configure CORS.
Limitations
| Limitation | Details |
|---|---|
| WebOffice URL cannot be opened directly | The URL returned by GenerateWebofficeToken cannot be opened in a browser directly. You must use JS-SDK to access it. |
| AccessToken validity | The AccessToken is valid for 30 minutes. You must open the preview before the AccessToken expires. Otherwise, the preview cannot be opened. |
| RefreshToken is single-use | The RefreshToken can be used only once and cannot be reused for refreshing. |
| PDF is preview-only | PDF files support only online preview, not editing. Set Readonly in Permission to true. |
| One instance per page | An HTML page supports only one WebOffice instance for document preview or editing. |
| Preview vs. edit mode | Document editing and preview call the same API via JS-SDK. To enable preview mode, set the Readonly parameter in Permission to true when calling GenerateWebofficeToken. |
Token lifecycle
| Token | Purpose | Validity | Refresh mechanism |
|---|---|---|---|
| AccessToken | Authenticates JS-SDK requests | 30 minutes | Call RefreshWebofficeToken on the server |
| RefreshToken | Obtains a new AccessToken | 1 day (single-use) | Single-use; each refresh returns a new RefreshToken |
JS-SDK automatically calls RefreshWebofficeToken 5 minutes before the token expires. Set the timeout value to greater than 20 minutes (20 x 60 x 1000 milliseconds) to prevent frequent token refreshing. The default token validity period is 30 minutes.
Token refreshing incurs API call charges. For billing details, see IMM Billing.
-
Projects created before May 1, 2023: Charged based on the number of times the collaboration URL is opened.
-
Projects created after May 1, 2023: Charged based on the number of API calls to GenerateWebofficeToken and RefreshWebofficeToken.
Step 1: Set up the server
Encapsulate the GenerateWebofficeToken and RefreshWebofficeToken operations on your server so that the frontend can obtain the collaboration URL and AccessToken.
The following example uses a project named test-project and an OSS file at oss://test-bucket/test-object.docx.
Call GenerateWebofficeToken to obtain the collaboration URL
Request example
{
"ProjectName": "test-project", // The name of the IMM project.
"SourceURI": "oss://test-bucket/test-object.docx", // The URI of the document in OSS.
"Filename": "test-object.docx", // The name of the document.
"UserData": "{\"fid\": \"123\"}", // The user data, which is returned in the MNS notification.
"PreviewPages": 3, // The number of preview pages.
"Permission": "{\"Rename\": \"true\", \"Readonly\": \"false\"}", // The permissions.
"User": "{\"Id\": \"test\", \"Name\": \"testuser\", \"Avatar\": \"http://xx.com/avatar.jpg\"}", // The user information displayed for online collaboration.
"Watermark": "{\"Type\": \"1\", \"Value\": \"imm\"}" // Set a watermark.
}
Response example
{
"RefreshToken": "f1fd1afd79ee445f95d3dd99f34f35ffv3",
"RequestId": "BC63D209-5E53-00E9-8D24-7043943DBC89",
"AccessToken": "3de242da81e1433abefbbea000aaae39v3",
"RefreshTokenExpiredTime": "2022-07-06T23:18:52.856132358Z",
"WebofficeURL": "https://office-cn-shanghai.imm.aliyuncs.com/office/w/7c1a7b53d6a4002751ac4bbaea69405a01475f4a?_w_tokentype=1",
"AccessTokenExpiredTime": "2022-07-05T23:48:52.856132358Z"
}
Complete sample code (IMM SDK for Python V1.27.3)
# -*- coding: utf-8 -*-
import os
from alibabacloud_imm20200930.client import Client as imm20200930Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_imm20200930 import models as imm_20200930_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient
class Sample:
def __init__(self):
pass
@staticmethod
def create_client(
access_key_id: str,
access_key_secret: str,
) -> imm20200930Client:
"""
Use the AccessKey ID and AccessKey secret to initialize a client.
@param access_key_id:
@param access_key_secret:
@return: Client
@throws Exception
"""
config = open_api_models.Config(
access_key_id=access_key_id,
access_key_secret=access_key_secret
)
# Specify the domain name you want to access.
config.endpoint = f'imm.cn-shenzhen.aliyuncs.com'
return imm20200930Client(config)
@staticmethod
def main() -> None:
# The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to perform operations is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M.
# We recommend that you do not include your AccessKey pair (AccessKey ID and AccessKey secret) in your project code for data security reasons.
# In this example, the AccessKey pair is read from the environment variables to implement identity verification for API access. For information about how to configure environment variables, visit https://www.alibabacloud.com/help/en/imm/developer-reference/configure-environment-variables?spm=a3c0i.29367734.6737026690.8.6d266e9bORGXvg.
imm_access_key_id = os.getenv("AccessKeyId")
imm_access_key_secret = os.getenv("AccessKeySecret")
client = Sample.create_client(imm_access_key_id, imm_access_key_secret)
# Set a watermark.
weboffice_watermark = imm_20200930_models.WebofficeWatermark(
type=1,
value='imm'
)
# Set the collaborator information.
weboffice_user = imm_20200930_models.WebofficeUser(
id='test',
name='testuser',
avatar='http://xx.com/avatar.jpg'
)
# Set permissions.
weboffice_permission = imm_20200930_models.WebofficePermission(
rename=True
)
get_weboffice_urlrequest = imm_20200930_models.GenerateWebofficeTokenRequest(
# Set the IMM project name.
project_name='test-project',
# Set the URI of the document for collaboration.
source_uri='oss://test-bucket/test-object.docx',
# Set the name of the document.
filename='test-object.docx',
# Set user data.
user_data='{"fid": "123"}',
preview_pages=3,
external_uploaded=False,
permission=weboffice_permission,
user=weboffice_user,
watermark=weboffice_watermark
)
runtime = util_models.RuntimeOptions()
try:
# Print the return value of the API operation.
response = client.get_weboffice_urlwith_options(get_weboffice_urlrequest, runtime)
print(response.body.to_map())
except Exception as error:
# Print the error message if necessary.
UtilClient.assert_as_string(error.message)
print(error)
if __name__ == '__main__':
Sample.main()
Call RefreshWebofficeToken to refresh the AccessToken
When an AccessToken expires, the frontend calls the RefreshWebofficeToken operation on the server to obtain a new one. The response format matches the GenerateWebofficeToken response.
Step 2: Set up the frontend with JS-SDK
Use JS-SDK to mount the collaboration URL to a block element in your HTML page and configure the AccessToken.
Import JS-SDK
In the following example, ${x.y.z} is a placeholder for the JS-SDK version number. Replace it with the actual latest version. For version information, see JS-SDK versions.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Demo</title>
</head>
<body>
<script src="https://g.alicdn.com/IMM/office-js/${x.y.z}/aliyun-web-office-sdk.min.js"></script>
<script>
console.log('After importing, the JS-SDK is available for use!');
console.log(aliyun); //Global variable name.
</script>
</body>
</html>
Initialize the JS-SDK
The frontend obtains the tokenInfo object by calling the GenerateWebofficeToken operation on your server.
The following example assumes the tokenInfo object matches the GenerateWebofficeToken response structure. The server endpoint /getTokenInfo represents your server-side GenerateWebofficeToken wrapper.
// Obtain the collaboration URL and AccessToken.
var tokenInfo = await $.get('http://example.com/getTokenInfo')
// Initialization
let instance = aliyun.config({
url: tokenInfo.WebofficeURL // Set the document collaboration URL.
})
Set the iframe mount point
After the DOMContentLoaded event is triggered, make sure the mount node exists before you perform initialization.
By default, the iframe is mounted to the body element. To specify a custom mount point:
let instance = aliyun.config({
mount: document.querySelector('#container'),
url: 'The document collaboration URL' // The document collaboration URL tokenInfo.WebofficeURL from Step 2b.
})
To customize the iframe object, access the DOM object through the JS-SDK instance:
var instance = aliyun.config({
mount: document.querySelector('#container')
//...
})
console.log(instance.iframe)
Set the token
Set the token to access the collaboration URL. You must call this method again each time the token is refreshed.
The timeout value must be greater than 20 minutes (20 x 60 x 1000 milliseconds). The default token validity period is 30 minutes. JS-SDK refreshes the token 5 minutes before expiry. Setting the timeout too low causes frequent token refreshing and may incur additional charges.
// Obtain the token by using an asynchronous request or template output based on your business requirements.
var token = 'yourToken';
// Specify the token.
instance.setToken({
token: token,
timeout: 25 * 60 * 1000, // The token validity period. This parameter is required. Unit: milliseconds. In this example, the token validity period is set to 25 minutes. You can use the refreshToken function to refresh the token 5 minutes before it expires.
})
Set up automatic token refresh
The frontend obtains the tokenInfo object by calling the RefreshWebofficeToken operation on your server. The following example assumes the tokenInfo object matches the RefreshWebofficeToken response structure.
Pass in a function that obtains the token. When the token validity period expires, JS-SDK automatically calls this function. The function must return a promise or a plain object containing the new token.
The following example uses the server endpoint /refreshTokenInfo to represent your server-side RefreshWebofficeToken wrapper.
The refreshToken function does not support async functions. Only a promise or a {token, timeout} object can be returned.
// Cache the last tokenInfo for token refreshing.
let lastTokenInfo = tokenInfo
// Obtain the token function.
// Note: refreshToken does not support async functions. Only promise or {token,timeout} objects can be returned.
const refreshToken = function() {
return new Promise(function(resolve){
// The business processing logic. Call the refreshToken operation that is encapsulated on the server.
$.get('http://example.com/refreshTokenInfo',{
RefreshToken: lastTokenInfo.RefreshToken,
AccessToken: lastTokenInfo.AccessToken,
//....
}).then(function(tokenInfo){
lastTokenInfo = tokenInfo
resolve({
token: tokenInfo.AccessToken, // This parameter is required.
timeout: 25 * 60 * 1000, // The token validity period. This parameter is required. Unit: milliseconds. In this example, the token validity period is set to 25 minutes. You can use the refreshToken function to refresh the token 5 minutes before it expires.
})
})
})
}
// Configure the function that obtains the token.
aliyun.config({
//...
refreshToken
})
Complete example
The following is a complete JS-SDK example for document preview:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>WebOffice Preview Demo</title>
<style>
#container {
width: 100%;
height: 100vh;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://g.alicdn.com/IMM/office-js/${x.y.z}/aliyun-web-office-sdk.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script>
(async function() {
// Step 1: Obtain the collaboration URL and AccessToken from your server.
var tokenInfo = await $.get('http://example.com/getTokenInfo')
// Step 2: Cache the tokenInfo for token refreshing.
let lastTokenInfo = tokenInfo
// Step 3: Define the token refresh function.
// Note: refreshToken does not support async functions. Only promise or {token,timeout} objects can be returned.
const refreshToken = function() {
return new Promise(function(resolve){
$.get('http://example.com/refreshTokenInfo', {
RefreshToken: lastTokenInfo.RefreshToken,
AccessToken: lastTokenInfo.AccessToken,
}).then(function(tokenInfo){
lastTokenInfo = tokenInfo
resolve({
token: tokenInfo.AccessToken,
timeout: 25 * 60 * 1000,
})
})
})
}
// Step 4: Initialize the JS-SDK.
let instance = aliyun.config({
mount: document.querySelector('#container'),
url: tokenInfo.WebofficeURL,
refreshToken
})
// Step 5: Set the initial token.
instance.setToken({
token: tokenInfo.AccessToken,
timeout: 25 * 60 * 1000,
})
})()
</script>
</body>
</html>
Replace ${x.y.z} with the actual JS-SDK version number and replace http://example.com/getTokenInfo and http://example.com/refreshTokenInfo with your actual server endpoints.