网络-上传和下载(ArkTS)
介绍
本示例使用@ohos.request接口创建上传和下载任务,实现上传、下载功能,hfs作为服务器,实现了文件的上传和下载和任务的查询功能。
效果预览
使用说明
1.本示例功能需要先配置服务器环境后使用,具体配置见[上传下载服务配置]。
2.首页展示上传和下载两个入口组件,点击进入对应的页面,如果要使用后台下载任务,请开启后台任务开关。
3.上传页面(请先在图库中确定已开启图库权限):
点击**+**,从相册选择拉起图库选择照片,图片选择页面支持拍照,选择照片后点击发表进行上传。
在首页中打开后台任务开关后,上传页面开启的是后台上传任务,后台任务在应用退出到后台时可以在通知栏看到任务状态。
4.下载页面:
点击文件列表选择要下载的文件后,点击下载选择指定路径后开始下载。
点击查看下载文件进入下载文件页面,点击文件夹查看文件夹内的文件。
在首页中打开后台任务开关后,下载页面开启的是后台下载任务,后台任务在应用退出到后台时可以在通知栏看到任务状态。
前台下载时只支持单文件下载,后台下载时支持选择多个文件下载。
具体实现
-
该示例分为两个模块:
-
上传模块
- 使用@ohos.request中接口agent.create创建上传任务,调用@ohos.request中的Task相关接口实现上传任务的创建、取消、进度加载,失败的任务会调用查询接口获取失败原因并打印在日志中,支持多个文件上传。
-
-
源码参考:[RequestUpload.ets]
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { request } from '@kit.BasicServicesKit';
import { urlUtils } from '../utils/UrlUtils';
import { logger } from '../utils/Logger';
import { MediaUtils } from '../utils/MediaUtils';
import { BackgroundTaskState, UPLOAD_TOKEN, TOAST_BOTTOM, TASK_MAX } from '../utils/Constants';
const TAG: string = 'RequestUpload';
const HEADER: Record<string, string> = { 'Content-Type': 'multipart/form-data' };
class Upload {
private mediaUtils: MediaUtils = new MediaUtils();
private config: request.agent.Config = {
action: request.agent.Action.UPLOAD,
headers: HEADER,
url: '',
mode: request.agent.Mode.FOREGROUND,
method: 'POST',
title: 'upload',
network: request.agent.Network.ANY,
data: [],
token: UPLOAD_TOKEN
}
private context: common.UIAbilityContext | undefined = undefined;
private uploadTask: request.agent.Task | undefined = undefined;
private backgroundTask: request.agent.Task | undefined = undefined;
private waitList: Array<string> = [];
progressCallback: Function | undefined = undefined;
completedCallback: Function | undefined = undefined;
failedCallback: Function | undefined = undefined;
constructor() {
setInterval(() => {
this.flushBackgroundTask()
}, 2000);
}
async uploadFilesBackground(fileUris: Array<string>): Promise<void> {
logger.info(TAG, `uploadFiles begin, ${JSON.stringify(fileUris)}`);
this.context = getContext(this) as common.UIAbilityContext;
if (fileUris.length === 0) {
return;
}
fileUris.forEach((item: string) => {
this.waitList.push(item);
});
}
async flushBackgroundTask() {
let tasks = await request.agent.search({
state: request.agent.State.RUNNING
});
let state = AppStorage.get<number>('backTaskState');
if (state === BackgroundTaskState.RUNNING) {
if (tasks.length < TASK_MAX && this.waitList.length > 0) {
this.createBackgroundTask(this.waitList);
this.waitList = [];
} else {
if (this.backgroundTask === undefined || tasks.indexOf(this.backgroundTask.tid) === -1) {
AppStorage.setOrCreate('backTaskState', BackgroundTaskState.NONE);
this.backgroundTask = undefined;
}
}
}
}
async createBackgroundTask(fileUris: Array<string>) {
if (this.context === undefined) {
return;
}
this.config.url = await urlUtils.getUrl(this.context);
this.config.data = await this.getFilesAndData(this.context.cacheDir, fileUris);
this.config.mode = request.agent.Mode.BACKGROUND;
try {
this.backgroundTask = await request.agent.create(this.context, this.config);
await this.backgroundTask.start();
let state = AppStorage.get<number>('backTaskState');
if (state === BackgroundTaskState.PAUSE) {
await this.backgroundTask.pause();
}
logger.info(TAG, `createBackgroundTask success`);
} catch (err) {
logger.error(TAG, `task err, err = ${JSON.stringify(err)}`);
}
}
async uploadFiles(fileUris: Array<string>, callback: (progress: number, isSucceed: boolean) => void): Promise<void> {
logger.info(TAG, `uploadFiles begin, ${JSON.stringify(fileUris)}`);
if (fileUris.length === 0) {
return;
}
// 查询到存在正在执行的上传任务,提示并返回
let tasks = await request.agent.search({
state: request.agent.State.RUNNING,
action: request.agent.Action.UPLOAD,
mode: request.agent.Mode.FOREGROUND
});
if (tasks.length > 0) {
promptAction.showToast({ message: $r('app.string.have_upload_task_tips'), bottom: TOAST_BOTTOM });
return;
}
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
this.config.data = await this.getFilesAndData(context.cacheDir, fileUris);
this.config.url = await urlUtils.getUrl(context);
this.config.mode = request.agent.Mode.FOREGROUND;
try {
this.uploadTask = await request.agent.create(context, this.config);
this.uploadTask.on('progress', this.progressCallback = (progress: request.agent.Progress) => {
logger.info(TAG, `progress, progress = ${progress.processed} ${progress.state}`);
let processed = Number(progress.processed.toString()).valueOf();
let size = progress.sizes[0];
let process: number = Math.floor(processed / size * 100);
if (process < 100) {
callback(process, false);
}
});
this.uploadTask.on('completed', this.completedCallback = (progress: request.agent.Progress) => {
logger.info(TAG, `complete, progress = ${progress.processed} ${progress.state}`);
callback(100, true);
this.cancelTask();
});
this.uploadTask.on('failed', this.failedCallback = async (progress: request.agent.Progress) => {
if (this.uploadTask) {
let taskInfo = await request.agent.touch(this.uploadTask.tid, UPLOAD_TOKEN);
logger.info(TAG, `fail, resean = ${taskInfo.reason}, faults = ${JSON.stringify(taskInfo.faults)}`);
}
callback(100, false);
this.cancelTask();
});
await this.uploadTask.start();
} catch (err) {
logger.error(TAG, `task err, err = ${JSON.stringify(err)}`);
callback(100, false);
}
}
async cancelTask() {
if (this.uploadTask === undefined) {
return;
}
try {
this.uploadTask.off('progress');
this.progressCallback = undefined;
this.uploadTask.off('failed');
this.failedCallback = undefined
this.uploadTask.off('completed');
this.completedCallback = undefined
await this.uploadTask.stop();
await request.agent.remove(this.uploadTask.tid);
} catch (err) {
logger.info(TAG, `deleteTask fail,err= ${JSON.stringify(err)}`);
}
this.uploadTask = undefined;
}
async pauseOrResume() {
let state = AppStorage.get<number>('backTaskState');
if (state === BackgroundTaskState.RUNNING) {
await this.pause();
AppStorage.setOrCreate('backTaskState', BackgroundTaskState.PAUSE);
} else if (state === BackgroundTaskState.PAUSE) {
await this.resume();
AppStorage.setOrCreate('backTaskState', BackgroundTaskState.RUNNING);
} else {
logger.info(TAG, 'this task state is error');
}
}
async pause() {
logger.info(TAG, 'pause');
if (this.backgroundTask === undefined) {
return;
}
try {
await this.backgroundTask.pause();
} catch (err) {
logger.info(TAG, `pause fail,err= ${JSON.stringify(err)}`);
}
}
async resume() {
logger.info(TAG, 'resume');
if (this.backgroundTask === undefined) {
return;
}
try {
await this.backgroundTask.resume();
} catch (err) {
logger.info(TAG, `resume fail,err= ${JSON.stringify(err)}`);
}
}
private async getFilesAndData(cacheDir: string, fileUris: Array<string>): Promise<Array<request.agent.FormItem>> {
logger.info(TAG, `getFilesAndData begin`);
let files: Array<request.agent.FormItem> = [];
for (let i = 0; i < fileUris.length; i++) {
logger.info(TAG, `getFile fileUri = ${fileUris[i]}`);
let imagePath = await this.mediaUtils.copyFileToCache(cacheDir, fileUris[i]);
logger.info(TAG, `getFilesAndData ${JSON.stringify(imagePath)}`);
let file: request.agent.FormItem = {
name: imagePath.split('cache/')