当前位置: 首页 > news >正文

开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载

 文章的目的为了记录使用Arkts 进行Harmony app 开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 Arkts 鸿蒙应用 开发(一)工程文件分析-CSDN博客

开源 Arkts 鸿蒙应用 开发(二)封装库.har制作和应用-CSDN博客

开源 Arkts 鸿蒙应用 开发(三)Arkts的介绍-CSDN博客

开源 Arkts 鸿蒙应用 开发(四)布局和常用控件-CSDN博客

开源 Arkts 鸿蒙应用 开发(五)控件组成和复杂控件-CSDN博客

开源 Arkts 鸿蒙应用 开发(六)数据持久--文件和首选项存储-CSDN博客

开源 Arkts 鸿蒙应用 开发(七)数据持久--sqlite关系数据库-CSDN博客

开源 Arkts 鸿蒙应用 开发(八)多媒体--相册和相机-CSDN博客

开源 Arkts 鸿蒙应用 开发(九)通讯--tcp客户端-CSDN博客

开源 Arkts 鸿蒙应用 开发(十)通讯--Http-CSDN博客

开源 Arkts 鸿蒙应用 开发(十一)证书和包名修改-CSDN博客

开源 Arkts 鸿蒙应用 开发(十二)传感器的使用-CSDN博客

开源 Arkts 鸿蒙应用 开发(十三)音频--MP3播放_arkts avplayer播放音频 mp3-CSDN博客

开源 Arkts 鸿蒙应用 开发(十四)线程--任务池(taskpool)-CSDN博客

开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘-CSDN博客

开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图-CSDN博客

开源 Arkts 鸿蒙应用 开发(十七)通讯--http多文件下载-CSDN博客

开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器-CSDN博客

 推荐链接:

开源 java android app 开发(一)开发环境的搭建-CSDN博客

开源 java android app 开发(二)工程文件结构-CSDN博客

开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客

开源 java android app 开发(四)GUI界面重要组件-CSDN博客

开源 java android app 开发(五)文件和数据库存储-CSDN博客

开源 java android app 开发(六)多媒体使用-CSDN博客

开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客

开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客

开源 java android app 开发(九)后台之线程和服务-CSDN博客

开源 java android app 开发(十)广播机制-CSDN博客

开源 java android app 开发(十一)调试、发布-CSDN博客

开源 java android app 开发(十二)封库.aar-CSDN博客

推荐链接:

开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客

开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-CSDN博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客

本章内容主要演示了一个多文件下载的HarmonyOS应用界面,主要包含三个部分:MultipleFilesDownload主组件、FileDownloadItem下载项组件和ProgressButton进度按钮组件。

1.工程结构

2.源码解析

3.演示效果

4.工程下载网址

一、工程结构,主要包括3个文件,Index.ets,FileDownloadItem.ets,ProgressButton.ets。

二、源码解析

2.1  Index.ets文件:管理多个文件的下载任务,提供"全部开始/暂停"功能,显示下载列表。

简单说明:

使用@Entry和@Component装饰器声明为入口组件,

维护一个下载URL数组downloadUrlArray

使用ForEach渲染多个FileDownloadItem组件

提供全局的开始/暂停所有下载的功能

跟踪下载计数和失败计数

以下为代码:

