• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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*/
15import mediaLibrary from '@ohos.multimedia.mediaLibrary'
16import fs from '@ohos.file.fs'
17import request from '@ohos.request'
18import common from '@ohos.app.ability.common'
19import Logger from '../util/Logger'
20import { UploadFile } from '../feature/UploadFile'
21import emitter from '@ohos.events.emitter'
22import CostTimeCompute from '../feature/CostTimeCompute'
23import { getConfig, ConfigType } from '../util/ConfigUtil'
24
25const TAG: string = '[UploadController]'
26
27//控制下载任务
28//展示下载进度,展示下载控制器
29@Preview
30@Component
31export default struct UploadController {
32  @State imageUrl: string = "";
33  @State isDownloading: boolean = false;
34  @State isError: boolean = false;
35  @State errorMessage: string = "";
36  @State @Watch('onCompleteChanged') isCompleted: boolean = false;
37  @State progress: number = 0;
38  @State total: number = 1;
39  @Link isCompletedArr: Array<boolean>
40  uploadUrl: string = ""
41  innerEvent = {
42    eventId: 1
43  };
44  uploadTask: request.UploadTask;
45
46  //  剩余时间计算
47  @State costTimeStr: string = "1s";
48  @State lastTimeStr: string = "";
49  costTimeCompute: CostTimeCompute = null;
50
51  onCompleteChanged(isCompleted: boolean) {
52    if (isCompleted) {
53      Logger.info(TAG, "上传任务,onCompleteChanged();imageUrl=" + this.imageUrl)
54      if (this.isCompletedArr == null) {
55        this.isCompletedArr = [];
56      }
57      let i = this.isCompletedArr.push(true)
58    }
59  }
60
61  async aboutToAppear() {
62    this.uploadUrl = await getConfig(getContext(this), ConfigType.uploadUrl)
63    Logger.info(TAG, 'register publish event!')
64    emitter.on(this.innerEvent, (eventData) => {
65      Logger.info(TAG, `receive publish event!=${JSON.stringify(eventData)}`)
66      this.startUpload(this.imageUrl);
67    });
68    //定义计算器
69    this.costTimeCompute = new CostTimeCompute(
70      (str) => {
71        this.lastTimeStr = str;
72      },
73      () => {
74        return this.progress;
75      },
76      () => {
77        return this.total;
78      },
79      (str) => {
80        this.costTimeStr = str;
81      }
82      , true
83    );
84  }
85
86  aboutToDisappear() {
87    emitter.off(this.innerEvent.eventId);
88    if (this.isDownloading) {
89      this.cancelUpload();
90    }
91  }
92
93  async startUpload(url: string): Promise<void> {
94    if (this.isDownloading || this.isCompleted) {
95      return;
96    }
97    this.isError = false;
98    this.isDownloading = true;
99    this.copyImage(url)
100    this.costTimeCompute.startCostTime();
101  }
102
103  async cancelUpload() {
104    if (this.uploadTask == null || !this.isDownloading) {
105      return;
106    }
107    this.removeListener();
108    await this.uploadTask.delete();
109    this.uploadTask = null;
110
111    this.isDownloading = false;
112    this.progress = 0;
113    this.total = 1;
114    this.costTimeCompute.cancelCostTime();
115  }
116
117  async restartUpload() {
118    await this.cancelUpload();
119    this.startUpload(this.imageUrl);
120  }
121
122  async copyImage(url: string): Promise<void> {
123    Logger.info(TAG, `copyImage url = ${url}`)
124    // 将选中图片存入沙箱路径
125    let id = url.slice(url.lastIndexOf('/')+1);
126    Logger.info(TAG, `this.id = ${id}`)
127    let mContext: common.Context = getContext(this) as common.Context
128    let media = mediaLibrary.getMediaLibrary(mContext)
129    Logger.info(TAG, `this.media = ${JSON.stringify(media)}`)
130
131    let file = fs.openSync(url, fs.OpenMode.READ_WRITE);
132    let fd = file.fd;
133    Logger.info(TAG, 'file fd: ' + file);
134
135    // upload可访问的沙箱路径:data/app/el2/100/base/com.example.myapplication/haps/entry/cache/
136    let imagePath = `${mContext.filesDir.split('files')[0]}cache/${new Date().getTime().toString()}.jpg`
137    Logger.info(TAG, `this.imagePath = ${JSON.stringify(imagePath)}`)
138    try {
139      fs.copyFileSync(fd, imagePath)
140      await this.isUploadImage(imagePath)
141    } catch (err) {
142      Logger.info(TAG, `this.err = ${err}`)
143    }
144  }
145
146  async isUploadImage(imagePath: string): Promise<void> {
147    // 转成可上传的图片格式进行上传
148    let file = new UploadFile()
149    file.uri = `internal://cache/${imagePath.split('cache/')[1]}`
150    Logger.info(TAG, `file.uri = ${file.uri}`)
151    file.filename = imagePath.split('cache/')[1]
152    Logger.info(TAG, `file.filename = ${file.filename}`)
153    file.name = imagePath.split('cache/')[1].split('.')[0]
154    Logger.info(TAG, `file.name = ${file.name}`)
155    file.type = imagePath.split('.')[1]
156    Logger.info(TAG, `file.type = ${file.type}`)
157    Logger.debug("upload path=" + this.uploadUrl);
158    let uploadConfig = {
159      url: this.uploadUrl,
160      header: { key1: 'value1', key2: 'value2' },
161      method: 'POST',
162      files: [{ filename: file.filename, name: file.name, uri: file.uri, type: file.type }],
163      data: [{ name: 'name123', value: '123' }],
164    }
165    Logger.info(TAG, `uploadConfig = ${JSON.stringify(uploadConfig)}`)
166    let uploadContext: common.BaseContext = getContext(this) as common.BaseContext
167    this.uploadTask = await request.uploadFile(uploadContext, uploadConfig)
168    this.addListener();
169  }
170
171  addListener() {
172    if (this.uploadTask == null) {
173      return;
174    }
175    this.uploadTask.on('complete', async (taskStates) => {
176      await this.uploadTask.delete()
177      Logger.info(TAG, `uploadSuccess`)
178      this.isDownloading = false;
179      this.isCompleted = true;
180      this.costTimeCompute.cancelCostTime();
181    })
182    this.uploadTask.on('progress', (uploadSize: number, uploadTotal: number) => {
183      this.progress = uploadSize;
184      this.total = uploadTotal;
185    })
186    this.uploadTask.on('fail', (err: Array<request.TaskState>) => {
187      this.isDownloading = false;
188      this.isError = true;
189      this.costTimeCompute.cancelCostTime();
190      this.progress = 0;
191      try {
192        this.errorMessage = err.pop().message;
193      } catch (err) {
194        Logger.error('获取err message报错:' + err)
195      }
196    })
197  }
198
199  removeListener() {
200    if (this.uploadTask == null) {
201      return;
202    }
203    this.uploadTask.off("complete");
204    this.uploadTask.off("progress");
205    this.uploadTask.off("fail");
206  }
207
208  getTimeLastTip() {
209    let context: common.Context = getContext(this) as common.Context;
210    let lastTime = context.resourceManager.getStringSync($r('app.string.tip_last_time2'));
211    let computingStr = context.resourceManager.getStringSync($r('app.string.tip_computing2'));
212    return lastTime + ((this.lastTimeStr != null && this.lastTimeStr.length > 0) ? this.lastTimeStr : computingStr);
213  }
214
215  getCostTimeStr() {
216    let context: common.Context = getContext(this) as common.Context;
217    let costTimeTip = context.resourceManager.getStringSync($r("app.string.tip_cost_time"));
218    return costTimeTip + this.costTimeStr
219  }
220
221  build() {
222    Stack({ alignContent: Alignment.Center }) {
223      RelativeContainer() {
224        //背景图
225        Stack() {
226          if (!this.isCompleted) {
227            //蒙层和按钮
228            Stack({ alignContent: Alignment.Center }) {
229              if (this.isError) {
230                //失败状态
231                RelativeContainer() {
232                  Image($r('app.media.ic_public_reset'))
233                    .fillColor($r('app.color.progress_color'))
234                    .width('45%')
235                    .height('45%')
236                    .alignRules({
237                      center: { anchor: "__container__", align: VerticalAlign.Center },
238                      middle: { anchor: "__container__", align: HorizontalAlign.Center }
239                    })
240                    .id('restart')
241                    .onClick(() => {
242                      this.restartUpload();
243                    })
244
245                  Text($r('app.string.upload_error_message'))
246                    .fontSize($r('app.float.upload_controller_font_size'))
247                    .fontColor($r('app.color.upload_error_color'))
248                    .margin({ top: $r('app.float.upload_time_margin_top') })
249                    .alignRules({
250                      top: { anchor: "restart", align: VerticalAlign.Bottom },
251                      middle: { anchor: "__container__", align: HorizontalAlign.Center }
252                    })
253                    .id('error')
254                }.width('100%')
255                .height('100%')
256              } else if (this.isDownloading) {
257                //下载中状态
258                RelativeContainer() {
259                  Progress({ value: this.progress, total: this.total, type: ProgressType.Ring })
260                    .width('45%')
261                    .height('45%')
262                    .style({
263                      strokeWidth: $r('app.float.progress_stroke_width')
264                    })
265                    .backgroundColor($r('app.color.progress_background'))
266                    .color($r('app.color.progress_color'))
267                    .alignRules({
268                      center: { anchor: "__container__", align: VerticalAlign.Center },
269                      middle: { anchor: "__container__", align: HorizontalAlign.Center }
270                    })
271                    .id('progress')
272
273                  Text(this.getTimeLastTip())
274                    .fontSize($r('app.float.upload_controller_font_size'))
275                    .fontColor($r('app.color.color_cover'))
276                    .margin({ top: $r('app.float.upload_time_margin_top') })
277                    .alignRules({
278                      top: { anchor: "progress", align: VerticalAlign.Bottom },
279                      middle: { anchor: "__container__", align: HorizontalAlign.Center }
280                    })
281                    .id('time')
282                }.width('100%')
283                .height('100%')
284              } else {
285                //准备状态
286                Image($r('app.media.ic_public_upload'))
287                  .width('50%')
288                  .aspectRatio(1)
289                  .fillColor($r('app.color.color_cover'))
290                  .onClick(() => {
291                    this.startUpload(this.imageUrl)
292                  })
293              }
294            }
295            .backgroundColor($r('app.color.color_blend'))
296            .width('100%')
297            .height('100%')
298          }
299        }
300        .alignRules({
301          center: { anchor: "__container__", align: VerticalAlign.Center },
302          middle: { anchor: "__container__", align: HorizontalAlign.Center }
303        })
304        .backgroundImage(this.imageUrl, ImageRepeat.NoRepeat)
305        .backgroundImageSize({ width: '100%', height: '100%' })
306        .width('100%')
307        .aspectRatio(1)
308        .margin($r('app.float.add_picture_margin_background'))
309        .id('background')
310
311        //删除按钮
312        if (this.isDownloading) {
313          Stack({ alignContent: Alignment.TopEnd }) {
314            Image($r('app.media.ic_public_list_deleted'))
315              .height($r('app.float.add_picture_cancel_ic_width'))
316              .width($r('app.float.add_picture_cancel_ic_width'))
317          }
318          .width($r('app.float.add_picture_cancel_click_area'))
319          .height($r('app.float.add_picture_cancel_click_area'))
320          .id('ic_deleted')
321          .alignRules({
322            top: { anchor: "__container__", align: VerticalAlign.Top },
323            right: { anchor: "__container__", align: HorizontalAlign.End }
324          })
325          .onClick(() => {
326            this.cancelUpload();
327          })
328        }
329      }
330      .height('100%')
331      .width('100%')
332    }
333    .width('100%')
334    .aspectRatio(1)
335  }
336}