1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import common from '@ohos.app.ability.common'; 17import request from '@ohos.request'; 18import promptAction from '@ohos.promptAction'; 19import { urlUtils } from '../utils/UrlUtils'; 20import { logger } from '../utils/Logger'; 21import { MediaUtils } from '../utils/MediaUtils'; 22import { BackgroundTaskState, UPLOAD_TOKEN, TOAST_BOTTOM, TASK_MAX } from '../utils/Constants'; 23 24const TAG: string = 'RequestUpload'; 25const HEADER: Record<string, string> = { 'Content-Type': 'multipart/form-data' }; 26 27class Upload { 28 private mediaUtils: MediaUtils = new MediaUtils(); 29 private config: request.agent.Config = { 30 action: request.agent.Action.UPLOAD, 31 headers: HEADER, 32 url: '', 33 mode: request.agent.Mode.FOREGROUND, 34 method: 'POST', 35 title: 'upload', 36 network: request.agent.Network.ANY, 37 data: [], 38 token: UPLOAD_TOKEN 39 } 40 private context: common.UIAbilityContext | undefined = undefined; 41 private uploadTask: request.agent.Task | undefined = undefined; 42 private backgroundTask: request.agent.Task | undefined = undefined; 43 private waitList: Array<string> = []; 44 45 constructor() { 46 setInterval(() => { 47 this.flushBackgroundTask() 48 }, 2000); 49 } 50 51 async uploadFilesBackground(fileUris: Array<string>): Promise<void> { 52 logger.info(TAG, `uploadFiles begin, ${JSON.stringify(fileUris)}`); 53 this.context = getContext(this) as common.UIAbilityContext; 54 if (fileUris.length === 0) { 55 return; 56 } 57 fileUris.forEach((item: string) => { 58 this.waitList.push(item); 59 }); 60 } 61 62 async flushBackgroundTask() { 63 let tasks = await request.agent.search({ 64 state: request.agent.State.RUNNING 65 }); 66 let state = AppStorage.get<number>('backTaskState'); 67 if (state === BackgroundTaskState.RUNNING) { 68 if (tasks.length < TASK_MAX && this.waitList.length > 0) { 69 this.createBackgroundTask(this.waitList); 70 this.waitList = []; 71 } else { 72 if (this.backgroundTask === undefined || tasks.indexOf(this.backgroundTask.tid) === -1) { 73 AppStorage.setOrCreate('backTaskState', BackgroundTaskState.NONE); 74 this.backgroundTask = undefined; 75 } 76 } 77 } 78 } 79 80 async createBackgroundTask(fileUris: Array<string>) { 81 if (this.context === undefined) { 82 return; 83 } 84 this.config.url = await urlUtils.getUrl(this.context); 85 this.config.data = await this.getFilesAndData(this.context.cacheDir, fileUris); 86 this.config.mode = request.agent.Mode.BACKGROUND; 87 try { 88 this.backgroundTask = await request.agent.create(this.context, this.config); 89 await this.backgroundTask.start(); 90 let state = AppStorage.get<number>('backTaskState'); 91 if (state === BackgroundTaskState.PAUSE) { 92 await this.backgroundTask.pause(); 93 } 94 logger.info(TAG, `createBackgroundTask success`); 95 } catch (err) { 96 logger.error(TAG, `task err, err = ${JSON.stringify(err)}`); 97 } 98 } 99 100 async uploadFiles(fileUris: Array<string>, callback: (progress: number, isSucceed: boolean) => void): Promise<void> { 101 logger.info(TAG, `uploadFiles begin, ${JSON.stringify(fileUris)}`); 102 if (fileUris.length === 0) { 103 return; 104 } 105 // 查询到存在正在执行的上传任务,提示并返回 106 let tasks = await request.agent.search({ 107 state: request.agent.State.RUNNING, 108 action: request.agent.Action.UPLOAD, 109 mode: request.agent.Mode.FOREGROUND 110 }); 111 if (tasks.length > 0) { 112 promptAction.showToast({ message: $r('app.string.have_upload_task_tips'), bottom: TOAST_BOTTOM }); 113 return; 114 } 115 let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; 116 this.config.data = await this.getFilesAndData(context.cacheDir, fileUris); 117 this.config.url = await urlUtils.getUrl(context); 118 this.config.mode = request.agent.Mode.FOREGROUND; 119 try { 120 this.uploadTask = await request.agent.create(context, this.config); 121 this.uploadTask.on('progress', (progress: request.agent.Progress) => { 122 logger.info(TAG, `progress, progress = ${progress.processed} ${progress.state}`); 123 let processed = Number(progress.processed.toString()).valueOf(); 124 let size = progress.sizes[0]; 125 let process: number = Math.floor(processed / size * 100); 126 if (process < 100) { 127 callback(process, false); 128 } 129 }); 130 this.uploadTask.on('completed', (progress: request.agent.Progress) => { 131 logger.info(TAG, `complete, progress = ${progress.processed} ${progress.state}`); 132 callback(100, true); 133 this.cancelTask(); 134 }); 135 this.uploadTask.on('failed', async (progress: request.agent.Progress) => { 136 if (this.uploadTask) { 137 let taskInfo = await request.agent.touch(this.uploadTask.tid, UPLOAD_TOKEN); 138 logger.info(TAG, `fail, resean = ${taskInfo.reason}, faults = ${JSON.stringify(taskInfo.faults)}`); 139 } 140 callback(100, false); 141 this.cancelTask(); 142 }); 143 await this.uploadTask.start(); 144 } catch (err) { 145 logger.error(TAG, `task err, err = ${JSON.stringify(err)}`); 146 callback(100, false); 147 } 148 } 149 150 async cancelTask() { 151 if (this.uploadTask === undefined) { 152 return; 153 } 154 try { 155 this.uploadTask.off('progress'); 156 this.uploadTask.off('failed'); 157 this.uploadTask.off('completed'); 158 await this.uploadTask.stop(); 159 await request.agent.remove(this.uploadTask.tid); 160 } catch (err) { 161 logger.info(TAG, `deleteTask fail,err= ${JSON.stringify(err)}`); 162 } 163 this.uploadTask = undefined; 164 } 165 166 async pauseOrResume() { 167 let state = AppStorage.get<number>('backTaskState'); 168 if (state === BackgroundTaskState.RUNNING) { 169 await this.pause(); 170 AppStorage.setOrCreate('backTaskState', BackgroundTaskState.PAUSE); 171 } else if (state === BackgroundTaskState.PAUSE) { 172 await this.resume(); 173 AppStorage.setOrCreate('backTaskState', BackgroundTaskState.RUNNING); 174 } else { 175 logger.info(TAG, 'this task state is error'); 176 } 177 } 178 179 async pause() { 180 logger.info(TAG, 'pause'); 181 if (this.backgroundTask === undefined) { 182 return; 183 } 184 try { 185 await this.backgroundTask.pause(); 186 } catch (err) { 187 logger.info(TAG, `pause fail,err= ${JSON.stringify(err)}`); 188 } 189 } 190 191 async resume() { 192 logger.info(TAG, 'resume'); 193 if (this.backgroundTask === undefined) { 194 return; 195 } 196 try { 197 await this.backgroundTask.resume(); 198 } catch (err) { 199 logger.info(TAG, `resume fail,err= ${JSON.stringify(err)}`); 200 } 201 } 202 203 private async getFilesAndData(cacheDir: string, fileUris: Array<string>): Promise<Array<request.agent.FormItem>> { 204 logger.info(TAG, `getFilesAndData begin`); 205 let files: Array<request.agent.FormItem> = []; 206 for (let i = 0; i < fileUris.length; i++) { 207 logger.info(TAG, `getFile fileUri = ${fileUris[i]}`); 208 let imagePath = await this.mediaUtils.copyFileToCache(cacheDir, fileUris[i]); 209 logger.info(TAG, `getFilesAndData ${JSON.stringify(imagePath)}`); 210 let file: request.agent.FormItem = { 211 name: imagePath.split('cache/')[1], 212 value: { 213 path: './' + imagePath.split('cache/')[1] 214 } 215 } 216 files.push(file); 217 } 218 logger.info(TAG, `getFilesAndData ${JSON.stringify(files)}`); 219 return files; 220 } 221} 222 223export const requestUpload = new Upload();