import { request } from '@kit.BasicServicesKit';
import { FileDownloadItem } from '../view/FileDownloadItem';const NO_TASK: number = 0;function downloadConfig(downloadUrl: string): request.agent.Config {const config: request.agent.Config = {action: request.agent.Action.DOWNLOAD,url: downloadUrl,overwrite: true,method: 'GET',saveas: './',mode: request.agent.Mode.BACKGROUND,gauge: true,retry: false};return config;
}@Entry
@Component
struct MultipleFilesDownload {/*** enter the download urls*/private downloadUrlArray: string[] = ["https://m.down.sandai.net/mobile/OfficialSite_MobileThunder1.apk"];@State downloadConfigArray: request.agent.Config[] = [];@State isStartAllDownload: boolean = false;@State downloadCount: number = 0;@State downloadFailCount: number = 0;aboutToAppear(): void {for (let i = 0; i < this.downloadUrlArray.length; i++) {const config: request.agent.Config = downloadConfig(this.downloadUrlArray[i]);this.downloadConfigArray.push(config);}this.downloadCount = this.downloadUrlArray.length;}build() {Column() {Row() {Text($r('app.string.multiple_files_download_transfer_list')).fontWeight(FontWeight.Bold).fontSize($r('app.integer.title_font_size')).width('100%').fontColor($r('app.color.text_color'))}.alignItems(VerticalAlign.Bottom).width('91.1%').height(112)Row() {Row() {Text($r('app.string.multiple_files_download_queue')).fontSize($r('app.integer.multiple_files_download_text_font_size_fourteen')).fontColor($r('sys.color.ohos_id_color_text_secondary'))}.width($r('app.string.multiple_files_download_row_width'))Row() {Text(this.isStartAllDownload && this.downloadCount > NO_TASK ? $r('app.string.pause_all') :$r('app.string.start_all')).fontSize($r('app.integer.multiple_files_download_text_font_size_fourteen')).fontWeight(500).fontColor($r('sys.color.ohos_id_color_text_primary_activated')).textAlign(TextAlign.End).width($r('app.string.multiple_files_download_row_text_width')).onClick(() => {if (this.downloadCount === NO_TASK) {this.getUIContext().showAlertDialog({message: $r('app.string.multiple_files_download_completed'),alignment: DialogAlignment.Center});return;}this.isStartAllDownload = !this.isStartAllDownload;})}.width($r('app.string.multiple_files_download_row_width'))}.margin({left: 16,right: 16,top: $r('app.integer.multiple_files_download_margin_top_twenty_eight'),bottom: $r('app.integer.margin_eight')})List() {ForEach(this.downloadConfigArray, (item: request.agent.Config) => {ListItem() {FileDownloadItem({downloadConfig: item,isStartAllDownload: this.isStartAllDownload,downloadCount: this.downloadCount,downloadFailCount: this.downloadFailCount})}}, (item: request.agent.Config) => JSON.stringify(item))}.width('100%').height('100%')}.focusable(false)}
}@Builder
export function getMultipleFilesDownload(): void {MultipleFilesDownload();
}

2.2  FileDownloadItem.ets组件:显示文件名、下载进度和状态,提供暂停/继续下载功能,处理下载过程中的各种回调。

简单说明:

completedCallback: 下载完成

failedCallback: 下载失败

pauseCallback: 暂停下载

resumeCallback: 继续下载

progressCallback: 下载进度更新

responseCallback: HTTP响应

使用ProgressButton组件显示下载进度和控制按钮

处理文件大小和下载进度的显示转换

