鸿蒙应用开发:下载功能
鸿蒙系统不断发展,有与安卓、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); // MBconsole.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<boolean> 文件是否存在*/
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%')}
}
下载过程
tips:执行下载的代码很简单,接下来完善一下断点续传等功能。欢迎感兴趣的看官老爷指点交流~