This topic describes how to use the HarmonyOS SDK to upload media files from a local device to ApsaraVideo VOD storage.
The HarmonyOS upload SDK is currently in public preview. Do not use it directly in production environments. If you must deploy it in production, perform thorough testing to ensure stability and compatibility.
Prerequisites
DevEco Studio version 5.0 Release or later.
HarmonyOS SDK API Version 12 or later.
A HarmonyOS device that supports audio and video, running HarmonyOS NEXT 5.0.0.102 or later.
Integrate the SDK
Integration methods
Integrate using the ohpm package manager (recommended)
In your terminal, navigate to your application module directory (for example, the entry directory) and run the following command:
ohpm install @aliyun_video_cloud/vod-upload-sdkOr use the shorthand command:
ohpm i @aliyun_video_cloud/vod-upload-sdkThis command automatically downloads the SDK and its dependencies (such as @aliyun/oss) and adds them to the dependencies node in your module’s oh-package.json5 file.
Manual integration
You can also manually add the dependency to your module’s oh-package.json5 file. To install from the official ohpm repository, use the following configuration:
"dependencies": {
"@aliyun_video_cloud/vod-upload-sdk": "^1.0.3", // Use the latest stable version
}If you manually downloaded a HAR package for local reference:
"dependencies": {
"@aliyun_video_cloud/vod-upload-sdk": "file:path/to/your/vod-upload-sdk.har", // Replace with the actual path to your HAR package
}After saving the file, run ohpm install in your project root directory to install all dependencies:
ohpm installConfigure permissions
Request permissions
In your application’s main module configuration file (src/main/module.json5), add the following required permission declarations:
Replace .EntryAbility in the example with the actual Ability name used by your app. Media read/write permissions (such as ohos.permission.READ_IMAGE and ohos.permission.READ_VIDEO) can be configured based on the file types your app supports.
{
"module": {
// ... other configurations
"requestPermissions": [
{
"name": "ohos.permission.INTERNET", // Required for internet access during upload
"usedScene": {
"ability": [
".EntryAbility" // Replace with your Ability name
],
"when": "inuse"
}
},
{
"name": "ohos.permission.READ_MEDIA", // Required to read local media files for upload
"usedScene": {
"ability": [
".EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.WRITE_MEDIA", // May be needed to write media files (e.g., copy to cache)
"usedScene": {
"ability": [
".EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.GET_NETWORK_INFO", // Required to get network status for optimized upload strategy
"usedScene": {
"ability": [
".EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.KEEP_BACKGROUND_RUNNING", // Allows uploads to continue when the screen is off or the app is in the background
"usedScene": {
"ability": [
".EntryAbility"
],
"when": "always"
}
}
]
}
}Declare background mode
Under the abilities node, add the backgroundModes declaration to your main Ability to ensure file uploads can continue in the background.
// src/main/module.json5
"abilities": [
{
"name": "EntryAbility",
// ... other configurations
"backgroundModes": [
"dataTransfer" // Declares data transfer mode, suitable for file uploads
]
}
]Basic setup
Initialize the upload instance
First, review the overall client-side upload workflow and deploy an authorization service based on your chosen method:
For the upload URL and credential method, you must obtain an upload URL and credential from your authorization service.
For the STS Token method, you must obtain an STS Token from your authorization service.
Initializing the upload instance involves the following steps:
Obtain credentials: The client-side upload workflow requires credentials to validate upload requests. The SDK supports the following credential methods:
ImportantRegardless of the method used, your server must interact with Alibaba Cloud VOD to obtain or refresh these credentials. For more information, see Obtain upload URLs and credentials for audio and video, Obtain upload URLs and credentials for images, and Refresh video upload credentials.
Upload URL and credential method
The application server calls the
CreateUploadVideo(for videos or audio) orCreateUploadImage(for images) API of the VOD service to obtain a one-time upload URL (UploadAddress) and an upload credential (UploadAuth).The client retrieves
uploadAuthanduploadAddressfrom your application server via theonUploadStartedcallback and uses this information to complete the upload. This method offers high security and centralized permission control on your application server, making it ideal for production deployments.
STS Token method
Your application server requests a temporary security token (AccessKeyId, AccessKeySecret, SecurityToken) from Alibaba Cloud Security Token Service (STS) and delivers it to the client.
The client provides the STS Token (AccessKeyId, AccessKeySecret, SecurityToken, ExpireTime) during initialization. The SDK uses this token internally to call VOD APIs (such as CreateUploadVideo/Image) or upload directly to OSS.
AK/SK method
The client uses the permanent AccessKeyId and AccessKeySecret of an Alibaba Cloud account or RAM user. The SDK uses these credentials internally to call VOD APIs. This method carries a risk of AK/SK leakage and should only be used for development and testing.
Initialize the upload instance: Use the appropriate
initmethod to initialize aVODUploadClientinstance based on your chosen authentication method.Upload URL and credential method
import { VODUploadClient, VODUploadClientImpl } from '@aliyun_video_cloud/vod-upload-sdk'; import { VODUploadCallback } from '@aliyun_video_cloud/vod-upload-sdk'; import { common } from '@kit.AbilityKit'; // Assume you obtained context in UIAbility let uiAbilityContext: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; let uploader: VODUploadClient | null = null; let uploadCallback: VODUploadCallback; // Implement the VODUploadCallback interface // Create a callback instance (implementation details in the next section) uploadCallback = { onUploadSucceed: (info, result) => { /* ... */ }, onUploadFailed: (info, code, message) => { /* ... */ }, onUploadProgress: (info, uploadedSize, totalSize) => { /* ... */ }, onUploadTokenExpired: () => { /* ... */ }, onUploadRetry: (code, message) => { /* ... */ }, onUploadRetryResume: () => { /* ... */ }, onUploadStarted: (info) => { /* ... */ } }; // Initialize the upload instance uploader = new VODUploadClientImpl(uiAbilityContext); // Upload URL and credential method (recommended) // With this method, the SDK requests uploadAuth and uploadAddress from your app server in the onUploadStarted callback await uploader.init(uploadCallback);STS Token method
import { VODUploadClient, VODUploadClientImpl } from '@aliyun_video_cloud/vod-upload-sdk'; import { VODUploadCallback } from '@aliyun_video_cloud/vod-upload-sdk'; import { common } from '@kit.AbilityKit'; // Assume you obtained context in UIAbility let uiAbilityContext: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; let uploader: VODUploadClient | null = null; let uploadCallback: VODUploadCallback; // Implement the VODUploadCallback interface // Create a callback instance (implementation details in the next section) uploadCallback = { onUploadSucceed: (info, result) => { /* ... */ }, onUploadFailed: (info, code, message) => { /* ... */ }, onUploadProgress: (info, uploadedSize, totalSize) => { /* ... */ }, onUploadTokenExpired: () => { /* ... */ }, onUploadRetry: (code, message) => { /* ... */ }, onUploadRetryResume: () => { /* ... */ }, onUploadStarted: (info) => { /* ... */ } }; // Initialize the upload instance uploader = new VODUploadClientImpl(uiAbilityContext); // STS Token method // Pass the temporary AccessKeyId, AccessKeySecret, SecurityToken, and ExpireTime obtained from STS await uploader.init( "STS_ACCESS_KEY_ID", "STS_ACCESS_KEY_SECRET", "STS_SECURITY_TOKEN", "STS_EXPIRE_TIME_ISO8601", // ISO8601-formatted expiration time of the STS credential, e.g., "2025-12-31T23:59:59Z" uploadCallback );AK/SK method
import { VODUploadClient, VODUploadClientImpl } from '@aliyun_video_cloud/vod-upload-sdk'; import { VODUploadCallback } from '@aliyun_video_cloud/vod-upload-sdk'; import { common } from '@kit.AbilityKit'; // Assume you obtained context in UIAbility let uiAbilityContext: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; let uploader: VODUploadClient | null = null; let uploadCallback: VODUploadCallback; // Implement the VODUploadCallback interface // Create a callback instance (implementation details in the next section) uploadCallback = { onUploadSucceed: (info, result) => { /* ... */ }, onUploadFailed: (info, code, message) => { /* ... */ }, onUploadProgress: (info, uploadedSize, totalSize) => { /* ... */ }, onUploadTokenExpired: () => { /* ... */ }, onUploadRetry: (code, message) => { /* ... */ }, onUploadRetryResume: () => { /* ... */ }, onUploadStarted: (info) => { /* ... */ } }; // Initialize the upload instance uploader = new VODUploadClientImpl(uiAbilityContext); // AK/SK method (for development and testing only) // Pass your AccessKeyId and AccessKeySecret directly await uploader.init("YOUR_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_SECRET", uploadCallback);Set up the upload status callback class:
VODUploadCallbackhandles upload events. Implement the following methods to manage upload status.import { VODUploadCallback, UploadFileInfo, VodUploadResult } from '@aliyun_video_cloud/vod-upload-sdk'; import { VodUploadErrorType } from '@aliyun_video_cloud/vod-upload-sdk'; // Error type enumeration class MyUploadCallback implements VODUploadCallback { onUploadSucceed(info: UploadFileInfo, result: VodUploadResult): void { console.info(`Upload succeeded: ${info.getFileName()}, VideoId: ${result.getVideoId()}, ImageUrl: ${result.getImageUrl()}`); // Use videoId or imageUrl from result for subsequent operations } onUploadFailed(info: UploadFileInfo, code: string, message: string): void { console.error(`Upload failed: ${info.getFileName()}, Code: ${code}, Message: ${message}`); // Handle failures based on error code and message, such as prompting the user to retry or check credentials } onUploadProgress(info: UploadFileInfo, uploadedSize: number, totalSize: number): void { const progress = totalSize > 0 ? (uploadedSize / totalSize) * 100 : 0; console.info(`Upload progress: ${info.getFileName()} - ${progress.toFixed(2)}% (${uploadedSize}/${totalSize})`); // Update the UI progress bar } onUploadTokenExpired(): void { console.warn("Upload token expired. Need to refresh credentials."); // In this callback, request new credentials from your app server, // then call uploader.resumeWithAuth(newAuth) or uploader.resumeWithToken(...) based on your initialization method } onUploadRetry(code: string, message: string): void { console.warn(`Upload will retry. Code: ${code}, Message: ${message}`); // Show a retry prompt in the UI } onUploadRetryResume(): void { console.info("Upload retry resumed."); // Remove the retry prompt from the UI } onUploadStarted(uploadFileInfo: UploadFileInfo): void { console.info(`Upload started: ${uploadFileInfo.getFileName()}`); // If you initialized the SDK using the upload URL and credential method (i.e., uploader.init(callback)), // request uploadAuth and uploadAddress from your app server in this callback, // then call uploader.setUploadAuthAndAddress(uploadFileInfo, uploadAuth, uploadAddress); // Example: // myAppServer.getUploadCredentials(uploadFileInfo.getFileName()) // .then(credentials => { // uploader.setUploadAuthAndAddress(uploadFileInfo, credentials.uploadAuth, credentials.uploadAddress); // }) // .catch(error => { // console.error("Failed to get upload credentials from server:", error); // uploader.onUploadFailed(uploadFileInfo, VodUploadErrorType.VOD_API_ERROR, "Failed to get upload credentials."); // }); } }Create an upload request function: Use the
addFilemethod to add files to the upload queue. You can specify VOD-related metadata for each file.ImportantIn HarmonyOS, file selection is typically implemented using the
pickerAPI, which returns a file URI. Because the VOD upload SDK’saddFilemethod requires a local file path, you must copy the selected URI (which starts withcontent://) to your app’s sandbox cache directory.import { VodInfo, UploadFileInfo } from '@aliyun_video_cloud/vod-upload-sdk'; import { picker } from '@kit.CoreFileKit'; import { common } from '@kit.AbilityKit'; import { FileUtils } from '@aliyun_video_cloud/vod-upload-sdk'; // File utility class provided by the SDK // Assume the uploader instance is already initialized async function selectAndAddFiles(uiAbilityContext: common.UIAbilityContext) { const docPicker = new picker.DocumentViewPicker(); const selectOptions: picker.DocumentSelectOptions = { maxSelectNumber: 5, // Allow selecting up to 5 files fileTypes: ['video/*', 'image/*', 'audio/*'] // Restrict file types }; try { const selectedUris = await docPicker.select(selectOptions); if (selectedUris && selectedUris.length > 0) { for (const uri of selectedUris) { const originalFileName = FileUtils.getFileName(uri); const tempFileName = `${Date.now()}_${Math.random().toString(36).substring(2, 8)}_${originalFileName}`; const cacheDirectory = uiAbilityContext.cacheDir; // Get the app cache directory const localTempFilePath = `${cacheDirectory}/${tempFileName}`; // Copy the file to the app sandbox cache directory so the SDK can access it try { await FileUtils.copyUriToFile(uiAbilityContext, uri, localTempFilePath); const fileSize = await FileUtils.getFileSize(localTempFilePath); // Construct VodInfo with media metadata const vodInfo = new VodInfo(); vodInfo.setTitle(`HarmonyOS upload-${originalFileName}`); vodInfo.setFileName(originalFileName); // Set the original filename; the SDK usually extracts this from the path vodInfo.setFileSize(String(fileSize)); vodInfo.setDescription("Uploaded via HarmonyOS SDK"); vodInfo.setTags("HarmonyOS,Demo,Upload"); vodInfo.setCateId(19); // Replace with your VOD category ID // vodInfo.setCoverUrl("http://your.cdn.com/cover.jpg"); // Optional: specify a cover URL // vodInfo.setUserData("{\"key\":\"value\"}"); // Optional: custom data for features like upload acceleration // Add the file to the upload queue uploader.addFile(localTempFilePath, vodInfo); console.info(`Added file to queue: ${originalFileName}, path: ${localTempFilePath}`); } catch (copyError) { console.error(`Failed to copy file ${originalFileName} to cache:`, copyError); // Handle file copy failure, such as notifying the user } } } else { console.info("No files selected."); } } catch (error) { console.error("File selection failed:", error); } }The
VodInfoobject contains metadata for the uploaded file. Key properties include the following:NoteAdvanced configuration parameters (such as
storageLocation,templateGroupId, andworkflowId) can be configured later in the advanced settings or set for each file usingVodInfo.Field
Type
Description
titlestringVideo or image title.
fileNamestringOriginal filename, including extension. Used by VOD for recordkeeping. The SDK usually extracts this from the file path.
descriptionstringDescription.
tagsstringTags, separated by commas.
appIdstringApplication ID.
cateIdnumberCategory ID.
coverUrlstringCover URL.
partSizenumberPart size for multipart upload of a single file. Takes precedence over the global
setPartSizesetting..
imageTypestringThe type of the image.
default(normal image) or
cover(video thumbnail). This parameter is valid only when you upload an image.
templateGroupIdstringThe ID of the transcoding template group. This parameter is related to
workflowId.at the same time,
workflowIdtakes precedence.
workflowIdstringWorkflow ID.
storageLocationstringStorage address.
userDatastringCustom settings as a JSON string, used for advanced features like upload acceleration.
Start the upload: Call the
start()method to begin uploading. The SDK processes files in the order they were added.// Assume the uploader instance is initialized and files have been added via addFile // Start uploading uploader.start();If the SDK was initialized using the credential method, calling
start()triggers theonUploadStartedcallback for any file that does not yet have an Endpoint/Bucket/Object configured. In this callback, asynchronously fetch the file’suploadAuthanduploadAddress, then calluploader.setUploadAuthAndAddress(uploadFileInfo, uploadAuth, uploadAddress).Once a file begins uploading to OSS, the
onUploadProgresscallback updates upload progress in real time.
Handle results:
Video upload: On success, the
onUploadSucceedcallback returns aVodUploadResultobject containing aVideoId, which you can use to retrieve a playback URL. For more information, see Obtain a playback URL.Image upload: On success, the
onUploadSucceedcallback returns aVodUploadResultobject containing anImageUrl. If URL signing is enabled, the imageUrl includes a time-to-live (TTL). For more information, see Configure URL signing.
Queue management
VODUploadClient supports sequential upload of multiple files and provides the following methods to manage the upload queue:
VODUploadClient supports multi-file uploads. However, if you use the upload credential and address method, you must configure credentials separately for each file. Because multi-file uploads increase code complexity, we recommend uploading only one file at a time.
Delete an upload file from the queue. If the file being deleted is currently uploading, the upload is canceled and the next file in the queue starts uploading automatically.
deleteFile(index: number): voidClear the entire upload queue. If any files are uploading, those uploads are canceled.
clearFiles(): voidObtain the list of files in the upload queue.
listFiles(): Array<UploadFileInfo>Cancel the upload of the file at the specified index. The file remains in the upload list but is marked as canceled. If the file is currently uploading, the upload is canceled and the next file in the queue starts uploading automatically.
cancelFile(index: number): voidRestore a file previously marked as canceled (via
cancelFile) or failed, at the specified index, so it can be re-uploaded. The SDK attempts to upload it the next time it processes the queue or afterstart()is called.resumeFile(index: number): void
Upload control
VODUploadClient supports the following upload control methods:
Stop all upload activity. If any files are uploading, those uploads are canceled. After calling
stop(), files in the queue (those not yet successful, failed, or canceled) reset to the initial (READY) state and wait for the nextstart()call.stop(): voidPause all ongoing uploads. The SDK state changes to
PAUSED. Any OSS part already started continues to completion, but no new parts begin uploading.pause(): voidResume uploads previously paused with
pause(). The SDK state changes toSTARTEDand continues uploading from the pause point.resume(): void
Advanced settings
VODUploadClient supports advanced settings such as callback handling, timeout handling, multipart upload configuration, specifying storage addresses, resumable upload, cellular network upload policies, upload acceleration, transcoding configuration, VOD service region, and application ID.
Callback handling
VODUploadClient uses the VODUploadCallback interface to handle various events:
Upload failure (
onUploadFailed)Triggered when a file upload fails. The callback includes an error code (
code) and error message (message) for troubleshooting and user notifications. Error codes may originate from VOD APIs, OSS services, or the SDK itself. For more information, see Common error codes and Exception responses (iOS SDK).Upload credential expiration (
onUploadTokenExpired)Triggered when an STS Token or VOD upload credential (UploadAuth) expires. After receiving this callback, fetch new credentials from your application server.
If the STS Token expires (affecting VOD API calls or direct OSS uploads), call
uploader.resumeWithToken(newStsAk, newStsSk, newToken, newExpireTime).If the VOD upload credential (UploadAuth) expires, call
uploader.resumeWithAuth(newUploadAuth).
Upload retry (
onUploadRetry/onUploadRetryResume): In theonUploadRetrycallback, your app can notify the user that a retry is in progress or stop the upload by callingcancelFile()orstop()after a certain number of retries.onUploadRetry: Triggered before the SDK automatically retries a VOD API call or OSS upload due to a retryable error (such as a network timeout).onUploadRetryResume: Triggered when a retry succeeds and the upload resumes.
Timeout handling
The SDK lets you configure HTTP request timeouts and maximum retry counts.
import { VodHttpClientConfig } from '@aliyun_video_cloud/vod-upload-sdk';
const httpClientConfig = new VodHttpClientConfig();
httpClientConfig.setConnectionTimeout(15000); // Connection timeout in milliseconds (default: 15000)
httpClientConfig.setSocketTimeout(30000); // Socket read timeout in milliseconds (default: 30000)
httpClientConfig.setMaxRetryCount(3); // Maximum HTTP request retries (default: 3)
if (uploader) { // Ensure uploader is initialized
uploader.setVodHttpClientConfig(httpClientConfig);
}Multipart upload settings
The SDK enables multipart upload by default. You can adjust multipart behavior as follows:
setPartSize(partSize: number): void: Sets the part size in bytes. The default is 1 MB (1 × 1024 × 1024 bytes). Files larger than this size are automatically split into parts.
// uploader is initialized uploader.setPartSize(2 * 1024 * 1024); // Set to 2 MBsetDisableMultiPart(disable: boolean): void: Set to
trueto disable multipart upload. All files are then uploaded using a simple PUT Object request. Use this when you know files are small or multipart is unnecessary. The default isfalse(multipart enabled).// uploader is initialized uploader.setDisableMultiPart(true); // Disable multipart upload
Specify storage address
The VODUploadClient instance lets you specify a storage address for files uploaded through the VOD workflow (which requires calling CreateUploadVideo/Image to obtain credentials). The storage address refers to the storage location identifier configured in the VOD console. The OSS Bucket’s Region and Endpoint are returned by the VOD service.
This is a global setting that affects all subsequent VOD workflow uploads unless overridden by specifying
storageLocationinVodInfo.This setting does not affect direct OSS uploads (when
addFileis called with endpoint, bucket, and objectKey).
// uploader is initialized
uploader.setStorageLocation("outin-xxxx.oss-cn-shanghai.aliyuncs.com"); // Obtain from the VOD consoleResumable upload
The SDK enables resumable upload by default. This feature relies on recording upload progress.
This feature works only for multipart uploads. It has no effect for small files that are not split into parts or when multipart upload is disabled via setDisableMultiPart(true).
setRecordUploadProgressEnabled(enabled: boolean): void: Enables or disables recording upload progress for resumable upload. The default is
true(enabled).// uploader is initialized uploader.setRecordUploadProgressEnabled(true); // Ensure it is enabled
Cellular network upload policy
The SDK can automatically pause uploads when using a cellular network.
This feature requires:
Your app has the
ohos.permission.GET_NETWORK_INFOpermission.The SDK network listener module is running normally.
When the network switches back to Wi-Fi or Ethernet, uploads automatically resume (only for pauses triggered by this policy). For manual pauses via pause(), you must call resume() to resume.
setPauseOnCellular(enabled: boolean): void: Enables or disables automatic upload pause on cellular networks. The default is
false(no automatic pause).// uploader is initialized uploader.setPauseOnCellular(true); // Enable automatic pause on cellular networksisPauseOnCellularEnabled(): boolean: Checks whether automatic pause on cellular networks is currently enabled.
Upload acceleration
You can enable upload acceleration for large files (GB or TB scale) or cross-region uploads. For more information about the setup, see How to enable. After enabling, when building a VodInfo object, use the setUserData method to set a JSON string that contains the acceleration configuration, as shown in the following example:
const vodInfo = new VodInfo();
// ... other VodInfo settings ...
const userData = {
"Vod": { // Ensure the top-level key is "Vod"
"UserData": { // Custom user data
"AccelerateConfig": { // Upload acceleration configuration
"Type": "oss", // Fixed value: "oss"
"Domain": "your-accelerate-domain.oss-accelerate.aliyuncs.com" // Your accelerate domain
}
}
}
};
vodInfo.setUserData(JSON.stringify(userData));Parameter descriptions
Name | Type | Description |
Type | string | Acceleration type (only "oss" is supported). |
Domain | string | The accelerate domain assigned by Alibaba Cloud for your Bucket (HTTPS by default). |
Configure transcoding
For audio and video files uploaded through the VOD workflow, you can specify a transcoding template group ID or workflow ID to enable automatic transcoding.
If both
WorkflowIdandTemplateGroupIdare set,WorkflowIdtakes precedence.This setting applies globally to all subsequent VOD workflow files unless overridden in
VodInfo.The client upload SDK does not support workflow-based image transcoding. Use image styles or a separate image processing service instead.
setTemplateGroupId(templateGroupId: string): void: Sets the transcoding template group ID.
// uploader is initialized uploader.setTemplateGroupId("your-template-group-id");setWorkflowId(workflowId: string): void: Sets the workflow ID.
// uploader is initialized uploader.setWorkflowId("your-workflow-id");
Set the VOD service region
You can set the region for VOD service API access and specify the corresponding region for OSS uploads when using STS credentials (applies when upload credentials do not include OSS Region information). The default is "cn-shanghai".
setRegion(region: string): void
// uploader is initialized uploader.setRegion("cn-beijing"); // For example, set to China (Beijing)
Set the application ID
You can use this method to set an application ID, which uploads media resources to a specific application (useful when using the ApsaraVideo VOD multi-application system).
This setting applies globally to all subsequent VOD workflow files unless overridden by specifying appId in VodInfo.
setAppId(appId: string): void
// uploader is initialized uploader.setAppId("app-yourxxxxid");