import { common } from '@kit.AbilityKit';
import { request } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';import { ProgressButton } from './ProgressButton';const uiContext: UIContext | undefined = AppStorage.get('uiContext');const TAG = 'FileDownloadItem';
const BYTE_CONVERSION: number = 1024;
const context = uiContext!.getHostContext()! as common.UIAbilityContext;function getFileNameFromUrl(url: string): string {const segments = url.split('/');let tmp = segments.pop() || '';if (tmp.indexOf('?') != -1) {return tmp.split('?')[0];}return tmp;
}@Component
export struct FileDownloadItem {@State downloadConfig: request.agent.Config = { action: request.agent.Action.DOWNLOAD, url: '' };@State fileName: string = '';@State textState: string | Resource = "";@Link @Watch('onDownLoadUpdated') isStartAllDownload: boolean;private downloadTask: request.agent.Task | undefined = undefined;@Link downloadCount: number;@Link downloadFailCount: number;@State isShow: boolean = false;@State downloading: boolean = false;@State sFileSize: string | number = "-";@State nFileSize: number = 0;@State sCurrentDownloadSize: string = "-";@State @Watch('updateProgress') nCurrentDownloadSize: number = 0;@State progress: number = 0;@State isPaused: boolean = false;private completedCallback = (progress: request.agent.Progress) => {this.textState = $r("app.string.download_completed");if (this.sFileSize === -1) {this.sFileSize = this.sCurrentDownloadSizethis.nCurrentDownloadSize = 1;}this.downloadCount--;}private failedCallback = (progress: request.agent.Progress) => {this.textState = $r("app.string.download_fail");this.downloadFailCount++;if (this.downloadFailCount === this.downloadCount) {this.isStartAllDownload = false;}if (this.downloadTask) {request.agent.show(this.downloadTask.tid, (err: Error, taskInfo: request.agent.TaskInfo) => {if (err) {hilog.error(0x0000, TAG, 'agent show error:', err);return;}});}}private pauseCallback = (progress: request.agent.Progress) => {this.isPaused = true;this.downloading = false;this.textState = $r("app.string.paused");}private resumeCallback = (progress: request.agent.Progress) => {this.isPaused = false;this.textState = $r("app.string.downloading");this.downloading = true;}private progressCallback = (progress: request.agent.Progress) => {this.textState = $r("app.string.downloading");this.downloading = true;this.isShow = true;if (this.downloadTask) {if (this.sFileSize === '-') {if (progress.sizes[0] === -1) {this.sFileSize = -1;this.nCurrentDownloadSize = 0;this.nFileSize = 1;} else {this.nFileSize = progress.sizes[0];this.sFileSize = (progress.sizes[0] / BYTE_CONVERSION).toFixed() + 'kb';this.nCurrentDownloadSize = progress.processed;}} else if (this.sFileSize === -1) {hilog.info(0x0000, TAG, 'file size unknown');} else {this.nCurrentDownloadSize = progress.processed;}this.sCurrentDownloadSize = (progress.processed / BYTE_CONVERSION).toFixed() + 'kb';}}private responseCallback = (response: request.agent.HttpResponse) => {hilog.info(0x0000, TAG, 'response:' + response.statusCode);};updateProgress() {setTimeout(() => {if (this.textState == $r("app.string.paused")) {this.isPaused = true;return;}let tmp = this.nCurrentDownloadSize / this.nFileSize * 100;if (tmp <= this.progress) {return;}this.progress = tmp;}, 10)}aboutToAppear(): void {this.fileName = getFileNameFromUrl(this.downloadConfig.url);}onDownLoadUpdated(): void {if (this.isStartAllDownload) {if (this.textState === $r("app.string.download_fail")) {this.downloadTask = undefined;this.isShow = false;this.textState = "";}this.startDownload();} else {if (this.downloadFailCount > 0 && this.downloadFailCount === this.downloadCount) {this.downloadFailCount = 0;} else {this.pauseDownload();}}}startDownload(): void {if (this.downloadTask === undefined) {request.agent.create(context, this.downloadConfig).then((task: request.agent.Task) => {task.on('completed', this.completedCallback);task.on('failed', this.failedCallback);task.on('pause', this.pauseCallback);task.on('resume', this.resumeCallback);task.on('progress', this.progressCallback);task.on('response', this.responseCallback);task.start().then(() => {this.downloadTask = task;}).catch((err: Error) => {hilog.error(0x0000, TAG, 'task start error:', err);})}).catch((err: Error) => {hilog.error(0x0000, TAG, 'create error:', err);});} else {this.resumeDownload();}}pauseOrResumeDownload(): void {if (this.downloadTask) {request.agent.show(this.downloadTask.tid, (err: Error, taskInfo: request.agent.TaskInfo) => {if (err) {hilog.error(0x0000, TAG, 'agent show error:', err);return;}if (taskInfo.progress.state === request.agent.State.PAUSED) {this.resumeDownload();} else {this.pauseDownload();}});}}pauseDownload(): void {if (this.downloadTask) {request.agent.show(this.downloadTask.tid, (err: Error, taskInfo: request.agent.TaskInfo) => {if (err) {hilog.error(0x0000, TAG, 'agent show error:', err);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, TAG, 'task pause error:', err);});}});}}resumeDownload(): void {if (this.downloadTask) {request.agent.show(this.downloadTask.tid, (err: Error, taskInfo: request.agent.TaskInfo) => {if (err) {hilog.error(0x0000, TAG, 'agent show error:', err);return;}if (this.downloadTask && taskInfo.progress.state === request.agent.State.PAUSED) {this.downloadTask.resume().then(() => {}).catch((err: Error) => {hilog.error(0x0000, TAG, 'task resume error:', err);});}});}}build() {RelativeContainer() {Image($r('app.media.multiple_files_download_file')).height($r("app.integer.multiple_files_download_image_size")).width($r("app.integer.multiple_files_download_image_size")).id('fileImage').alignRules({center: { anchor: '__container__', align: VerticalAlign.Center }})Text(this.fileName).fontSize($r("app.integer.multiple_files_download_text_font_size")).padding({ left: $r("app.integer.multiple_files_download_padding") }).alignRules({left: { anchor: 'fileImage', align: HorizontalAlign.End },top: { anchor: 'fileImage', align: VerticalAlign.Top },}).fontWeight(FontWeight.Medium).margin({ top: $r('app.integer.item_name_top_margin') }).id('fileName')ProgressButton({paused: this.isPaused,progress: this.progress,progressButtonWidth: $r('app.integer.progress_btn_width'),content: this.textState,enable: true,clickCallback: () => {this.pauseOrResumeDownload();}}).visibility(this.isShow ? Visibility.Visible : Visibility.Hidden).alignRules({right: { anchor: '__container__', align: HorizontalAlign.End },center: { anchor: '__container__', align: VerticalAlign.Center }})Text(this.sCurrentDownloadSize + "/" + this.sFileSize).fontSize($r('app.integer.multiple_files_download_text_font_size_fourteen')).width($r('app.string.multiple_files_download_text_width')).fontColor($r('app.color.multiple_files_download_text_font_color')).margin({ top: $r("app.integer.multiple_files_download_margin_top") }).padding({ left: $r("app.integer.multiple_files_download_padding") }).alignRules({top: { anchor: 'fileName', align: VerticalAlign.Bottom },left: { anchor: 'fileImage', align: HorizontalAlign.End }}).id('downloadVal')}.height($r('app.integer.item_height')).margin({ left: 16, right: 16 }).padding({ left: 12, right: 12 })}
}

