八卦阵 发表于 2024-10-16 18:08:20

鸿蒙系统开辟【网络-上传和下载(ArkTS)】基本功能

网络-上传和下载(ArkTS)

先容

本示例利用@ohos.request接口创建上传和下载任务,实现上传、下载功能,hfs作为服务器,实现了文件的上传和下载和任务的查询功能。
效果预览

https://i-blog.csdnimg.cn/direct/186039ea39f64d0ebafcf0f8252540d4.png
利用阐明
1.本示例功能需要先配置服务器情况后利用,具体配置见[上传下载服务配置]。
2.首页展示上传和下载两个入口组件,点击进入对应的页面,如果要利用背景下载任务,请开启背景任务开关。
3.上传页面(请先在图库中确定已开启图库权限):
​ 点击**+**,从相册选择拉起图库选择照片,图片选择页面支持拍照,选择照片后点击发表进行上传。
​ 在首页中打开背景任务开关后,上传页面开启的是背景上传任务,背景任务在应用退出到背景时可以在关照栏看到任务状态。
4.下载页面:
​ 点击文件列表选择要下载的文件后,点击下载选择指定路径后开始下载。
​ 点击查看下载文件进入下载文件页面,点击文件夹查看文件夹内的文件。
​ 在首页中打开背景任务开关后,下载页面开启的是背景下载任务,背景任务在应用退出到背景时可以在关照栏看到任务状态。
​ 前台下载时只支持单文件下载,背景下载时支持选择多个文件下载。
具体实现



[*] 该示例分为两个模块:

[*] 上传模块

[*]利用@ohos.request中接口agent.create创建上传任务,调用@ohos.request中的Task相干接口实现上传任务的创建、取消、进度加载,失败的任务会调用查询接口获取失败缘故起因并打印在日志中,支持多个文件上传。


