鸿蒙应用开发:下载功能

简介: 鸿蒙应用开发:下载功能

鸿蒙系统不断发展,有与安卓、iOS 形成三足鼎立之势,且其在智能手机、智能穿戴、车载、家居等行业领域的应用越来越广泛。作为开发者,如何抓住鸿蒙生态崛起的机遇,解决开发挑战,创造更好的应用体验?欢迎您和我们一起探讨~

鸿蒙提供了多种API用于下载,今天我就拿request包来讲解下如何实现下载。

版本:

DevEco Studio 4.1 Release

SDK10

API
request API中介绍了两种下载方式:request.downloadFile和request.agent.create。如果仅限于下载,两者区别不大,仅仅是下载路径区别。前者的config可指定所有沙箱路径,后者的config只能指定缓存目录。但是若考虑到一些细节问题,如断点续传等,推荐第二种。

主代码
下载功能主代码要求输入下载地址(url)和下载文件名(name),并且首先基于文件名检查文件是否存在,然后创建下载任务,并且在下载过程中订阅了下载进度,即时将下载信息传递给页面进行渲染。

import fs from '@ohos.file.fs';
import request from '@ohos.request';
import Prompt from '@system.prompt';
import EventBus from './EventBus';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';

let downloadTask: request.agent.Task;
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

export class DownLoader {

/**

  • 下载文件
  • @param url 文件地址
  • @param name 文件名
    */
    static async downloadthisFile(url,name) {
    let filePath = context.cacheDir+'/'+name;
    try {

    // 检查文件是否存在
    const fileExists = await checkFileExists(filePath);
    if (fileExists) {
    console.log("File already exists, deleting the existing file...");
    await fs.unlink(filePath); // 删除已存在的文件
    console.log("File deleted successfully.");
    }

    // 创建下载任务
    let config: request.agent.Config = {
    action: request.agent.Action.DOWNLOAD,
    url: url,
    saveas: './'+name,
    overwrite: true,
    mode: request.agent.Mode.FOREGROUND,
    index: 0,
    begins: 0,
    ends: -1,
    };

    try {
    downloadTask = await request.agent.create(getContext(), config);
    console.info(Succeeded in creating a download task. result: ${downloadTask.tid});
    downloadTask.start();
    Prompt.showToast({ message: "Download task started.", duration: 1000 });

    // 订阅下载进度
    if (downloadTask.tid) {

     let lastReceivedSize = 0;
     let lastTime = Date.now();
    
     // 监听下载进度
     let call = async (progress: request.agent.Progress) => {
       const Speed = calculateSpeed(progress.processed, lastTime, lastReceivedSize);
       const percent = (progress.processed / progress.sizes[0]) * 100;
       const size = (progress.sizes[0] / 1024 / 1024).toFixed(2); // MB
       console.log("进度 速度 大小", percent, Speed, size);
    
       // 直接更新状态
       EventBus.emit('downloadProgress', percent);
       EventBus.emit('downloadSpeed', Speed);
       EventBus.emit('downloadSize', size);
       console.log("Download task returned:", downloadTask.tid)
     };
    
     // 订阅下载进度
     downloadTask.on('progress', call);
    
     console.log("Download task returned:", downloadTask.tid);
    

    }
    } catch (err) {
    console.error(Failed to create a download task, Code: ${err.code}, message: ${err.message});
    }

    if (!downloadTask.tid)
    throw new Error("Download task failed, no task returned.");

    } catch (err) {
    console.error(Failed to download file. Error: ${err.message});
    }

    }

    /**

  • 暂停下载
    */
    static async pausedownload() {
    downloadTask.pause().then(() => {
    console.info(Succeeded in pausing a download task.);
    }).catch((err: BusinessError) => {
    console.error(Failed to pause the upload task, Code: ${err.code}, message: ${err.message});
    });
    }

    /**

  • 恢复下载
    */
    static async resumedownload() {
    downloadTask.resume().then(() => {
    console.info(Succeeded in resuming a download task.);
    }).catch((err: BusinessError) => {
    console.error(Failed to resume the download task, Code: ${err.code}, message: ${err.message});
    });
    }
    }

/**

  • 计算下载速度
  • @param receivedSize 已接收的字节数
  • @param lastTime 上次计算速度的时间戳
  • @param lastReceivedSize 上次计算速度时已接收的字节数
  • @returns 速度,单位KB/s
    */
    function calculateSpeed(receivedSize,lastTime,lastReceivedSize) {
    const currentTime = Date.now();
    const timeElapsed = (currentTime - lastTime) / 1000; // 转换为秒
    const bytesReceived = receivedSize - lastReceivedSize; // 新接收到的字节数

    // 计算速度
    const speed = timeElapsed > 0 ? (bytesReceived / timeElapsed) : 0; // 字节/秒

    // 更新历史数据
    lastTime = currentTime;
    lastReceivedSize = receivedSize;

    return Number((speed / 1024).toFixed(2)); // 转换为KB/s
    };