2.3  ProgressButton.ets,这个自定义的组建实现了:显示下载进度百分比,根据状态显示不同文本(下载中/暂停/完成),提供点击回调功能。

简单说明:

使用HarmonyOS的Progress组件显示进度条

根据进度值自动更新显示文本

支持启用/禁用状态

提供圆角胶囊样式

const EMPTY_STRING: string = '';
const MAX_PROGRESS: number = 100;
const MAX_PERCENTAGE: string = '100%';
const MIN_PERCENTAGE: string = '0%';
const TEXT_OPACITY: number = 0.4;
const BUTTON_NORMAL_WIDTH: number = 44;
const BUTTON_NORMAL_HEIGHT: number = 28;
const BUTTON_BORDER_RADIUS: number = 14;
const TEXT_ENABLE: number = 1.0;
const MIN_WIDTH: Length = 44;
const PADDING_TEXT: Length = 8;const PROGRESS_BUTTON_PROGRESS_KEY = 'progress_button_progress_key';
const PROGRESS_BUTTON_PRIMARY_FONT_KEY = 'progress_button_primary_font_key';
const PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY = 'progress_button_container_background_color_key';
const PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY = 'progress_button_emphasize_secondary_button_key';@Component
export struct ProgressButton {@Prop @Watch('updateText') paused: boolean = false;@Prop @Watch('getProgressContext') progress: number;@State textProgress: string = EMPTY_STRING;@Prop content: string | Resource = EMPTY_STRING;@State isLoading: boolean = false;progressButtonWidth?: Length = BUTTON_NORMAL_WIDTH;clickCallback: () => void = () => {};@Prop enable: boolean = true;@State progressColor: ResourceColor = '#330A59F7';@State containerBorderColor: ResourceColor = '#330A59F7';@State containerBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_foreground_contrary');private getButtonProgress(): number {if (this.progress < 0) {return 0;} else if (this.progress > MAX_PROGRESS) {return MAX_PROGRESS;}return this.progress;}updateText() {if (this.paused) {setTimeout(() => {this.isLoading = !this.paused;}, 10);} else {this.isLoading = !this.paused;}}private getProgressContext() {if (this.progress < 0) {this.isLoading = false;this.textProgress = MIN_PERCENTAGE;} else if (this.progress >= MAX_PROGRESS) {this.isLoading = false;this.textProgress = MAX_PERCENTAGE;} else {this.isLoading = true;this.textProgress = Math.floor(this.progress / MAX_PROGRESS * MAX_PROGRESS).toString() + "%";}}build() {Button() {Stack() {Progress({value: this.getButtonProgress(), total: MAX_PROGRESS,style: ProgressStyle.Capsule}).height(BUTTON_NORMAL_HEIGHT).borderRadius(BUTTON_BORDER_RADIUS).width('100%').hoverEffect(HoverEffect.None).clip(false).enabled(this.enable).key(PROGRESS_BUTTON_PROGRESS_KEY).color(this.progressColor)Text(this.isLoading ? this.textProgress : this.content).fontSize($r('sys.float.ohos_id_text_size_button3')).fontWeight(FontWeight.Medium).key(PROGRESS_BUTTON_PRIMARY_FONT_KEY).maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).padding({ left: PADDING_TEXT, right: PADDING_TEXT }).opacity(this.enable ? TEXT_ENABLE : TEXT_OPACITY)Row().key(PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY).backgroundColor(Color.Transparent).border({ width: 1, color: this.containerBorderColor }).height(BUTTON_NORMAL_HEIGHT).borderRadius(BUTTON_BORDER_RADIUS).width('100%')}}.borderRadius(BUTTON_BORDER_RADIUS).clip(false).hoverEffect(HoverEffect.None).key(PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY).backgroundColor(this.containerBackgroundColor).constraintSize({ minWidth: MIN_WIDTH }).width((!this.progressButtonWidth || this.progressButtonWidth < BUTTON_NORMAL_WIDTH) ?BUTTON_NORMAL_WIDTH : this.progressButtonWidth).stateEffect(this.enable).onClick(() => {if (!this.enable) {return;}if (this.progress < MAX_PROGRESS) {this.isLoading = !this.isLoading;}this.clickCallback && this.clickCallback();})}
}

