• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  getFileName(filePath: string): string {
52    const lastSlashIndex = filePath.lastIndexOf('/');
53    if (lastSlashIndex === -1) {
54      return filePath;
55    }
56    return filePath.substring(lastSlashIndex + 1);
57  }
58
59  async uploadFilesBackground(fileUris: Array<string>): Promise<void> {
60    logger.info(TAG, `uploadFilesBackground:uploadFiles begin, ${JSON.stringify(fileUris)}`);
61    this.context = getContext(this) as common.UIAbilityContext;
62    if (fileUris.length === 0) {
63      return;
64    }
65    fileUris.forEach((item: string) => {
66      this.waitList.push(item);
67    });
68  }
69
70  async flushBackgroundTask() {
71    logger.info(TAG, 'flushBackgroundTask:uploadFiles begin');
72    let tasks = await request.agent.search({
73      state: request.agent.State.RUNNING
74    });
75    let state = AppStorage.get<number>('backTaskState');
76    if (state === BackgroundTaskState.RUNNING) {
77      if (tasks.length < TASK_MAX && this.waitList.length > 0) {
78        this.createBackgroundTask(this.waitList);
79        this.waitList = [];
80      } else {
81        if (this.backgroundTask === undefined || tasks.indexOf(this.backgroundTask.tid) === -1) {
82          AppStorage.setOrCreate('backTaskState', BackgroundTaskState.NONE);
83          this.backgroundTask = undefined;
84        }
85      }
86    }
87  }
88
89  async createBackgroundTask(fileUris: Array<string>) {
90    logger.info(TAG, `createBackgroundTask:uploadFiles begin, ${JSON.stringify(fileUris)}`);
91    if (this.context === undefined) {
92      return;
93    }
94    this.config.url = await urlUtils.getUrl(this.context);
95    this.config.data = await this.getFilesAndData(this.context.cacheDir, fileUris);
96    this.config.mode = request.agent.Mode.BACKGROUND;
97    try {
98      this.backgroundTask = await request.agent.create(this.context, this.config);
99      await this.backgroundTask.start();
100      let state = AppStorage.get<number>('backTaskState');
101      if (state === BackgroundTaskState.PAUSE) {
102        await this.backgroundTask.pause();
103      }
104      logger.info(TAG, `createBackgroundTask success`);
105    } catch (err) {
106      logger.error(TAG, `task  err, err  = ${JSON.stringify(err)}`);
107    }
108  }
109
110  async uploadFiles(fileUris: Array<string>, callback: (progress: number, isSucceed: boolean) => void): Promise<void> {
111    logger.info(TAG, `uploadFiles begin, ${JSON.stringify(fileUris)}`);
112    if (fileUris.length === 0) {
113      return;
114    }
115    // 查询到存在正在执行的上传任务,提示并返回
116    let tasks = await request.agent.search({
117      state: request.agent.State.RUNNING,
118      action: request.agent.Action.UPLOAD,
119      mode: request.agent.Mode.FOREGROUND
120    });
121    if (tasks.length > 0) {
122      promptAction.showToast({ message: $r('app.string.have_upload_task_tips'), bottom: TOAST_BOTTOM });
123      return;
124    }
125    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
126    this.config.data = await this.getFilesAndData(context.cacheDir, fileUris);
127    logger.info(TAG, `config data:${JSON.stringify(this.config.data)}`);
128    this.config.url = await urlUtils.getUrl(context);
129    logger.info(TAG, `config url:${JSON.stringify(this.config.url)}`);
130    this.config.mode = request.agent.Mode.FOREGROUND;
131    try {
132      this.uploadTask = await request.agent.create(context, this.config);
133      this.uploadTask.on('progress', (progress: request.agent.Progress) => {
134        logger.info(TAG, `progress,  progress = ${progress.processed} ${progress.state}`);
135        let processed = Number(progress.processed.toString()).valueOf();
136        let size = progress.sizes[0];
137        let process: number = Math.floor(processed / size * 100);
138        if (process < 100) {
139          callback(process, false);
140        }
141      });
142      this.uploadTask.on('completed', (progress: request.agent.Progress) => {
143        logger.info(TAG, `complete,  progress = ${progress.processed} ${progress.state}`);
144        callback(100, true);
145        this.cancelTask();
146      });
147      this.uploadTask.on('failed', async (progress: request.agent.Progress) => {
148        if (this.uploadTask) {
149          let taskInfo = await request.agent.touch(this.uploadTask.tid, UPLOAD_TOKEN);
150          logger.info(TAG, `fail,  resean = ${taskInfo.reason}, faults = ${JSON.stringify(taskInfo.faults)}`);
151        }
152        callback(100, false);
153        this.cancelTask();
154      });
155      await this.uploadTask.start();
156    } catch (err) {
157      logger.error(TAG, `task  err, err  = ${JSON.stringify(err)}`);
158      callback(100, false);
159    }
160  }
161
162  async uploadFileChunk(fileUris: Array<string>, begin: number, end: number, callback: (progress: number, isSucceed: boolean) => void): Promise<void> {
163    logger.info(TAG, `uploadFileChunk begin,${fileUris}, begin:${begin}, end:${end}`);
164    // 查询到存在正在执行的上传任务,提示并返回
165    let tasks = await request.agent.search({
166      state: request.agent.State.RUNNING,
167      action: request.agent.Action.UPLOAD,
168      mode: request.agent.Mode.FOREGROUND
169    });
170    if (tasks.length > 0) {
171      promptAction.showToast({ message: $r('app.string.have_upload_task_tips'), bottom: TOAST_BOTTOM });
172      return;
173    }
174    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
175    let config: request.agent.Config = {
176      action: request.agent.Action.UPLOAD,
177      headers: HEADER,
178      url: await urlUtils.getUrl(context),
179      mode: request.agent.Mode.FOREGROUND,
180      method: 'POST',
181      title: 'upload',
182      network: request.agent.Network.ANY,
183      data: await this.getFilesAndData(context.cacheDir, fileUris),
184      token: UPLOAD_TOKEN,
185      index: 0, // 指定片段上传的文件
186      begins: begin,
187      ends: end
188    };
189
190    try {
191      this.uploadTask = await request.agent.create(context, config);
192      this.uploadTask.on('progress', (progress: request.agent.Progress) => {
193        let processed = Number(progress.processed.toString()).valueOf();
194        let size = progress.sizes[progress.index];
195        if (progress.index === 0) {
196          if (end === -1) {
197            size -= begin;
198          } else {
199            size = end - begin + 1;
200          }
201        }
202        logger.info(TAG, `progress,  index:${progress.index}, progressed = ${progress.processed}, size=${size}`);
203        let process: number = Math.floor(processed / size * 100);
204        if (process < 100) {
205          callback(process, false);
206        }
207      });
208      this.uploadTask.on('completed', (progress: request.agent.Progress) => {
209        logger.info(TAG, `complete,  progress = ${progress.processed} ${progress.state}`);
210        callback(100, true);
211        this.cancelTask();
212      });
213      this.uploadTask.on('failed', async (progress: request.agent.Progress) => {
214        if (this.uploadTask) {
215          let taskInfo = await request.agent.touch(this.uploadTask.tid, UPLOAD_TOKEN);
216          logger.info(TAG, `fail,  resean = ${taskInfo.reason}, faults = ${JSON.stringify(taskInfo.faults)}`);
217        }
218        callback(100, false);
219        this.cancelTask();
220      })
221      await this.uploadTask.start();
222    } catch (err) {
223      logger.error(TAG, `task  err, err  = ${JSON.stringify(err)}`);
224      callback(100, false);
225    }
226  }
227
228  async cancelTask() {
229    logger.info(TAG, 'cancelTask');
230    if (this.uploadTask === undefined) {
231      return;
232    }
233    try {
234      this.uploadTask.off('progress');
235      this.uploadTask.off('failed');
236      this.uploadTask.off('completed');
237      await this.uploadTask.stop();
238      await request.agent.remove(this.uploadTask.tid);
239    } catch (err) {
240      logger.info(TAG, `deleteTask fail,err= ${JSON.stringify(err)}`);
241    }
242    this.uploadTask = undefined;
243  }
244
245  async pauseOrResume() {
246    logger.info(TAG, 'pauseOrResume');
247    let state = AppStorage.get<number>('backTaskState');
248    if (state === BackgroundTaskState.RUNNING) {
249      await this.pause();
250      AppStorage.setOrCreate('backTaskState', BackgroundTaskState.PAUSE);
251    } else if (state === BackgroundTaskState.PAUSE) {
252      await this.resume();
253      AppStorage.setOrCreate('backTaskState', BackgroundTaskState.RUNNING);
254    } else {
255      logger.info(TAG, 'this task state is error');
256    }
257  }
258
259  async pause() {
260    logger.info(TAG, 'pause');
261    if (this.backgroundTask === undefined) {
262      return;
263    }
264    try {
265      await this.backgroundTask.pause();
266    } catch (err) {
267      logger.info(TAG, `pause fail,err= ${JSON.stringify(err)}`);
268    }
269  }
270
271  async resume() {
272    logger.info(TAG, 'resume');
273    if (this.backgroundTask === undefined) {
274      return;
275    }
276    try {
277      await this.backgroundTask.resume();
278    } catch (err) {
279      logger.info(TAG, `resume fail,err= ${JSON.stringify(err)}`);
280    }
281  }
282
283  private async getFilesAndData(cacheDir: string, fileUris: Array<string>): Promise<Array<request.agent.FormItem>> {
284    logger.info(TAG, `getFilesAndData begin`);
285    let files: Array<request.agent.FormItem> = [];
286    for (let i = 0; i < fileUris.length; i++) {
287      logger.info(TAG, `getFile fileUri = ${fileUris[i]}`);
288      let imagePath = fileUris[i];
289      logger.info(TAG, `getFilesAndData  imagePath: ${JSON.stringify(imagePath)}`);
290      let fileName = this.getFileName(imagePath);
291      let file: request.agent.FormItem = {
292        name: 'createTest',
293        value: {
294          filename: fileName,
295          mimeType: 'application/octet-stream',
296          path: imagePath,
297        }
298      }
299      logger.info(TAG, `getFile file = ${JSON.stringify(file)}`);
300      files.push(file);
301    }
302    logger.info(TAG, `getFilesAndData ${JSON.stringify(files)}`);
303    return files;
304  }
305}
306
307export const requestUpload = new Upload();