/**

  • 检查文件是否存在
  • @param filePath 文件路径
  • @returns Promise 文件是否存在
    */
    async function checkFileExists(filePath) {
    try {
    console.log("Checking file existence for:", filePath);
    await fs.stat(filePath);
    return true; // 文件存在
    } catch (err) {
    if (err.code === 'ENOENT') {
    return false; // 文件不存在
    } else {
    console.error(Error checking file existence: ${err.message});
    return false; // 处理其他错误
    }
    }
    }
    页面
    页面主体是两个输入框,输入下载地址(url)和下载文件名(name),执行下载的按钮、暂停(继续)按钮以及一个圆形进度条。

import { DownLoader } from '../common/Download';
import EventBus from '../common/EventBus';

@Entry
@Component
struct Index {
@State message: string = 'Hello World';
@State download: boolean = true;
@State url: string = '';
@State fileName: string = '';
@State percent: number = 0;

aboutToAppear(): void {
// 订阅下载进度更新
EventBus.on('downloadProgress', async (percent) => {
this.percent = percent;
console.log("Updated download progress:", this.percent);
});
}
build() {
Column() {
TextInput({ text: this.url, placeholder: 'input your download url' }).margin({ top: 20 })
.onSubmit((EnterKeyType) => {
console.info(EnterKeyType + '输入法回车键的类型值')
})
TextInput({ text: this.fileName, placeholder: 'input your download file name' }).margin({ top: 20 })
.onSubmit((EnterKeyType) => {
console.info(EnterKeyType + '输入法回车键的类型值')
})
Button('Download')
.width(150)
.margin({ top: 20 })
.onClick(async ()=>{
await DownLoader.downloadthisFile(this.url,this.fileName);
})
Button(this.download ? 'Pause' : 'Resume')
.width(150)
.margin({ top: 20 })
.onClick(async ()=>{
this.download = !this.download;
this.download ? DownLoader.resumedownload() : DownLoader.pausedownload();
})

  Progress({ value: this.percent, total: 100, type: ProgressType.ScaleRing }).width(100).height(100)
    .margin({top: 50})
    .visibility(this.percent >= 0 && this.percent <= 100 ? Visibility.Visible : Visibility.Hidden)
    .backgroundColor(Color.Black)
    .style({ strokeWidth: 15, scaleCount: 20, scaleWidth: 5 })    // 设置环形有刻度进度条宽度15,总刻度数为20,刻度宽度为5vp
}
.padding(20)
.width('100%')

}
}

下载过程

相关文章
|
6月前
|
开发者 容器
鸿蒙应用开发从入门到实战(十四):ArkUI组件Column&Row&线性布局
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解Column和Row组件的使用以及线性布局的方法。
573 12
|
6月前
|
API 数据处理
鸿蒙应用开发从入门到实战(十三):ArkUI组件Slider&Progress
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解滑块Slider和进度条Progress组件的使用。
291 1
|
6月前
|
JavaScript 开发者 索引
鸿蒙应用开发从入门到实战(九):ArkTS渲染控制
ArkTS拓展了TypeScript,可以结合ArkUI进行渲染控制,是的界面设计具有可编程性。本文简要描述鸿蒙应用开发中的条件渲染和循环渲染。
290 5
|
6月前
|
数据安全/隐私保护 开发者
鸿蒙应用开发从入门到实战(十一):ArkUI组件Text&TextInput
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解文本组件Text和TextInput的使用。
432 3
|
6月前
鸿蒙应用开发从入门到实战(十六):线性布局案例
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文通过简单案例演示如何使用Column和Row组件实现线性布局。
250 1
|
6月前
|
API 数据处理
鸿蒙应用开发从入门到实战(十三):ArkUI组件Slider&Progress
ArkUI提供滑块Slider与进度条Progress组件,用于鸿蒙原生APP开发。Slider支持拖动调节音量、亮度等,可设步长、方向及提示气泡;Progress支持线性、环形等多种样式,可自定义颜色、宽度与刻度,实时显示任务进度。
406 2
|
6月前
|
JavaScript
鸿蒙应用开发从入门到实战(八):ArkTS自定义组件语法
ArkUI除系统预置的组件外,还支持自定义组件。使用自定义组件,可使代码的结构更加清晰,并且能提高代码的复用性。
322 7
|
6月前
|
开发者
鸿蒙应用开发从入门到实战(十二):ArkUI组件Button&Toggle
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解按钮组件Button和Toggle的使用。
481 2
|
6月前
|
存储 编解码 JSON
鸿蒙应用开发从入门到实战(十):ArkUI图片组件Image
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解Image图片组件的使用。
408 1
|
6月前
|
API
鸿蒙应用开发从入门到实战(七):ArkTS组件声明语法
《鸿蒙应用开发从入门到项目实战》系列文章持续更新中,陆续更新AI+编程、企业级项目实战等原创内容、欢迎关注!​本文从界面制作从组件声明开始,通过一个相对简单的案例来系统的学习 ArkTS 声明组件的语法。
204 2

热门文章

最新文章