鸿蒙应用开发:断点下载
鸿蒙系统不断发展,有与安卓、iOS 形成三足鼎立之势,且其在智能手机、智能穿戴、车载、家居等行业领域的应用越来越广泛。作为开发者,如何抓住鸿蒙生态崛起的机遇,解决开发挑战,创造更好的应用体验?欢迎您和我们一起探讨~
之前跟各位看官老爷们分享了使用request API实现下载功能,并且强调了request.agent.create具备实现断点续传(下载)功能。今天就以限速下载为例跟大家分享一下。
断点续传(下载)
断点续传是一种文件传输技术,它允许用户在下载或上传文件的过程中暂停,然后从停止的位置继续开始,而不需要重新开始整个过程。这项技术对于网络不稳定或者文件非常大的情况特别有用,因为它可以避免因网络中断或其他原因导致的下载失败后需要重新下载整个文件的问题。
断点续传的工作原理
-
请求头部信息:当客户端(如浏览器或下载管理器)首次尝试下载一个大文件时,它会向服务器发送一个HTTP请求。这个请求可能包含一个特殊的头部
Range
,用来指定希望从哪个字节开始下载。 -
服务器响应:如果服务器支持断点续传,它会在响应中包含一个状态码206(Partial Content),表示只返回了部分内容,并且会使用
Content-Range
头部来说明实际返回的数据范围。如果请求的是整个文件,则服务器可能会返回状态码200(OK)和整个文件。 -
下载并保存:客户端接收到数据后,将其保存到本地。如果下载过程中断,客户端可以记录下已经下载了多少数据。
-
续传请求:当用户决定恢复下载时,客户端再次向服务器发送带有
Range
头部的请求,指明从上次中断的地方开始下载剩余的部分。 -
完成下载:服务器根据新的请求发送剩余的数据,直到文件完整下载完毕。
实现方式
-
HTTP协议支持:大多数现代Web服务器都支持HTTP/1.1协议中的
Range
请求,这意味着它们能够处理断点续传的需求。客户端只需要正确地构造包含Range
头部的请求即可实现断点续传。 -
FTP协议:FTP协议也支持断点续传,通过使用
REST
命令来指定文件偏移量,从而从特定位置开始下载或上传。 -
下载管理器:许多下载管理器软件提供了断点续传的功能。这些工具通常会自动处理上述过程,包括检测网络连接是否稳定、自动重试以及在连接恢复后从断点处继续下载等。
-
自定义解决方案:对于某些特殊场景,可能需要开发定制化的断点续传功能。这通常涉及到与服务器端进行沟通,确保双方都能理解并执行断点续传的逻辑。
限速下载
首先让我们来看看什么是限速下载。
限速下载是指在下载文件时对下载速度进行限制,以防止占用过多的网络带宽,影响其他网络应用的正常使用。在开发过程中通常是采用限宽方法,也可以通过定期检查已下载的数据量和所花费的时间,计算出平均下载速度。如果超过设定的速度限制,就暂停下载一段时间,以达到限速的效果。
本文所采用的方法其实是第二种,但也不存粹。因为在使用暂停-重启循环时发现会报错,于是将暂停-重启换为了开始-结束循环。那么相应的再一次启动下载的起始位必然不能是0!那么下一次的下载配置如何设置呢?
先来看看配置信息Config,其中的begins和ends参数便是实现断点续传的关键:我们只需在下载超速时将已下载字节数当作下一次下载的begins即可!
那么我们是否可以执行限速下载么?
答案是不可以!
WHY?
因为config中的overwrite属性不答应!
overwrite属性有两个值:true-覆盖已下载文件;false-下载失败。根据上面的思路很显然我们要执行多次下载任务,每一次都是原始任务的一部分那必然不能被覆盖。所以我们在执行新的下载任务时还要更新保存路径!
那是不是可以开始下载了?
依然不行!!!
因为这时候我们下载的文件是分割的!!我们还要把它“拼”起来!!
限速主代码
// 限速
let limtedSpeed = 1500;
if (Speed > limtedSpeed) {
if(!isDownloading){
return;
}
downloadTask.off('progress',call);
downloadTask.stop()
let next = WriteStream(config.saveas);
lastReceivedSize = 0;
console.log("the number out,开始分片下载:上一次开头" + config.begins)
config.begins = next + config.begins;
console.log("the number out,开始分片下载:下一次开头" + config.begins)
config.index ++
config.saveas = './update'+config.index+'.zip';
const sleepTime = Math.max(0, (Speed - limtedSpeed) / limtedSpeed * 1000);
console.log("Sleeping for:", sleepTime);
setTimeout(async ()=>{
if(!isDownloading){
return;
}
downloadTask = await request.agent.create(getContext(), config);
downloadTask.start();
downloadTask.on('progress',call)
}, sleepTime)
return
}
文件拼接
// 创建文件流
function CreatStream() {
let pathDir = context.filesDir;
let filePath = pathDir + "/update.zip";
stream = fs.createStreamSync(filePath, "w+");
console.info("createStream succeed");
return filePath
}
// 写入文件
function WriteStream(name: string) {
let filePath = context.cacheDir + "/" + name;
let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE);
let buf = new ArrayBuffer(8485760); //8485760
let len = fs.readSync(file.fd, buf);
stream.flushSync();
let num = stream.writeSync(buf,{length:len});
console.log('the number of bytes written: ' + num, 'the number of bytes read: ' + len)
fs.unlinkSync(filePath);
return num;
}
页面
Row(){
Column(){
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();
})
}
Column(){
Button('LimtedDownload')
.width(150)
.margin({ top: 20 })
.onClick(async ()=>{
await DownLoader.limtedDownload(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();
})
}
}
gitee仓库附上:DownLoad: 基于request包的鸿蒙开发,实现基本下载和限速下载功能 欢迎多多点星