赞
踩
在开发应用时,要实现高效的客户端跟服务器之间数据交换,文件传输的性能是至关重要的。一个数据交换性能较低的应用会导致其在加载过程中耗费较长时间,在很多的场景造成页面卡顿,极大的影响了用户体验。相反,一个数据交换高效的应用,则会让应用变得更加流畅。
本文将介绍两种常见的上传下载传输和网络请求的关键技术:数据压缩和断点续传,可提升上传下载的性能、减少宽带占用,从而提高数据传输效率。
目前系统内提供给文件上传下载可用的模块有@ohos.net.http模块和@ohos.request模块。@ohos.net.http模块提供基础的HTTP数据请求能力,功能较为基础,本文不做介绍。@ohos.request模块主要给应用提供上传下载文件、后台传输代理的基础能力。它具备任务管理系统的默认并发功能,简化下载功能的实现和管理,提升数据传输的安全,整合通知机制,新增任务状态与进度查询功能,具有灵活性、高效性、可扩展性、可靠性、一致性和安全性的优势。
具体来说,@ohos.request模块包括以下功能:
使用@ohos.request模块执行下载的任务,具有四种运行状态:初始任务、就绪任务、挂起任务、待网任务、成功任务、失败任务。可以通过create创建任务,start开始任务,pause挂起任务,resume恢复任务,remove移除任务,stop停止任务,任务结果有final-failed任务失败,final-completed下载完成,recoverable-failed重试失败,并支持查询任务状态,具体流程如图一所示:
图一 模块流程图
场景1:低带宽网络上传琐碎文件场景
在网络连接较差,低带宽的网络环境中,HTTP连接的建立耗时可能会大幅提升。这时候进行数据压缩可以加快页面加载速度,并减少HTTP请求数量和移动数据流量。
场景2:处理大量资源的场景
如应用商店、网盘应用等,这类应用通常拥有大体积的文件资源。当用户从暂停或者断网中重新恢复时,如果从头开始上传下载则会额外耗费大量的时间。此时可以采用断点续传方法进行上传下载。
数据压缩是指在应用中对数据进行压缩,以减少存储空间和数据传输量、节省带宽,提高加载速度。数据压缩通常在网络传输和存储方面发挥着重要作用,特别是在处理大量数据或需要频繁传输数据的场景下。
在应用开发中,常见的数据压缩技术分类如下:
以从相册批量上传图片为例,介绍大量文件打包无损压缩上传相关技术,下图为相关示例的界面截图:
图二 相册批量上传图片示例图
以批量上传照片(分辨率为480*640,24位,平均大小50~120KB)为例,在RK设备上测试的结果如下表所示:
图三 上传数量和耗时对比图表
由于上传耗时收到网络状态影响偏差较大,结果取的几次测量结果的最小值。但是仍然可以从数据中看出,优化前的耗时基本为线性增长,压缩优化后的耗时在上传文件数量较低时并不明显,还会因为多余的压缩处理影响耗时。不过随着上传的照片数量增多,优化后的耗时和优化之前的耗时差距越来越明显,优化效果越好。
数据压缩的相关示例代码如下:
1.导入相关模块:
import common from '@ohos.app.ability.common';
import fs from '@ohos.file.fs';
import zlib from '@ohos.zlib';
2.创建压缩上传相关类:
class ZipUpload {
// 创建任务前存放的uri
private waitList: Array<string> = [];
// 需要上传的文件uri
private fileUris: Array<string> = [];
...
}
3.建立用于接收图库图片的临时文件夹,并将整个临时文件夹打包添加到待上传list内:
// 文件压缩处理 async zipUploadFiles(fileUris: Array<string>): Promise<void> { this.context = getContext(this) as common.UIAbilityContext; let cacheDir = this.context.cacheDir; let tempDir = fs.mkdtempSync(`${cacheDir}/XXXXXX`); // 将图库图片获取的uri放入fileUris中,遍历复制到临时文件夹 for (let i = 0; i < fileUris.length; i++) { let fileName = fileUris[i].split('/').pop(); let resourceFile: fs.File = fs.openSync(fileUris[i], fs.OpenMode.READ_ONLY); fs.copyFileSync(resourceFile.fd, `${tempDir}/${fileName}`, 0); fs.closeSync(resourceFile); } // 文件压缩,将之前生成的临时文件夹内打包到test.zip内 let options: zlib.Options = { level: zlib.CompressLevel.COMPRESS_LEVEL_DEFAULT_COMPRESSION, memLevel: zlib.MemLevel.MEM_LEVEL_DEFAULT, strategy: zlib.CompressStrategy.COMPRESS_STRATEGY_DEFAULT_STRATEGY }; let data = await zlib.compressFile(tempDir, `${cacheDir}/test.zip`, options); // 删除临时文件夹 fs.rmdirSync(tempDir); // 将生成的zip包放到传输队列 this.waitList.push(`${cacheDir}/test.zip`); }
断点续传功能的实现,不管是应用端还是服务器端都需要用到合理的技术来互相协同。在实际开发中,开发者无需亲自实现断点续传功能,只需对SDK进行合理配置。
在应用端需要用到的技术和API:
在服务器端需要用到的技术:
通过结合应用端和服务器端的相关技术,可以共同实现高效且可靠的文件断点续传功能,提供更好的用户体验并确保数据传输的稳定性。
对于大文件断点续传上传,本文采用@ohos.request(上传下载)模块中的request.agent任务托管接口,可以自动实现暂停继续重试等操作,无需手动将文件分片和记录上传分片信息。流程图如图四所示:
图四 断点续传上传流程图
断点续传上传示例代码如下:
1.导入相关模块:
import common from '@ohos.app.ability.common';
import request from '@ohos.request';
1.创建相关上传类:
class Upload {
// 后台任务
private backgroundTask: request.agent.Task | undefined = undefined;
// 创建任务前存放的uri
private waitList: Array<string> = [];
...
}
1.生成MD5码,上传到服务器进行校验:
async checkFileExist(fileUri: string): Promise<boolean> { let httpRequest = http.createHttp(); // 生成md5码 let md5 = await hash.hash(fileUri, 'md5'); let requestOption: http.HttpRequestOptions = { method: http.RequestMethod.POST, extraData: { 'MD5': md5 } } let response = await httpRequest.request('http://XXX.XXX.XXX.XXX/XXXX', requestOption); let result = response.result; let flag = false; ... // 根据服务器返回对应数据判断是否存在 if (flag) { return true; } else { return false; } }
1.配置Config,创建后台上传任务:
private config: request.agent.Config = { action: request.agent.Action.UPLOAD, headers: HEADER, url: '', mode: request.agent.Mode.BACKGROUND, method: 'POST', title: 'upload', network: request.agent.Network.ANY, data: [], token: 'UPLOAD_TOKEN' } ... // 转换uri private async getFilesAndData(cacheDir: string, fileUris: Array<string>): Promise<Array<request.agent.FormItem>> { ... } // 创建文件上传后台任务 async createBackgroundTask(fileUris: Array<string>) { // 获取上传url this.config.url = 'http://XXX.XXX.XXX.XXX'; this.config.mode = request.agent.Mode.BACKGROUND; let tempData = await this.getFilesAndData(this.context.cacheDir, fileUris); // 判断每个文件是否为空 for (let i = 0; i < tempData.length; i++) { let flag = await this.checkFileExist(`${this.context.cacheDir}/${tempData[i].name}`); if (!flag) { this.config.data.push(tempData[i]) } } let isFileExist = await this.checkFileExist(`${this.context.cacheDir}/${this.config.data[0].name}`); if (this.config.data.length === 0) { return; } this.backgroundTask = await request.agent.create(this.context, this.config); }
1.任务开始:
await this.backgroundTask.start();
1.任务暂停:
async pause() {
if (this.backgroundTask === undefined) {
return;
}
await this.backgroundTask.pause();
}
1.任务继续:
async resume() {
if (this.backgroundTask === undefined) {
return;
}
await this.backgroundTask.resume();
}
对于大文件断点续传下载,也可以直接调用request.agent接口,该接口的断点续传是基于HTTP协议Header里的Range字段实现的,在任务暂停重启的时候,会自动设置Header中的Range字段,无需进行额外的配置。
Range简介
HTTP协议里面的Range字段,官方名称为范围请求,它允许服务器只发送 HTTP
消息的一部分到客户端,可以用来请求部分数据而不是整个资源。Range的格式通常是Range:
=-,其中表示范围所采用的单位,通常是字节(bytes), 和
表示请求的起始字节和结束字节的位置。Range语法如下:
// 表示从range-start到文件末尾 Range: =- //
表示从range-start到range-end Range: =- //
可以同时选择多段,用逗号分隔 Range: =-,
- // 示例:表示返回1024 bytes之后的文件 Range: bytes=1024-服务器收到请求后,正确处理请求会回复206 Partial Content,未正常处理则会回复其他响应码。下表是服务器回复的常见响应码:
服务器响应码常见的原因206 Partial Content服务器收到正常Range请求的响应码,返回部分内容的响应。416 Range
Not Satisfiable所请求的范围不合法,表示服务器错误。200 OK服务器忽略了 Range 首部,返回整个文件。
断点续传下载示例代码如下:
1.导入模块:
import common from '@ohos.app.ability.common';
import request from '@ohos.request';
2.创建下载类:
class Download {
// 任务存放前的uri
private waitList: Array<string[]> = [];
// 下载任务
private downloadTask: request.agent.Task | undefined = undefined;
// 后台任务下载列表
private backgroundDownloadTaskList: Array<request.agent.Task> = [];
...
}
3.配置Config,创建后台下载任务:
async createBackgroundTask(downloadList: Array<string[]>) { let splitUrl = url.split('//')[1].split('/'); let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; let downloadConfig: request.agent.Config = { action: request.agent.Action.DOWNLOAD, url: url, method: 'POST', title: 'download', mode: request.agent.Mode.FOREGROUND, // 必须是后台任务才能续传 network: request.agent.Network.ANY, saveas: `./${folder}/${splitUrl[splitUrl.length-1]}`, overwrite: true } this.downloadTask = await request.agent.create(context, downloadConfig); if (this.backgroundDownloadTaskList.findIndex(task => task.config.url === downTask.config.url) === -1) { this.backgroundDownloadTaskList.push(downTask); } }
4.任务开始:
...
await downTask.start();
...
5.任务暂停:
async pause() {
if (this.backgroundDownloadTaskList.length === 0) {
return;
}
this.backgroundDownloadTaskList.forEach(async task => {
await task.pause();
})
}
6.任务继续:
async resume() {
if (this.backgroundDownloadTaskList.length === 0) {
return;
}
this.backgroundDownloadTaskList.forEach(async task => {
await task.resume();
})
}
7.任务停止:
async deleteAllBackTasks() {
if (this.backgroundDownloadTaskList.length > 0) {
this.backgroundDownloadTaskList.forEach(async task => {
await request.agent.remove(task.tid);
})
this.backgroundDownloadTaskList = [];
}
}
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.基本概念
2.构建第一个ArkTS应用
3.……
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
https://docs.qq.com/doc/DZVVBYlhuRkZQZlB3
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。