[*] 源码参考:
/*
* 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, `taskerr, 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;
      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, `taskerr, 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}`);
      let imagePath = await this.mediaUtils.copyFileToCache(cacheDir, fileUris);
      logger.info(TAG, `getFilesAndData ${JSON.stringify(imagePath)}`);
      let file: request.agent.FormItem = {
      name: imagePath.split('cache/'),
      value: {
          path: './' + imagePath.split('cache/')
      }
      }
      files.push(file);
    }
    logger.info(TAG, `getFilesAndData ${JSON.stringify(files)}`);
    return files;
}
}

export const requestUpload = new Upload();


[*]源码参考:
/*
* 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 { picker } from '@kit.CoreFileKit';
import { logger } from '@ohos/uploaddownload';
import { BusinessError } from '@kit.BasicServicesKit';

const TAG: string = 'AddPictures';

@Extend(Image) function imageStyle() {
.width('100%')
.aspectRatio(1)
.objectFit(ImageFit.Fill)
.backgroundColor($r('app.color.light_gray'))
.borderRadius(12)
}

const TEXT_WIDTH_FULL: Length = '100%';
@Component
export struct AddPictures {
@Consume imageList: Array<string>;

build() {
    Column() {
      Text($r('app.string.tip'))
      .fontColor($r('app.color.text_normal'))
      .fontWeight(400)
      .fontFamily('HarmonyHeiTi')
      .fontSize(14)
      .opacity(0.4)
      .margin({ top: $r('app.float.add_pictures_margin_top'), bottom: $r('app.float.add_pictures_margin_bottom') })
      .width(TEXT_WIDTH_FULL)
      GridRow({ columns: { sm: 3, md: 6, lg: 8 }, gutter: 12 }) {
      ForEach(this.imageList, (item: string) => {
          GridCol({ span: 1 }) {
            Image(item)
            .imageStyle()
          }
      })
      GridCol({ span: 1 }) {
          Row() {
            Image($r('app.media.ic_public_add'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
          }
          .width('100%')
          .height('100%')
          .justifyContent(FlexAlign.Center)
      }
      .width('100%')
      .aspectRatio(1)
      .backgroundColor($r('app.color.white'))
      .borderRadius(12)
      .onClick(() => {
          this.showDialog();
      })
      }
    }
    .width('100%')
}

addImages = (images: Array<string>) => {
    images.forEach((item: string) => {
      if (!this.imageList.includes(item)) {
      this.imageList.push(item);
      }
    })
    logger.info(TAG, `addImages imageList=${JSON.stringify(this.imageList)}`);
}

showDialog() {
    AlertDialog.show({
      message: $r('app.string.pick_album'),
      alignment: DialogAlignment.Bottom,
      offset: { dx: 0, dy: -12 },
      primaryButton: {
      value: $r('app.string.cancel'),
      fontColor: $r('app.color.btn_text_blue'),
      action: () => {
      }
      },
      secondaryButton: {
      value: $r('app.string.ok'),
      fontColor: $r('app.color.btn_text_blue'),
      action: () => {
          try {
            let photoSelectOptions = new picker.PhotoSelectOptions();
            photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
            photoSelectOptions.maxSelectNumber = 5;
            let photoPicker = new picker.PhotoViewPicker();
            photoPicker.select(photoSelectOptions).then((photoSelectResult: picker.PhotoSelectResult) => {
            this.addImages(photoSelectResult.photoUris);
            }).catch((err: BusinessError) => {
            logger.error(TAG, `'PhotoViewPicker.select failed with err: ${JSON.stringify(err)}`);
            });
          } catch (err) {
            logger.error(TAG, `'PhotoViewPicker failed with err: ${JSON.stringify(err)}`);
          }
      }
      }
    })
}
}


[*]源码参考:
/*
* 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 { AddPictures } from '../components/AddPictures';
import { BackgroundTaskState, requestUpload, TOAST_BOTTOM } from '@ohos/uploaddownload';

const TIME_MAX: number = 5;

@Entry
@Component
struct Upload {
@StorageLink('isBackground') isBackground: boolean = false;
@StorageLink('backTaskState') @Watch('stateChange') backTaskState: BackgroundTaskState = BackgroundTaskState.NONE;
@State isBegin: boolean = false;
@Provide imageList: Array<string> = [];
@State progress: number = 0;
@State countdown: number = 0;

build() {
    Navigation() {
      Scroll() {
      AddPictures()
      }
      .padding({ left: 24, right: 24 })
      .width('100%')
      .layoutWeight(1)
      .align(Alignment.Top)

      Column() {
      Button() {
          if (this.isBackground && this.backTaskState !== BackgroundTaskState.NONE) {
            if (this.backTaskState === BackgroundTaskState.RUNNING) {
            Text($r('app.string.pause'))
                .fontSize(16)
                .fontWeight(500)
                .fontColor($r('app.color.white'))
            } else {
            Text($r('app.string.continue'))
                .fontSize(16)
                .fontWeight(500)
                .fontColor($r('app.color.white'))
            }
          } else if (this.isBegin && !this.isBackground) {
            Row() {
            Progress({ value: this.progress, type: ProgressType.Ring })
                .width(20)
                .height(20)
                .backgroundColor('#FFFFFF')
                .color('#558DFF')
                .style({ strokeWidth: 2, scaleCount: 100, scaleWidth: 2 })
            Text(`${this.getResourceString($r('app.string.uploading'))}${this.progress}%`)
                .fontSize(16)
                .fontColor('#FFFFFF')
                .fontWeight(500)
                .margin({ left: 12 })
            }.alignItems(VerticalAlign.Center)
          } else {
            if (this.countdown > 0) {
            Text(`${this.countdown}s`)
                .fontSize(16)
                .fontWeight(500)
                .fontColor($r('app.color.white'))
            } else {
            Text($r('app.string.upload'))
                .fontSize(16)
                .fontWeight(500)
                .fontColor($r('app.color.white'))
            }
          }
      }
      .id('publish')
      .width('100%')
      .height(40)
      .margin({ bottom: this.isBegin ? 16 : 24 })
      .enabled(this.countdown > 0 ? false : true)
      .backgroundColor($r('app.color.button_blue'))
      .onClick(() => {
          if (this.isBackground && this.backTaskState !== BackgroundTaskState.NONE) {
            requestUpload.pauseOrResume();
          } else {
            this.uploadFiles();
          }
      })

      if (this.isBegin) {
          Button() {
            Text($r('app.string.cancel'))
            .fontSize(16)
            .fontWeight(500)
            .fontColor($r('app.color.btn_text_blue'))
          }
          .id('cancel')
          .width('100%')
          .height(40)
          .margin({ bottom: 24 })
          .backgroundColor($r('app.color.button_light_gray'))
          .onClick(() => {
            // cancel task
            requestUpload.cancelTask();
            this.progress = 0;
            this.isBegin = false;
          })
      }
      }
      .width('100%')
      .padding({ left: 24, right: 24 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.light_gray'))
    .title($r('app.string.upload'))
    .hideBackButton(false)
    .titleMode(NavigationTitleMode.Mini)
    .mode(NavigationMode.Stack)
}

aboutToAppear() {
    this.isBegin = false;
    this.backTaskState = BackgroundTaskState.NONE;
}

stateChange() {
    if (this.backTaskState === BackgroundTaskState.NONE) {
      this.imageList = [];
    }
}

uploadFiles() {
    if (this.imageList.length === 0) {
      return;
    }
    if (this.isBackground) {
      AppStorage.setOrCreate('backTaskState', BackgroundTaskState.RUNNING)
      requestUpload.uploadFilesBackground(this.imageList);
      promptAction.showToast({ message: $r('app.string.background_task_start'), bottom: TOAST_BOTTOM });
    } else {
      this.isBegin = true;
      this.progress = 0;
      requestUpload.uploadFiles(this.imageList, (progress: number, isSucceed: boolean) => {
      this.progress = progress;
      if (this.progress === 100 && isSucceed) {
          this.isBegin = false;
          this.imageList = [];
          promptAction.showToast({ message: $r('app.string.upload_success'), bottom: TOAST_BOTTOM })
      }
      if (this.progress === 100 && isSucceed === false) {
          this.isBegin = false;
          this.countdown = TIME_MAX;
          let interval = setInterval(() => {
            if (this.countdown > 0) {
            this.countdown--;
            } else {
            clearInterval(interval);
            }
          }, 1000);
          promptAction.showToast({ message: $r('app.string.upload_fail'), bottom: TOAST_BOTTOM })
      }
      });
    }
}

getResourceString(resource: Resource) {
    let context = getContext(this) as common.UIAbilityContext;
    return context.resourceManager.getStringSync(resource.id);
}
}


[*] 参考接口:@ohos.request,@ohos.file.picker

[*] 下载模块

[*]利用@ohos.request中接口agent.create创建上传任务,调用@ohos.request中的Task相干接口实现上传任务的创建、取消、暂停、继续、进度加载,失败的任务会调用查询接口获取失败缘故起因并打印在日志中,前台下载任务只支持单个文件下载,背景下载任务支持多文件下载。利用@ohos.file.fs完成指定路径的创建和查询已下载的文件。


[*] 源码参考:
/*
* 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 { logger } from '../utils/Logger';
import { TOAST_BOTTOM, TASK_MAX, TASK_PAUSE_MSG, TASK_NET_PAUSE_MSG, TASK_RESUME_MSG, TASK_NET_RESUME_MSG } from '../utils/Constants';

const TAG: string = 'RequestDownload';
let isNetPause = false;
class RequestDownload {
private context: common.UIAbilityContext | undefined = undefined;
private waitList: Array<string[]> = [];
private downloadTask: request.agent.Task | undefined = undefined;
progressCallback: Function | undefined = undefined;
completedCallback: Function | undefined = undefined;
failedCallback: Function | undefined = undefined;

constructor() {
    setInterval(() => {
      this.flushBackgroundTask()
    }, 2000);
}

async downloadFilesBackground(folder: string, files: Array<string>) {
    logger.info(TAG, 'downloadFiles');
    this.context = getContext(this) as common.UIAbilityContext;
    files.forEach((item: string) => {
      this.waitList.push();
    });
}

async flushBackgroundTask() {
    let tasks = await request.agent.search({
      state: request.agent.State.RUNNING
    });
    if (tasks.length < TASK_MAX && this.waitList.length > 0) {
      let downloadList: Array<string[]> = [];
      if (this.waitList.length <= TASK_MAX - tasks.length) {
      downloadList = this.waitList;
      this.waitList = [];
      } else {
      downloadList = this.waitList.slice(0, TASK_MAX - tasks.length);
      this.waitList = this.waitList.slice(TASK_MAX - tasks.length, this.waitList.length);
      }
      logger.info(TAG, `this.waitList = ${JSON.stringify(this.waitList)}`);
      this.createBackgroundTask(downloadList);
    }
}

async createBackgroundTask(downloadList: Array<string[]>) {
    if (this.context === undefined) {
      return;
    }
    for (let i = 0; i < downloadList.length; i++) {
      try {
      let splitUrl = downloadList.split('//').split('/');
      let downloadConfig: request.agent.Config = {
          action: request.agent.Action.DOWNLOAD,
          url: downloadList,
          method: 'POST',
          title: 'download',
          mode: request.agent.Mode.BACKGROUND,
          network: request.agent.Network.ANY,
          saveas: `./${downloadList}/${splitUrl}`,
          overwrite: true,
          gauge: true
      }
      let downTask = await request.agent.create(this.context, downloadConfig);
      await downTask.start();
      } catch (err) {
      logger.error(TAG, `taskerr, err= ${JSON.stringify(err)}`);
      this.waitList.push(downloadList);
      }
    }
}

async pause() {
    if (this.downloadTask) {
      let taskInfo = await request.agent.show(this.downloadTask.tid);
      logger.info(TAG, `task pause, taskInfo= ${JSON.stringify(taskInfo)}`);
      await this.downloadTask.pause();
    }
}

async resume() {
    if (this.downloadTask) {
      let taskInfo = await request.agent.show(this.downloadTask.tid);
      logger.info(TAG, `task resume, taskInfo= ${JSON.stringify(taskInfo)}`);
      await this.downloadTask.resume();
    }
}

async downloadFile(folder: string, url: string, callback: (progress: number, isSuccess: boolean) => void) {
    logger.info(TAG, 'downloadFile');
    // 查询到存在正在执行的下载任务,提示并返回
    let tasks = await request.agent.search({
      state: request.agent.State.RUNNING,
      action: request.agent.Action.DOWNLOAD,
      mode: request.agent.Mode.FOREGROUND
    });
    if (tasks.length > 0) {
      promptAction.showToast({ message: $r('app.string.have_download_task_tips'), bottom: TOAST_BOTTOM });
      return;
    }
    let splitUrl = url.split('//').split('/');
    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    let downloadConfig: request.agent.Config = {
      action: request.agent.Action.DOWNLOAD,
      url: url,
      method: 'GET',
      title: 'download',
      mode: request.agent.Mode.BACKGROUND,
      retry: true,
      network: request.agent.Network.ANY,
      saveas: `./${folder}/${splitUrl}`,
      overwrite: true
    }
    logger.info(TAG, `downloadFile, downloadConfig = ${JSON.stringify(downloadConfig)}`);
    try {
      this.downloadTask = await request.agent.create(context, downloadConfig);
      this.downloadTask.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;
      let process: number = Math.floor(processed / size * 100);
      if (process < 100) {
          callback(process, false);
      }
      })
      this.downloadTask.on('completed', this.completedCallback = (progress: request.agent.Progress) => {
      logger.info(TAG, `download complete, file= ${url}, progress = ${progress.processed}`);
      callback(100, true);
      this.deleteTask();
      })
      this.downloadTask.on('pause', this.failedCallback = async (progress: request.agent.Progress) => {
      if (this.downloadTask) {
          let taskInfo = await request.agent.show(this.downloadTask.tid);
          logger.info(TAG, `pause,resean = ${taskInfo.reason}, progress = ${progress.processed}, faults = ${JSON.stringify(taskInfo.faults)}`);
          isNetPause = taskInfo.faults === 0;
          if (isNetPause) {
            callback(TASK_NET_PAUSE_MSG, isNetPause);
          }
          else {
            callback(TASK_PAUSE_MSG, isNetPause);
          }
      }
      })
      this.downloadTask.on('resume', this.failedCallback = async (progress: request.agent.Progress) => {
      if (this.downloadTask) {
          let taskInfo = await request.agent.show(this.downloadTask.tid);
          logger.info(TAG, `resume,resean = ${taskInfo.reason}, progress = ${progress.processed}, faults = ${JSON.stringify(taskInfo.faults)}`);
          if (isNetPause) {
            isNetPause = false;
            callback(TASK_NET_RESUME_MSG, isNetPause);
          }
          else {
            callback(TASK_RESUME_MSG, isNetPause);
          }
      }
      })
      this.downloadTask.on('failed', this.failedCallback = async (progress: request.agent.Progress) => {
      if (this.downloadTask) {
          let taskInfo = await request.agent.show(this.downloadTask.tid);
          logger.info(TAG, `fail,resean = ${taskInfo.reason}, progress = ${progress.processed}, faults = ${JSON.stringify(taskInfo.faults)}`);
      }
      callback(100, false);
      this.deleteTask();
      })
      await this.downloadTask.start();
    } catch (err) {
      logger.error(TAG, `taskerr, err= ${JSON.stringify(err)}`);
      callback(100, false);
    }
}

async deleteTask() {
    if (this.downloadTask) {
      try {
      this.downloadTask.off('progress');
      this.progressCallback = undefined;
      this.downloadTask.off('completed');
      this.completedCallback = undefined
      this.downloadTask.off('failed');
      this.failedCallback = undefined
      await request.agent.remove(this.downloadTask.tid);
      } catch (err) {
      logger.info(TAG, `deleteTask fail, err= ${JSON.stringify(err)}`);
      }
    }

    this.downloadTask = undefined;
}
}

export const requestDownload = new RequestDownload();


[*]源码参考:
/*
* 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 { promptAction } from '@kit.ArkUI';
import { router } from '@kit.ArkUI';
import { CustomDataSource } from '../components/CustomDataSource';
import {
FileModel,
FileType,
fileUtils,
logger,
requestFiles,
requestDownload,
TOAST_BOTTOM,
TASK_PAUSE_MSG,
TASK_RESUME_MSG,
TASK_NET_PAUSE_MSG,
TASK_NET_RESUME_MSG
} from '@ohos/uploaddownload';
import { SelectFolderDialog } from '../components/SelectFolderDialog';

const TAG: string = 'Download';

const OFFSET_DY: Length = -12;
const OFFSET_DX: Length = 0;
const LOADING_PROGRESS_WIDTH: Length = 100;
const FULL_WIDTH: Length = '100%';
const FULL_HEIGHT: Length = '100%';
const LIST_WIDTH: Length = '100%';
const LIST_HEIGHT: Length = 'auto';
const LIST_BORDER_RADIUS: Length | BorderRadiuses = 24;
const PADDING_TOP: Length = 4;
const PADDING_BOTTOM: Length = 4;
const STROKE_WIDTH: Length = 1;
const START_MARGIN: Length = 44;
const END_MARGIN: Length = 12;
const COLUMN_PADDING_LEFT: Length = 12;
const COLUMN_PADDING_RIGHT: Length = 12;
const COLUMN_PADDING_BOTTOM: Length = 12;
const BUTTON_FONT_SIZE = 16;
const MARGIN_TOP: Length = 12;
const MARGIN_LEFT: Length = 12;
const MARGIN_RIGHT: Length = 12;
const MARGIN_BOTTOM: Length = 12;
const BUTTON_HEIGHT: Length = 45;
@Entry
@Component
struct Download {
private fileData: CustomDataSource = new CustomDataSource([]);
@StorageLink('isBackground') isBackground: boolean = false;
@Provide downloadFolder: Array<string> = [];
@State isGetData: boolean = false;
@State checkFile: Array<string> = [];
@State checkList: Array<boolean> = [];
@State isRunning: boolean = false;
@State isPause: boolean = false;
@State isNetPause: boolean = false;
@State progress: number = 0;
private selectFolder = (folder: string) => {
    logger.info(TAG, `selectFolder = ${folder}`);
    this.download(folder);
}
private folderDialogController: CustomDialogController = new CustomDialogController({
    builder: SelectFolderDialog({ selectFolder: this.selectFolder }),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: OFFSET_DX,
      dy: OFFSET_DY }
});

build() {
    Navigation() {
      Column() {
      if (this.isGetData) {
          LoadingProgress()
            .width(LOADING_PROGRESS_WIDTH)
            .layoutWeight(1)
      } else {
          List({ space: 12 }) {
            LazyForEach(this.fileData, (item: FileModel, index: number) => {
            ListItem() {
                this.FileItem(item, index)
            }
            }, (item: FileModel) => JSON.stringify(item))
          }
          .width(LIST_WIDTH)
          .height(LIST_HEIGHT)
          .scrollBar(BarState.Off)
          .backgroundColor(Color.White)
          .borderRadius(LIST_BORDER_RADIUS)
          .padding({ top: PADDING_TOP,
            bottom: PADDING_BOTTOM })
          .divider({ strokeWidth: STROKE_WIDTH,
            startMargin: START_MARGIN,
            endMargin: END_MARGIN })
      }
      Column().layoutWeight(1)

      this.BottomView()
      }
      .padding({ left: COLUMN_PADDING_LEFT,
      right: COLUMN_PADDING_RIGHT,
      bottom: COLUMN_PADDING_BOTTOM })
      .height(FULL_HEIGHT)
    }
    .width(FULL_WIDTH)
    .height(FULL_HEIGHT)
    .hideBackButton(false)
    .titleMode(NavigationTitleMode.Mini)
    .mode(NavigationMode.Stack)
    .backgroundColor($r('app.color.light_gray'))
    .hideToolBar(false)
    .title($r('app.string.download'))
}

@Builder
FileItem(file: FileModel, index: number) {
    Row() {
      Row() {
      if (file.fileType === FileType.FOLDER) {
          Image($r('app.media.ic_files_folder'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
      } else if (file.fileType === FileType.IMAGE) {
          Image($r('app.media.ic_public_picture'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
      } else if (file.fileType === FileType.MUSIC) {
          Image($r('app.media.ic_public_music'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
      } else if (file.fileType === FileType.Video) {
          Image($r('app.media.ic_public_video'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
      } else {
          Image($r('app.media.ic_public_document'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
      }

      Text(decodeURIComponent(file.name))
          .fontSize(16)
          .fontWeight(400)
          .layoutWeight(1)
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .margin({ left: 12 })
      }
      .layoutWeight(1)

      Checkbox({ name: '', group: 'checkboxGroup' })
      .select(this.checkList)
      .selectedColor($r('app.color.button_blue'))
      .margin({ left: 12 })
      .hitTestBehavior(HitTestMode.None)
    }
    .width('100%')
    .padding({ left: 12, right: 12 })
    .height(48)
    .onClick(() => {
      this.fileCheck(index);
    })
}

@Builder
BottomView() {
    Column({ space: 12 }) {
      Button() {
      Row() {
          if (!this.isBackground && this.isRunning) {
            if (this.isPause || this.isNetPause) {
            Text($r('app.string.continue'))
                .fontColor(Color.White)
                .fontSize(BUTTON_FONT_SIZE)
            }
            else {
            Text(`${this.progress}%`)
                .fontColor(Color.White)
                .fontSize(BUTTON_FONT_SIZE)
            Text($r('app.string.downloading'))
                .fontColor(Color.White)
                .fontSize(BUTTON_FONT_SIZE)
                .margin({ left: MARGIN_LEFT })
            }
          } else {
            Text($r('app.string.download'))
            .fontColor(Color.White)
            .fontSize(BUTTON_FONT_SIZE)
          }
      }
      }
      .id('download_to')
      .type(ButtonType.Capsule)
      .height(BUTTON_HEIGHT)
      .width(FULL_WIDTH)
      .backgroundColor($r('app.color.button_blue'))
      .onClick(() => {
      if (!this.isRunning) {
          this.folderDialogController.open();
      }
      else {
          if (!this.isNetPause) {
            if (this.isPause) {
            requestDownload.resume();
            }
            else {
            requestDownload.pause();
            }
          }
      }
      })

      Button($r('app.string.view_download_files'))
      .id('view_download_files')
      .type(ButtonType.Capsule)
      .backgroundColor($r('sys.color.ohos_id_color_button_normal'))
      .width('100%')
      .fontSize(BUTTON_FONT_SIZE)
      .margin({ bottom: MARGIN_BOTTOM })
      .fontColor($r('app.color.btn_text_blue'))
      .onClick(() => {
          router.pushUrl({
            url: 'pages/DownloadFiles'
          });
      })
    }
    .margin({ top: MARGIN_TOP,
      left: MARGIN_LEFT,
      right: MARGIN_RIGHT })
}

aboutToAppear() {
    this.isRunning = false;
    this.isPause = false;
    this.isGetData = true;
    requestFiles.requestFiles().then((data: FileModel[]) => {
      this.checkList = [];
      this.isRunning = false;
      this.fileData.dataArray = data;
      this.fileData.dataArray.forEach(() => {
      this.checkList.push(false);
      })
      this.isGetData = false;
      this.fileData.notifyDataReload();
    })
    fileUtils.listFolders().then((folders: Array<string>) => {
      this.downloadFolder = folders;
    })
}

fileCheck(index: number) {
    if (!this.isBackground) {
      for (let i = 0; i < this.checkList.length; i++) {
      if (i !== index) {
          this.checkList = false;
      }
      }
    }
    this.checkList = !this.checkList;
    logger.info(TAG, `this.checkList = ${JSON.stringify(this.checkList)}`);
}

download(folder: string) {
    this.checkFile = [];
    if (this.checkList === undefined) {
      return;
    }
    logger.info(TAG, `this.checkList = ${JSON.stringify(this.checkList)}`);
    for (let i = 0; i < this.checkList.length; i++) {
      if (this.checkList) {
      let fileModel = this.fileData.getData(i);
      logger.info(TAG, `fileModel = ${JSON.stringify(fileModel)}`);
      fileModel.files.forEach((url: string) => {
          let splitUrl = url.split('//').split('/');
          if (splitUrl !== '') {
            this.checkFile.push(url);
          }
      });
      }
    }
    logger.info(TAG, `this.checkFile = ${JSON.stringify(this.checkFile)}`);
    if (this.checkFile.length === 0) {
      promptAction.showToast({ message: $r('app.string.check_file_tips'), bottom: TOAST_BOTTOM });
      return;
    }
    this.progress = 0;
    if (this.isBackground) {
      this.isRunning = false;
      requestDownload.downloadFilesBackground(folder, this.checkFile);
      this.checkFile = [];
      this.checkList = [];
      this.fileData.dataArray.forEach(() => {
      this.checkList.push(false);
      })
      this.fileData.notifyDataReload();
      promptAction.showToast({ message: $r('app.string.background_task_start'), bottom: TOAST_BOTTOM });
    } else {
      this.isRunning = true;
      requestDownload.downloadFile(folder, this.checkFile, this.downloadFileCallback);
    }
}

downloadFilesCallback = (downloadCount: number, isSuccess: boolean) => {
    this.progress = downloadCount;
    if (downloadCount === this.checkFile.length) {
      this.downloadFinish(isSuccess);
    }
}
downloadFileCallback = (progress: number, isSuccess: boolean) => {
    logger.info(TAG, `downloadFileCallback = ${progress}`);
    if (progress === TASK_PAUSE_MSG) {
      this.isPause = true;
    }
    else if (progress === TASK_RESUME_MSG) {
      this.isPause = false;
    }
    else if (progress === TASK_NET_PAUSE_MSG) {
      this.isNetPause = true;
      let message = $r('app.string.net_pause');
      promptAction.showToast({ message: message, bottom: TOAST_BOTTOM });
    }
    else if (progress === TASK_NET_RESUME_MSG) {
      this.isNetPause = false;
      let message = $r('app.string.net_resume');
      promptAction.showToast({ message: message, bottom: TOAST_BOTTOM });
    }
    else {
      this.progress = progress;
      if (this.progress === 100) {
      this.downloadFinish(isSuccess);
      }
    }
}

downloadFinish(isSuccess: boolean) {
    this.isRunning = false;
    this.checkFile = [];
    this.checkList = [];
    this.fileData.dataArray.forEach(() => {
      this.checkList.push(false);
    })
    this.fileData.notifyDataReload();
    let message = isSuccess ? $r('app.string.download_finish') : $r('app.string.download_fail');
    promptAction.showToast({ message: message, bottom: TOAST_BOTTOM });
}
}


[*]源码参考:
/*
* 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 { fileIo } from '@kit.CoreFileKit';
import { logger } from '../utils/Logger';

const TAG: string = 'FileUtil';
const ALBUMS: string[] = ['Pictures', 'Videos', 'Others'];

class FileUtil {
constructor() {
}

async initDownloadDir(): Promise<void> {
    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    logger.info(TAG, `initDownloadDir cacheDir=${context.cacheDir}`);
    try {
      fileIo.mkdirSync(`${context.cacheDir}/${ALBUMS}`);
      fileIo.mkdirSync(`${context.cacheDir}/${ALBUMS}`);
      fileIo.mkdirSync(`${context.cacheDir}/${ALBUMS}`);
    } catch (err) {
      logger.info(TAG, `initDownloadDir err =${JSON.stringify(err)}`);
    }
}

async listFolders(): Promise<Array<string>> {
    await this.initDownloadDir();
    return ALBUMS;
}

async clearFolder(folderName: string): Promise<void> {
    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    try {
      let files: string[] = fileIo.listFileSync(`${context.cacheDir}/${folderName}`);
      logger.info(TAG, `listFiles listFileSync =${JSON.stringify(files)}`);
      for (let i = 0; i < files.length; i++) {
      fileIo.unlinkSync(`${context.cacheDir}/${folderName}/${files}`);
      }
    } catch (err) {
      logger.info(TAG, `listFiles err =${JSON.stringify(err)}`);
    }
}

async listFiles(folderName: string): Promise<Array<string>> {
    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    let files: string[] = [];
    try {
      files = fileIo.listFileSync(`${context.cacheDir}/${folderName}`);
      logger.info(TAG, `listFiles listFileSync =${JSON.stringify(files)}`);
    } catch (err) {
      logger.info(TAG, `listFiles err =${JSON.stringify(err)}`);
    }
    return files;
}
}

export const fileUtils = new FileUtil();


[*]源码参考:
/*
* 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 { fileUtils } from '../utils/FileUtils';

@Preview
@Component
export struct FileBrowse {
@State folders: Array<string> = ['folder'];
@State files: Array<string> = [];
@State currentFolder: string = '';

aboutToAppear() {
    fileUtils.listFolders().then((folders: Array<string>) => {
      this.folders = folders;
    })
}

build() {
    Navigation() {
      List({ space: 12 }) {
      ForEach(this.folders, (item: string) => {
          ListItem() {
            NavRouter() {
            Row() {
                Image($r('app.media.ic_files_folder'))
                  .size({ width: 32, height: 26 })
                  .objectFit(ImageFit.Contain)
                Text(item)
                  .fontSize(16)
                  .width('100%')
                  .margin({ left: 12 })
            }
            .height(56)
            .padding({ left: 16 })
            .backgroundColor(Color.White)
            .borderRadius(24)

            NavDestination() {
                this.FilesView()
            }
            .title(this.CustomTitle(item))
            .backgroundColor($r('app.color.light_gray'))
            }
            .onStateChange(async (isActivated: boolean) => {
            if (isActivated) {
                this.currentFolder = item;
                this.files = await fileUtils.listFiles(item);
            }
            })
          }
      })
      }
      .padding({ left: 12, right: 12 })
    }
    .hideBackButton(false)
    .titleMode(NavigationTitleMode.Mini)
    .title($r('app.string.download_files_title'))
    .mode(NavigationMode.Stack)
    .backgroundColor($r('app.color.light_gray'))
}

@Builder
CustomTitle(title: string) {
    Row() {
      Text(title)
      .fontSize(20)
      .fontColor($r('app.color.text_normal'))
      .fontWeight(700)
      .margin({ left: 8 })
    }
    .width('100%')
}

@Builder
FilesView() {
    Column() {
      List({ space: 12 }) {
      if (this.files.length === 0) {
          ListItem() {
            Text($r('app.string.folder_empty'))
            .fontSize(16)
            .width('100%')
            .margin({ top: 50 })
            .textAlign(TextAlign.Center)
          }
      }
      ForEach(this.files, (item: string) => {
          ListItem() {
            Text(decodeURIComponent(item))
            .fontSize(16)
            .width('100%')
          }
          .padding(12)
          .height(48)
          .backgroundColor(Color.White)
          .borderRadius(24)
      })
      }
      .padding({ left: 12, right: 12 })
      .layoutWeight(1)

      Column() {
      Button() {
          Image($r('app.media.ic_public_delete'))
            .objectFit(ImageFit.Cover)
            .size({ width: 24, height: 24 })
      }
      .type(ButtonType.Circle)
      .width(40)
      .height(40)
      .backgroundColor('#FF0000')
      .margin({ left: 5 })

      Text($r('app.string.clear_folder'))
          .fontSize(14)
          .fontColor($r('app.color.text_normal'))
          .opacity(0.6)
          .margin({ top: 8 })
      }
      .margin({ bottom: 24, top: 6 })
      .onClick(() => {
      fileUtils.clearFolder(this.currentFolder);
      this.files = [];
      })
    }
    .height('100%')
    .backgroundColor($r('app.color.light_gray'))
}
}


[*]参考接口:@ohos.request,@ohos.file.fs
以上就是本篇文章所带来的鸿蒙开辟中一小部分技能解说;想要学习完整的鸿蒙全栈技能。可以在末端找我可全部拿到!
下面是鸿蒙的完整学习蹊径,展示如下:
https://i-blog.csdnimg.cn/direct/5919b86cebec482181f87bf4647322a6.png#pic_center
除此之外,根据这个学习鸿蒙全栈学习蹊径,也附带一整套完整的学习【文档+视频】,内容包含如下:
   内容包含了:(ArkTS、ArkUI、Stage模型、多端摆设、分布式应用开辟、音频、视频、WebGL、OpenHarmony多媒体技能、Napi组件、OpenHarmony内核、鸿蒙南向开辟、鸿蒙项目实战)等技能知识点。资助大家在学习鸿蒙路上快速发展!
鸿蒙【北向应用开辟+南向系统层开辟】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

https://i-blog.csdnimg.cn/direct/d46380eba5fc4b5cac0d6868dc26f3e0.png#pic_center
为了制止大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 鸿蒙系统开辟【网络-上传和下载(ArkTS)】基本功能