diff --git a/OAT.xml b/OAT.xml index 00823cbb19069888bca94342a2f5cb785651c7a6..3024af96188f3867a96eee5b54ed3bbbfd19a010 100644 --- a/OAT.xml +++ b/OAT.xml @@ -209,6 +209,12 @@ Note:If the text contains special characters, please escape them according to th + + + + + + @@ -2224,6 +2230,13 @@ Note:If the text contains special characters, please escape them according to th + + + + + + + diff --git a/code/ArkTS1.2/DownloadSample/README.md b/code/ArkTS1.2/DownloadSample/README.md index 8ba7d5cc4a08ce2a0586a0d0d36f2dca68fbf855..3100f83983f17ee9711561ae98c14367a7c98017 100644 --- a/code/ArkTS1.2/DownloadSample/README.md +++ b/code/ArkTS1.2/DownloadSample/README.md @@ -15,7 +15,7 @@ 使用说明 在多文件下载监听案例。 - * 点击全部开始进行下载,下载超过3分钟进行并行化下载 + * 点击全部开始进行下载。 diff --git a/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/Index.ets b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/Index.ets index 180910f994430ecb930810ca23615ac77eeb9220..9350f4c51aeaeffae1b643329f14d746559fe7fb 100644 --- a/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/Index.ets +++ b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/Index.ets @@ -23,22 +23,20 @@ * * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins -import { Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView, Entry, JSON } from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins -import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy } from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import { memo, __memo_context_type, __memo_id_type } from '@ohos.arkui.stateManagement'; // should be insert by ui-plugins +import { Text, TextAttribute, Column, Component, Button, ButtonAttribute, + ClickEvent, UserView, Entry, JSON } from '@ohos.arkui.component'; // TextAttribute should be insert by ui-plugins +import { State, MutableState, stateOf, observableProxy } from '@ohos.arkui.stateManagement'; // should be insert by ui-plugins import hilog from '@ohos.hilog'; -import request from '@ohos.request'; import {BusinessError} from '@ohos.base'; import common from '@ohos.app.ability.common'; // 导入依赖资源context模块 -import { UIContext, Router } from "@ohos.arkui.UIContext"; -import { MultipleFilesDownloadComponent } from './view/MultipleFilesDownload'; +import { MultipleFilesDownloadComponent } from './view/MultipeFilesDownload'; @Entry @Component struct MyStateSample { - build() { - Column(undefined) { + Column() { MultipleFilesDownloadComponent(); } } diff --git a/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/model/dataType.ets b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/model/dataType.ets index 702faa42fa2fe1c103d38678862c15065911ad09..5786c942e23fc64fdca31c291cb4d671a0d96f34 100644 --- a/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/model/dataType.ets +++ b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/model/dataType.ets @@ -27,13 +27,9 @@ let nextID: number = 1; export class downloadFilesData { - // 唯一标识 id: string; - // 下载地址 url: string; - // 文件下载状态: 0未下载, 1下载成功, 2下载失败, 3已删除 fileStatus: number; - // 下载完成的时间戳 downloadTime: number; constructor( diff --git a/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/FileDownloadItem.ets b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/FileDownloadItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..ffe9e6507b7b94117def6b2e901f6cf2cd2cd115 --- /dev/null +++ b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/FileDownloadItem.ets @@ -0,0 +1,362 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { + memo, + __memo_context_type, + __memo_id_type +} from '@ohos.arkui.stateManagement'; // should be insert by ui-plugins +import {Entry,Text,TextAttribute,Column,Component, + Button,ButtonAttribute,ClickEvent,UserView,Row,TextAlign,Visibility,List,ListItem,ForEach,TextOverflow, + Image,HorizontalAlign,Progress,ProgressType,FlexAlign,SafeAreaType,JSON,$r,Margin,TextDecorationType +} from '@ohos.arkui.component'; // TextAttribute should be insert by ui-plugins +import {State,Link,MutableState,stateOf,observableProxy,Prop,Watch} from '@ohos.arkui.stateManagement'; // should be insert by ui-plugins\ +import hilog from '@ohos.hilog'; +import common from '@ohos.app.ability.common'; +import { BusinessError } from '@ohos.base'; +import request from '@ohos.request'; // 导入上传下载模块。需要配置ohos.permission.INTERNET权限 +import { downloadFilesData } from '../model/dataType'; + +const NO_TASK: number = 0; // 0个下载任务 +const INIT_PROGRESS: number = 0; // 进度条初始值 +const BYTE_CONVERSION: number = 1024; // 字节转换 + +@Component +export struct FileDownloadItem { + @State fileName: string = ''; + // 下载任务状态 + @State state: string = ''; + // 监听是否全部开始下载 + @Prop @Watch('onDownLoadUpdated') isStartAllDownload: boolean; + // 下载任务对象初始化。用于下载失败和下载过程中暂停和重新启动下载。 + private downloadTask: request.agent.Task | undefined; + // 待下载任务数量 + @Link downloadCount: number; + // 下载失败任务数量 + @Link downloadFailCount: number; + // 下载状态图标显隐控制。下载中显示图标,下载完成或者下载失败隐藏图标 + @State isShow: boolean = false; + // 是否正在下载标志位 + @State downloading: boolean = false; + // 下载文件大小。类型字符串 + @State sFileSize: string = '-'; + // 下载文件大小。类型数值 + @State nFileSize: number = 0; + // 当前已下载数据量。类型字符串 + @State sCurrentDownloadSize: string = '-'; + // 当前已下载数据量。类型数值 + @State nCurrentDownloadSize: number = 0; + // 下载文件数据 + @Prop fileDataInfo: downloadFilesData; + // 下载历史列表 + @Link historyArray: downloadFilesData[]; + // 下载列表 + @Link downloadFileArray: downloadFilesData[]; + + // 监听是否开始下载/暂停下载 + onDownLoadUpdated(s: string):void { + if (this.isStartAllDownload) { + // 如果下载失败,则重新下载。下载失败原因一般是网络原因导致。 + if (this.state === '下载失败') { + // 下载任务完成或者任务失败时,底层会自动销毁任务资源。所以如果需要重新下载,重新创建任务即可。这里只做了初始化task对象 + this.downloadTask = undefined; + // 隐藏下载状态图标 + this.isShow = false; + // 重置下载任务状态 + this.state = ''; + } + this.startDownload(); + } else { + if (this.downloadFailCount > 0 && this.downloadFailCount === this.downloadCount) { + // 如果是任务全部下载失败,重置isStartAllDownload为false的情况,重置downloadFailCount + this.downloadFailCount = 0; + } else { + // 暂停下载 + this.pauseDownload(); + } + } + } + + // 启动下载任务 + startDownload() { + // 首次下载,创建任务 + if (this.downloadTask === undefined) { + hilog.info(0x0000, 'TAGhttp', `点击`); + let context = this.getUIContext().getHostContext() as common.UIAbilityContext; + hilog.info(0x0000, 'TAGhttp', `context创建`); + (request.agent.create(context, { + action: request.agent.Action.DOWNLOAD, // 配置任务选项,这里配置为下载任务 + url: this.fileDataInfo.url, // 配置下载任务url + overwrite: true, // 下载过程中路径已存在时的解决方案选择。true表示覆盖已存在的文件 + method: 'GET', // HTTP标准方法。下载时,使用GET或POST。 + saveas: './', // 这里'./'表示下载至应用当前缓存路径下。 + mode: request.agent.Mode.BACKGROUND, // 任务模式设置后台任务。 + gauge: true, // 后台任务的过程进度通知策略,仅应用于后台任务。true表示发出每个进度已完成或失败的通知。 + retry: false, // 默认为true,如果没有网络或者网络不满足时,会自动暂停waiting,等网络满足时进行一次重试。设置为false时,没网直接走失败回调 + } as request.agent.Config) as Promise) + .then((task: request.agent.Task) => { + hilog.info(0x0000, 'TAGhttp', `create成功`); + this.downloadTask = task as request.agent.Task; + hilog.info(0x0000, 'TAGhttp', 'downloadTask: ' + String(this.downloadTask!.tid)); + // 注册下载任务相关回调 + task.on('completed', this.completedCallback); // 下载任务完成回调 + task.on('failed', this.failedCallback); // 下载任务失败回调 + task.on('progress', this.progressCallback); // 下载进度更新回调 + task.on('pause', this.pauseCallback); // 暂停任务回调 + task.on('resume', this.resumeCallback); // 重新启动任务回调 + + // TODO 知识点:启动下载任务。本例在每个FileDownloadItem中使用task.start方法启动各自的下载任务。 + task.start((err: BusinessError) => { + if (err) { + hilog.error(0x0000, 'TAGhttp', + `task.start Failed to task start with error message: ${err.message}, error code: ${err.code}`); + return; + } + }); + }); + }else { + // 任务已存在时,继续下载 + this.resumeDownload(); + } + } + + // 下载任务完成回调 + private completedCallback: (progress: request.agent.Progress) => void = (progress: request.agent.Progress) => { + try { + // 下载状态设置为下载完成 + this.state = '下载完成'; + // 文件下载完成,待下载任务数量减1 + if(this.downloadCount > 0) { + this.downloadCount--; + } + if (this.sFileSize === '未知大小') { + this.nCurrentDownloadSize = 1; + } + + // 隐藏下载状态图标 + this.isShow = false; + this.getFileStatusAndTime(1); + }catch(err:Error){ + hilog.error(0x0000, 'TAGhttp,err: ', String(err.message)); + } + } + + // 下载任务失败回调。任务下载失败一般是由于网络不好,底层重试也失败后进入该下载失败回调。如果网络没问题,建议重新下载再试。 + private failedCallback: (progress: request.agent.Progress) => void = (progress: request.agent.Progress) => { + try { + this.state = '下载失败'; + this.getFileStatusAndTime(2) + // 当所有任务下载失败时,'全部暂停'状态重置为'全部开始'。 + this.downloadFailCount++; + if (this.downloadFailCount === this.downloadCount) { + this.isStartAllDownload = false; + } + }catch(err:Error){ + hilog.error(0x0000, 'TAGhttp,err: ', String(err.message)) + } + } + + // 文件下载成功和下载失败更改文件的状态 + getFileStatusAndTime(status: number) { + this.fileDataInfo.fileStatus = status; + this.historyArray = [...this.historyArray, this.fileDataInfo] + // 下载列表删除下载成功的数据 + this.downloadFileArray = this.downloadFileArray.filter((item:downloadFilesData) => { + return item.id !== this.fileDataInfo.id; + }); + } + + // 下载进度更新回调 + private progressCallback: (progress: request.agent.Progress) => void = (progress: request.agent.Progress) => { + try { + // 性能知识点: 如果注册了progress下载进度更新监听,不建议在progress下载进度更新回调中加日志打印,减少不必要的性能损耗。 + hilog.info(0x0000, 'TAGhttp', JSON.stringify(progress)); + this.nFileSize = progress.sizes[0]; + + this.state = '下载中'; + this.downloading = true; + // 显示下载状态图标 + this.isShow = true; + // 第一次开始下载 + if (this.sFileSize === '-') { + // 如果下载url文件的服务器采用chunk分块传输文件数据,是获取不到下载文件总大小的。传过来的值为-1,则在页面上显示'未知大小' + if (progress.sizes[0] === -1) { + this.sFileSize = '未知大小'; + // 文件大小无法获取的情况下,进度条的值设置为0,总进度设置为1 + this.nCurrentDownloadSize = 0; + this.nFileSize = 1; + } else { + // 能获取文件大小时,按实际下载数据量更新进度 + this.nFileSize = progress.sizes[0]; + this.sFileSize = String(progress.sizes[0] / BYTE_CONVERSION).split('.')[0] as string + 'kb'; + this.nCurrentDownloadSize = progress.processed; + } + } else { + // 非首次下载(暂停过下载任务后重新启动下载时),文件大小能获取到的情况,更新下载进度 + this.nCurrentDownloadSize = progress.processed; + } + // 用于显示已下载文件数据大小 + this.sCurrentDownloadSize = String(progress.processed / BYTE_CONVERSION).split('.')[0] as string + 'kb'; + }catch(err:Error){ + hilog.error(0x0000, 'TAGhttp,err: ', String(err.message)) + } + } + + // 暂停任务回调 + private pauseCallback: (progress: request.agent.Progress) => void = (progress: request.agent.Progress) => { + try { + this.state = '已暂停'; + // 切换下载状态图标 + this.downloading = false; + }catch(err:Error){ + hilog.error(0x0000, 'TAGhttp,err: ', String(err.message)) + } + } + + // 暂停下载任务 + pauseDownload(): void { + if (this.downloadTask) { + // TODO 知识点:使用request.agent.show,根据任务id可查询任务的详细信息。本处用于查询下载任务状态 + request.agent.show(this.downloadTask!.tid, (err: Error, taskInfo: request.agent.TaskInfo) => { + if (err.code !== 0) { + hilog.error(0x0000, 'TAGhttp', `Failed to show with error message: ${err.message}, error code: ${err.code}`); + return; + } + // 判断当前下载任务状态是否满足暂停条件。 + if (this.downloadTask && (taskInfo.progress.state === request.agent.State.WAITING || + taskInfo.progress.state === request.agent.State.RUNNING || + taskInfo.progress.state === request.agent.State.RETRYING)) { + this.downloadTask!.pause().then(() => { + // 暂停任务成功 + }).catch((err: Error) => { + hilog.error(0x0000, 'TAGhttp', `Failed to pause with error message: ${err.message}, error code: ${err.code}`); + }); + } else { + if (this.downloadTask) { + // 不满足暂停任务条件 + hilog.info(0x0000, 'TAGhttp', `Not meeting the pause task conditions,current task state: ${taskInfo.progress.state}`); + } + } + }); + } + } + + // 重新启动任务回调。如果下载url文件的服务器不支持分片传输,则文件将重新下载。如果服务器支持分片传输,则会基于之前暂停时的下载进度继续下载。 + private resumeCallback: (progress: request.agent.Progress) => void = (progress: request.agent.Progress) => { + try { + this.state = '下载中'; + // 切换下载状态图标 + this.downloading = true; + }catch(err:Error){ + hilog.error(0x0000, 'TAGhttp err: ', String(err.message)); + } + } + + // 重新启动下载任务 + resumeDownload(): void { + if (this.downloadTask) { + // 查询任务状态 + request.agent.show(this.downloadTask!.tid, (err: BusinessError, taskInfo: request.agent.TaskInfo) => { + if (err.code !== 0) { + hilog.error(0x0000, 'TAGhttp', `Failed to show with error message: ${err.message}, error code: ${err.code}`); + return; + } + // 判断如果任务是暂停状态,则重新启动下载任务 + if (this.downloadTask && taskInfo.progress.state === request.agent.State.PAUSED) { + // TODO 知识点:使用task.resume可以重新启动任务,可恢复暂停的后台任务。 + this.downloadTask!.resume((err: BusinessError) => { + if (err.code !== 0) { + hilog.error(0x0000, 'TAGhttp', `Failed to resume with error message: ${err.message}, error code: ${err.code}`); + return; + } + // 重新启动下载任务成功 + }); + } + }); + } + } + + build(){ + Row() { + Row() { + Image($r('app.media.multiple_files_download_file')) + .height(50) + .width(50) + .borderRadius(8) + .id('fileImage') + }.width('17%') + + Column() { + Row() { + Column() { + Text(this.fileDataInfo.url.split('/').pop() || '文件') + .height(22) + .width('100%') + .fontSize(14) + .fontColor('#000000') + .textAlign(TextAlign.Start) + .maxLines(1)// 限制为单行 + .id('fileName') + + Row() { + Text(this.sCurrentDownloadSize + '/' + this.sFileSize) + .fontSize(12) + .fontColor('#66182431') + .id('downloadVal') + + Text(this.state) + .fontSize(12) + .fontColor('#66182431') + .margin({left: 10} as Margin) + .id(this.fileName + 'state') + }.width('100%') + .height(23) + } + .width('90%') + + Image(this.downloading ? + $r('app.media.multiple_files_download_start') : $r('app.media.multiple_files_download_stop')) + .height(25) + .width('10%') + .id('downloadImage') + } + + // 下载进度条,用于显示从下载进度更新回调中获取到的已下载数据大小 + Progress({ value: this.nCurrentDownloadSize, total: this.nFileSize, type: ProgressType.Capsule }) + .value(1) + .value(this.nCurrentDownloadSize) + .height(5) + .id('progress') + + } + .width('83%') + .height(50) + .alignItems(HorizontalAlign.Start) + } + .width('100%') + .height(50) + .margin({ top: 15, bottom: 15 } as Margin) + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/HistoryItem.ets b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/HistoryItem.ets index db2357d0e8cdfbd1bba2220798d57468c37abbb0..726d2ea369eeea2b31f3bb85ffa48d66af43c904 100644 --- a/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/HistoryItem.ets +++ b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/HistoryItem.ets @@ -23,40 +23,79 @@ * * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import { memo, __memo_context_type, __memo_id_type } from '@ohos.arkui.stateManagement'; // should be insert by ui-plugins -import { Entry, Text, TextAttribute, Column, Component, Button, ButtonAttribute, - ClickEvent, UserView, Row, TextAlign, Visibility, List, ListItem, ForEach, - TextOverflow, Image, HorizontalAlign, Progress, ProgressType, FlexAlign, SafeAreaType, - RelativeContainer, TextDecorationType, ButtonType, $r, Margin -} from '@ohos.arkui.component'; -import { Entry, Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView, Row, TextAlign, - Visibility, List, ListItem, ForEach, TextOverflow, Image, HorizontalAlign, Progress, ProgressType, FlexAlign, - SafeAreaType, RelativeContainer, TextDecorationType, ButtonType, $r, Margin -} from '@ohos.arkui.component'; -import { State, Link, StateDecoratedVariable, MutableState, stateOf, observableProxy, Prop, Watch } from '@ohos.arkui.stateManagement'; // should be insert by ui-plugins -import { downloadFilesData } from '../model/dataType'; -import fs from '@ohos.file.fs'; +import { + memo, + __memo_context_type, + __memo_id_type +} from '@ohos.arkui.stateManagement'; // should be insert by ui-plugins +import { + Entry, + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Row, + TextAlign, + Visibility, + List, + ListItem, + ForEach, + TextOverflow, + Image, + HorizontalAlign, + Progress, + ProgressType, + FlexAlign, + SafeAreaType, + JSON, + $r, + Margin, + TextDecorationType +} from '@ohos.arkui.component'; // TextAttribute should be insert by ui-plugins +import { + State, + Link, + MutableState, + stateOf, + observableProxy, + Prop, + Watch +} from '@ohos.arkui.stateManagement'; // should be insert by ui-plugins\ import hilog from '@ohos.hilog'; -import { BusinessError } from '@ohos.base'; import common from '@ohos.app.ability.common'; - -const TAG: string = 'HistoryFiles'; +import fs from '@ohos.file.fs'; +import { BusinessError } from '@ohos.base'; +import request from '@ohos.request'; // 导入上传下载模块。需要配置ohos.permission.INTERNET权限 +import { downloadFilesData } from '../model/dataType'; +const NO_TASK: number = 0; // 0个下载任务 +const INIT_PROGRESS: number = 0; // 进度条初始值 +const BYTE_CONVERSION: number = 1024; // 字节转换 @Component export struct HistoryItem { // 文件名称 - @State fileName: string = '文件名'; + @Prop fileDataInfo: downloadFilesData; + @State fileName: string = ''; // 待下载任务数量 @Link downloadCount: number; // 下载历史列表 - @Link historyArray: downloadFilesData[] = new Array(); + @Link historyArray: downloadFilesData[]; // 下载列表 - @Link downloadFileArray: downloadFilesData[] = new Array(); + @Link downloadFileArray: downloadFilesData[]; // 文件下载状态 @State fileStatus: number = -1; aboutToAppear(): void { + // 从下载链接获取文件名 + this.fileName = this.fileDataInfo.url.split('/').pop() as string; + // 获取文件下载状态 + this.fileStatus = this.fileDataInfo.fileStatus; } + build() { Row(){ Row(){ @@ -65,12 +104,14 @@ export struct HistoryItem { .width(50) .borderRadius(8) .id('fileImage') + .enabled(this.fileStatus === 1 ? true : false) + .opacity(this.fileStatus === 1 ? 1 : 0.7) }.width('17%') Column(){ Row(){ Column(){ - Text(this.fileName) + Text(this.fileDataInfo.url.split('/').pop() || '文件') .height(22) .width('100%') .fontSize(14) @@ -78,24 +119,33 @@ export struct HistoryItem { .textAlign(TextAlign.Start) .maxLines(1) // 限制为单行 .id('fileName') + .enabled(this.fileStatus === 1 ? true : false) + .opacity(this.fileStatus === 1 ? 1 : 0.7) + .decoration({ + type: this.fileStatus === 1 ? TextDecorationType.None : TextDecorationType.LineThrough + }) Row(){ Text($r('app.string.text_download')) .fontSize(12) .fontColor('#66182431') .id('downloadVal') + .enabled(this.fileStatus === 1 ? true : false) + .opacity(this.fileStatus === 1 ? 1 : 0.7) }.width('100%') .height(23) } .width('75%') // 完成按钮 - Button($r('app.string.button_finish')) + Button($r('app.string.multiple_files_download_history_text_downloaded')) .fontSize(12) .height(30) .width('25%') .borderRadius(8) .backgroundColor($r('app.color.operate_rdb_in_taskpool_button_background_color_green')) + .enabled(this.fileStatus === 1 ? true : false) + .opacity(this.fileStatus === 1 ? 1 : 0.7) } } .width('83%') @@ -104,5 +154,6 @@ export struct HistoryItem { } .width('100%') .height(80) + .id(this.fileName) } } \ No newline at end of file diff --git a/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/MultipleFilesDownload.ets b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/MultipeFilesDownload.ets similarity index 40% rename from code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/MultipleFilesDownload.ets rename to code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/MultipeFilesDownload.ets index e1caa88b8d02ccdaa69c8b43a448df16337f06d0..b7354a10677e89436fdb239bc404e7ebb31f9637 100644 --- a/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/MultipleFilesDownload.ets +++ b/code/ArkTS1.2/DownloadSample/entry/src/main/ets/pages/view/MultipeFilesDownload.ets @@ -53,12 +53,12 @@ import { SafeAreaType, JSON, $r, - Margin + Margin, + TextDecorationType } from '@ohos.arkui.component'; // TextAttribute should be insert by ui-plugins import { State, Link, - StateDecoratedVariable, MutableState, stateOf, observableProxy, @@ -66,41 +66,17 @@ import { Watch } from '@ohos.arkui.stateManagement'; // should be insert by ui-plugins\ import hilog from '@ohos.hilog'; -import zlib from '@ohos.zlib'; import common from '@ohos.app.ability.common'; -import fs from '@ohos.file.fs'; -import { BusinessError } from '@ohos.base'; -import { HistoryItem } from './HistoryItem'; +import { BusinessError } from '@ohos.base' import request from '@ohos.request'; // 导入上传下载模块。需要配置ohos.permission.INTERNET权限 import { downloadFilesData } from '../model/dataType'; -import util from '@ohos.util'; +import { FileDownloadItem } from './FileDownloadItem'; +import { HistoryItem } from './HistoryItem'; const NO_TASK: number = 0; // 0个下载任务 const INIT_PROGRESS: number = 0; // 进度条初始值 const BYTE_CONVERSION: number = 1024; // 字节转换 -/** - * 功能描述: 多文件下载监听在应用开发中是一个非常常见的需求。本示例将介绍如何使用request上传下载模块实现多文件下载监听,如监听每个文件下载任务的进度,任务暂停,下载完成等下载情况。 - * 每个应用最多支持创建10个未完成的任务,相关规格说明请参考request.agent.create。 - * - * 推荐场景: 多文件下载 - * - * 核心组件: - * 1. FileDownloadItem - * - * 实现步骤: - * 1.配置下载参数。一个下载任务需要配置对应一套下载参数request.agent.Config。本例中使用downloadConfig方法简单配置了下载文件的url,实际业务 - * 中请按实际情况按需配置。 - * 2.创建多个文件下载监听实例。单个文件下载监听只需要配置下载参数,创建下载任务,注册下载任务相关监听,启动下载任务即可实现。而要实现多文件下载监听, - * 需要每个下载任务注册独立的下载监听回调。本例通过封装自定义组件FileDownloadItem,在每个FileDownloadItem中创建各自的下载任务和监听回调,从 - * 而实现多文件下载监听。 - * 3.创建下载任务,并注册下载任务相关监听。本例在每个FileDownloadItem中使用request.agent.create创建下载任务。然后在下载任务创建成功后,注 - * 册各自下载任务相关监听。本例中注册了下载任务完成回调,下载任务失败回调,下载进度更新回调,暂停任务回调,重新启动任务回调。 - * 4.启动下载任务。本例在每个FileDownloadItem中使用task.start方法启动各自的下载任务。 - * 5.本例中下载任务用到的其他操作:使用request.agent.show,根据任务id可查询任务的详细信息。使用task.pause可以暂停正在等待WAITING/正在运行 - * RUNNING/正在重试RETRYING的后台下载任务。使用task.resume可以重新启动任务,可恢复暂停的后台任务。 - */ - @Entry @Component export struct MultipleFilesDownloadComponent { @@ -122,152 +98,37 @@ export struct MultipleFilesDownloadComponent { // 点击历史列表tab时,控制下载列表相应的内容的显隐 @State downloadPageVisibility: Visibility = Visibility.Visible; // 下载列表数据 - @State downloadFileArray: downloadFilesData[] = new Array(); + @State downloadFileArray: downloadFilesData[] = [ + { + 'id': '1', + 'url': 'https://gitee.com/harmonyos-cases/cases/raw/master/plugin/case_plugin-1.0.10-Alpha.zip', + 'fileStatus': 0, + 'downloadTime': 1728529510880 + } + , + { + 'id': '2', + 'url': 'https://gitee.com/harmonyos-cases/cases/blob/master/CommonAppDevelopment/product/entry/src/main/resources/base/media/after_cache.png', + 'fileStatus': 0, + 'downloadTime': 1728529510880 + } + , + { + 'id': '3', + 'url': 'https://gitee.com/harmonyos-cases/cases/blob/master/CommonAppDevelopment/product/entry/src/main/resources/base/media/icon_main_color.png', + 'fileStatus': 0, + 'downloadTime': 1728529510880 + } + ] as downloadFilesData[]; // 下载历史列表数据 - @State historyList: downloadFilesData[] = new Array(); - // 下载文件名 - @State fileName: string = '文件名'; - // 下载任务状态 - @State state: string = ''; - // 下载状态图标显隐控制。下载中显示图标,下载完成或者下载失败隐藏图标 - @State isShow: boolean = false; - // 是否正在下载标志位 - @State downloading: boolean = false; - // 下载文件大小。类型字符串 - @State sFileSize: string = '-'; - // 下载文件大小。类型数值 - @State nFileSize: number = 0; - // 当前已下载数据量。类型字符串 - @State sCurrentDownloadSize: string = '-'; - // 当前已下载数据量。类型数值 - @State nCurrentDownloadSize: number = 0; - // 下载任务对象初始化。用于下载失败和下载过程中暂停和重新启动下载。 - private downloadTask: request.agent.Task | undefined; + @State historyArray: downloadFilesData[] = [] as downloadFilesData[]; // 下载信息初始化 aboutToAppear(): void { - this.downloadFileArray = [ - { - 'id': '1', - 'url': '这里改为下载文件路径', - 'fileStatus': 0, - 'downloadTime': 1728529510880 - }, - { - 'id': '2', - 'url': '这里改为下载文件路径', - 'fileStatus': 0, - 'downloadTime': 1728529510880 - }, - { - 'id': '3', - 'url': '这里改为下载文件路径', - 'fileStatus': 0, - 'downloadTime': 1728529510880 - }, - { - 'id': '4', - 'url': '这里改为下载文件路径', - 'fileStatus': 0, - 'downloadTime': 1728529510880 - } - ] // 下载数量,用于显示页面上下载队列数量 this.downloadCount = this.downloadFileArray.length; } - // 监听是否开始下载/暂停下载 - onDownLoadUpdated(): void { - if (this.isStartAllDownload) { - this.startAllDownloads(); - } - } - - startAllDownloads() { - this.downloadFileArray.forEach((item: downloadFilesData) => { - this.startDownload(item.url); - }) - } - - // 启动下载任务 - startDownload(url: string) { - // 首次下载,创建任务 - let context = this.getUIContext().getHostContext() as common.UIAbilityContext; - (request.agent.create(context, { - action: request.agent.Action.DOWNLOAD, // 配置任务选项,这里配置为下载任务 - url: url, // 配置下载任务url - overwrite: true, // 下载过程中路径已存在时的解决方案选择。true表示覆盖已存在的文件 - method: 'GET', // HTTP标准方法。下载时,使用GET或POST。 - saveas: './', // 这里'./'表示下载至应用当前缓存路径下。 - mode: request.agent.Mode.BACKGROUND, // 任务模式设置后台任务。 - gauge: true, // 后台任务的过程进度通知策略,仅应用于后台任务。true表示发出每个进度已完成或失败的通知。 - retry: false, // 默认为true,如果没有网络或者网络不满足时,会自动暂停waiting,等网络满足时进行一次重试。设置为false时,没网直接走失败回调 - } as request.agent.Config) as Promise) - .then((task: request.agent.Task) => { - // 注册下载任务相关回调 - task.on('completed', (progress: request.agent.Progress) => { - hilog.info(0x0000, 'TAGhttp', `下载完成`); - }); // 下载任务完成回调 - task.on('progress', this.progressCallback); // 下载进度更新回调 - task.on('failed', (progress: request.agent.Progress) => { - hilog.info(0x0000, 'TAGhttp', `下载失败`); - }); // 下载任务失败回调 - - task.on('resume', (progress: request.agent.Progress) => { - hilog.info(0x0000, 'TAGhttp', `重新启动`); - }); // 重新启动任务回调 - - task.on('pause', (progress: request.agent.Progress) => { - hilog.info(0x0000, 'TAGhttp', `暂停`); - }); // 暂停任务回调 - - // TODO 知识点:启动下载任务。本例在每个FileDownloadItem中使用task.start方法启动各自的下载任务。 - task.start((err: BusinessError) => { - if (err) { - hilog.info(0x0000, 'TAGhttp', - `Failed to task start with error message: ${err.message}, error code: ${err.code}`); - return; - } - this.downloadTask = task as request.agent.Task; - }) - }) - } - - // 下载进度更新回调 - private progressCallback: (progress: request.agent.Progress) => void = (progress: request.agent.Progress) => { - // 性能知识点: 如果注册了progress下载进度更新监听,不建议在progress下载进度更新回调中加日志打印,减少不必要的性能损耗。 - this.state = '下载中'; - this.downloading = true; - // 显示下载状态图标 - this.isShow = true; - // 第一次开始下载 - if (this.sFileSize === '-') { - // 如果下载url文件的服务器采用chunk分块传输文件数据,是获取不到下载文件总大小的。传过来的值为-1,则在页面上显示'未知大小' - if (progress.sizes[0] === -1) { - this.sFileSize = '未知大小'; - // 文件大小无法获取的情况下,进度条的值设置为0,总进度设置为1 - this.nCurrentDownloadSize = 0; - this.nFileSize = 1; - } else { - // 能获取文件大小时,按实际下载数据量更新进度 - this.nFileSize = progress.sizes[0]; - hilog.info(0x0000, 'TAGhttp', `nFileSize: ` + String(this.nFileSize)); - this.sFileSize = (progress.sizes[0] / BYTE_CONVERSION) + 'kb'; - hilog.info(0x0000, 'TAGhttp', `sFileSize: ` + String(this.sFileSize)); - this.nCurrentDownloadSize = progress.processed; - hilog.info(0x0000, 'TAGhttp', `nCurrentDownloadSize: ` + String(this.nCurrentDownloadSize)); - } - } else if (this.sFileSize === '未知大小') { - // 非首次下载(暂停过下载任务后重新启动下载时),文件大小未知情况时,下载时进度不做更新 - hilog.info(0x0000, 'TAGhttp', `When the file size is unknown, the download progress will not be updated`); - } else { - // 非首次下载(暂停过下载任务后重新启动下载时),文件大小能获取到的情况,更新下载进度 - this.nCurrentDownloadSize = progress.processed; - } - // 用于显示已下载文件数据大小 - this.sCurrentDownloadSize = (progress.processed / BYTE_CONVERSION) + 'kb'; - } - build() { Column() { Text($r('app.string.router_back')) @@ -296,6 +157,9 @@ export struct MultipleFilesDownloadComponent { this.downloadPageVisibility = Visibility.Visible; this.historyPageVisibility = Visibility.None; this.isHistoryPageEnabled = false; + if(this.downloadCount === 0) { + this.isStartAllDownload = false; + } }) Text($r('app.string.multiple_files_download_album_backup')) @@ -313,6 +177,9 @@ export struct MultipleFilesDownloadComponent { this.downloadPageVisibility = Visibility.None; this.historyPageVisibility = Visibility.Visible; this.isHistoryPageEnabled = true; + if(this.downloadCount === 0) { + this.isStartAllDownload = false; + } }) } .width('100%') @@ -338,7 +205,6 @@ export struct MultipleFilesDownloadComponent { return; } this.isStartAllDownload = !this.isStartAllDownload; - this.onDownLoadUpdated() }) }.width('50%') } @@ -350,62 +216,14 @@ export struct MultipleFilesDownloadComponent { List() { ForEach(this.downloadFileArray, (item: downloadFilesData) => { ListItem() { - Row() { - Row() { - Image($r('app.media.multiple_files_download_file')) - .height(50) - .width(50) - .borderRadius(8) - .id('fileImage') - }.width('17%') - - Column() { - Row() { - Column() { - Text(this.fileName) - .height(22) - .width('100%') - .fontSize(14) - .fontColor('#000000') - .textAlign(TextAlign.Start) - .maxLines(1)// 限制为单行 - .id('fileName') - - Row() { - Text(this.sCurrentDownloadSize + '/' + this.sFileSize + 'MB ') - .fontSize(12) - .fontColor('#66182431') - .id('downloadVal') - - Text(this.state) - .fontSize(12) - .fontColor('#66182431') - .id(this.fileName + 'state') - }.width('100%') - .height(23) - } - .width('90%') - - Image($r('app.media.multiple_files_download_start')) - .height(25) - .width('10%') - .id('downloadImage') - } - - // 下载进度条,用于显示从下载进度更新回调中获取到的已下载数据大小 - Progress({ value: INIT_PROGRESS, total: this.nFileSize, type: ProgressType.Capsule }) - .value(1) - .height(5) - .id('progress') - - } - .width('83%') - .height(50) - .alignItems(HorizontalAlign.Start) - } - .width('100%') - .height(50) - .margin({ top: 15, bottom: 15 } as Margin) + FileDownloadItem({ + fileDataInfo: item, // 文件下载配置 + isStartAllDownload: this.isStartAllDownload, // 是否全部开始下载 + downloadCount: this.downloadCount, // 待下载任务数量 + downloadFailCount: this.downloadFailCount, // 下载失败任务数量 + historyArray: this.historyArray, + downloadFileArray: this.downloadFileArray // 下载文件数据源 + }) } }, (item: downloadFilesData) => item.id) } @@ -417,17 +235,16 @@ export struct MultipleFilesDownloadComponent { // 下载历史列表 List() { - ForEach(this.downloadFileArray, (item: downloadFilesData) => { + ForEach(this.historyArray, (item: downloadFilesData) => { ListItem() { HistoryItem({ + fileDataInfo: item, downloadCount: this.downloadCount, // 待下载任务数量 - historyArray: this.historyList, + historyArray: this.historyArray, downloadFileArray: this.downloadFileArray // 下载文件数据源 }) - }.onClick((): void => { - hilog.info(0x0000, 'testTag', '点击单个文件'); - }) - }) + } + }, (item: downloadFilesData) => item.id) }.visibility(this.historyPageVisibility) .enabled(this.isHistoryPageEnabled) .width('100%') diff --git a/code/ArkTS1.2/DownloadSample/entry/src/main/resources/base/element/color.json b/code/ArkTS1.2/DownloadSample/entry/src/main/resources/base/element/color.json index 4d7d6016598d738996ff7ea3be42ad8df7516edf..c2e868edecd832c8a17ee6aeb05d91b1bb6f98e4 100644 --- a/code/ArkTS1.2/DownloadSample/entry/src/main/resources/base/element/color.json +++ b/code/ArkTS1.2/DownloadSample/entry/src/main/resources/base/element/color.json @@ -32,6 +32,5 @@ "name": "operate_rdb_in_taskpool_button_background_color_green", "value": "#FF2F9117" } - ] } \ No newline at end of file diff --git a/code/ArkTS1.2/DownloadSample/entry/src/main/resources/base/element/string.json b/code/ArkTS1.2/DownloadSample/entry/src/main/resources/base/element/string.json index 1f87b0cf1c3f1b2b503ad882d9936d66b42f7f3d..762b286ced657991a401efb933ec10d163ef4b58 100644 --- a/code/ArkTS1.2/DownloadSample/entry/src/main/resources/base/element/string.json +++ b/code/ArkTS1.2/DownloadSample/entry/src/main/resources/base/element/string.json @@ -1,5 +1,9 @@ { "string": [ + { + "name": "router_back", + "value": "←" + }, { "name": "module_desc", "value": "module description" @@ -8,10 +12,6 @@ "name": "EntryAbility_desc", "value": "description" }, - { - "name": "router_back", - "value": "←" - }, { "name": "EntryAbility_label", "value": "DownloadSample" diff --git a/code/ArkTS1.2/DownloadSample/ohosTest.md b/code/ArkTS1.2/DownloadSample/ohosTest.md index 33406552043ea890890f2e4946ea6f24633953ab..43e444453e48b6def1c4fa687366a344a1c5b306 100644 --- a/code/ArkTS1.2/DownloadSample/ohosTest.md +++ b/code/ArkTS1.2/DownloadSample/ohosTest.md @@ -9,4 +9,3 @@ | 查看设置"压缩偏好"选项后,压缩功能是否正常 | 1. 需在真机测试
2. 构建并安装测试hap
3.进入【图片压缩方案】| 1、选择“优先压缩质量”
2、选择“优先压缩尺寸”
3、点击压缩按钮 | 1、“packing最小二分单位”滑块可以正常滑动,“最低图片质量”滑块不可滑动
2、“packing最小二分单位”滑块不可滑动,“最低图片质量”滑块可以滑动
3、在"压缩后"下方显示压缩后的图片大小,格式,尺寸| 否 | Pass | | 选择不同的"输出格式",查看压缩后的图片格式 | 1. 需在真机测试
2. 构建并安装测试hap
3.进入【图片压缩方案】| 1、选择“原格式”,点击压缩按钮
2、选择“PNG”,点击压缩按钮
3、选择“JPG”,点击压缩按钮
4、选择“WEBP”,点击压缩按钮 | 1、压缩后的图片和压缩前图片格式一致
2、压缩后的图片格式为PNG格式
3、压缩后的图片格式为JPG格式
4、压缩后的图片格式为WEBP格式 | 否 | Pass | | 下载文件是否正常 | 1. 需在真机测试
2. 构建并安装测试hap
3.进入【多文件下载监听案例】| 1、点击“全部开始”
2、点击“全部暂停”
3、点击“全部开始”
4、等待下载完成后,查看下载的文件是否保存在应用缓存路径下(手工可用hdc shell cd /data/app/el2/100/base/com.example.helloworld/haps/entry/cache) | 1、下载队列中两个任务能正常下载
2、下载队列中正在下载的任务被暂停了
3、下载任务重新恢复下载
4、下载完成后,在案例应用缓存路径下可以找到下载的文件:favor_list_tea.png favor_list_tree.png | 否 | Pass | -| 在下载历史中把已删除的文件重新下载 | 1. 需在真机测试
2. 构建并安装测试hap
3.进入【多文件下载监听案例】| 1、点击已下载文件
2、点击“删除”
3、点击已删除文件
4、点击“确认”| 1、弹出“文件已存在”toast
2、文件被删除样式发生变化且在案例应用缓存路径下不能找到已删除的文件
3、弹出“xxx文件已被删除,是否重新下载”的弹窗提示
4、文件重新下载 | 否 | Pass | \ No newline at end of file