2.4  module.json5权限需要添加ohos.permission.INTERNET

{"module": {"name": "entry","type": "entry","description": "$string:module_desc","mainElement": "EntryAbility","deviceTypes": ["phone"],"requestPermissions": [{"name": "ohos.permission.INTERNET","reason": "$string:reason_internet","usedScene": {"abilities": ["EntryAbility"],"when": "always"}},],"deliveryWithInstall": true,"installationFree": false,"pages": "$profile:main_pages","abilities": [{"name": "EntryAbility","srcEntry": "./ets/entryability/EntryAbility.ets","description": "$string:EntryAbility_desc","icon": "$media:layered_image","label": "$string:EntryAbility_label","startWindowIcon": "$media:startIcon","startWindowBackground": "$color:start_window_background","exported": true,"skills": [{"entities": ["entity.system.home"],"actions": ["action.system.home"]}]}]}
}

三、演示效果,下载地址数组里只用写了1个,所以只有1个文件,需要的可以自行添加。

四、工程下载网址:https://download.csdn.net/download/ajassi2000/91685765?spm=1011.2124.3001.6210

http://www.dtcms.com/a/333994.html

相关文章:

  • 储能领域大数据平台的设计中如何使用 Hadoop、Spark、Flink 等组件实现数据采集、清洗、存储及实时 / 离线计算,支持储能系统分析与预测
  • 三天速通 Vue+Flask+SQLite 项目+阿里云轻量应用级服务器【宝塔面板】②
  • IO流与单例模式
  • java项目怎么实现用户行为分析、漏斗转化、数据可视化报表。
  • 奈飞工厂 —— 算法优化实战推荐
  • QT基础入门
  • AI热点周报(8.10~8.16):AI界“冰火两重天“,GPT-5陷入热议,DeepSeek R2模型训练受阻?
  • SpringBoot学习日记(九)
  • uv - 基本使用
  • 【LeetCode 热题 100】70. 爬楼梯——(解法二)自底向上
  • [1Prompt1Story] 滑动窗口机制 | 图像生成管线 | VAE变分自编码器 | UNet去噪神经网络
  • 双指针-leetcode算法题总结+练习
  • Ubuntu2204server系统安装后的初始化配置报错
  • Windows 基于ACL(访问控制列表)的权限管理
  • FTP上传文件错误
  • C# 中常用 IO 类的核心属性表格及使用示例
  • 流处理、实时分析与RAG驱动的Python ETL框架:构建智能数据管道(下)
  • FastDeploy2.0:Prometheus3.5.0通过直接采集,进行性能指标分析
  • KNN 算法详解:从电影分类到鸢尾花识别的实战指南
  • EP1C12F324I7N Altera Cyclone FPGA
  • 肖臻《区块链技术与应用》第23-26讲 - The DAO事件、BEC事件、反思和总结
  • 陪诊小程序系统开发:让就医不再是一件难事
  • UniApp 页面传参方式详解
  • 告别在线转换风险:本地运行的PDF转Word技术评测
  • Redis-plus-plus 安装指南
  • AI杀死的第一个仪式:“hello world”
  • 分享一个Oracle表空间自动扩容与清理脚本
  • 告别重复纹理:用Substance Designer构建UE5程序化地貌材质系统
  • 设计模式之静态代理
  • 基于Python3.10.6与jieba库的中文分词模型接口在Windows Server 2022上的实现与部署教程