前言
最近在鸿蒙项目开发过程中,测试提出图片预览时无法下载的 bug,这么简单的功能怎么会有问题。一开始还以为是手机的问题或者网络不好,拿到测试机复现问题的时候发现下载的 Gif 图确实无法下载成功,报了个 2300023 错误码,查看鸿蒙文档才发现图片下载时使用的是 http 请求,但是 http 请求限制了流的大小为 5M,而 Gif 的大小超过了限制,本篇文章将详细带你一步步解决这个问题,同时解决图片链接为 png 但是下载后是 Gif 图的问题,建议点赞收藏!
需求分析
图片下载突破最大 5M 限制。
图片链接以 jpg 结尾但是张 GIF。
技术实现
下载突破 5M 限制
官方的 http 请求对数据流大小有限制,最大为 5M,所以需要采用其他方案实现下载功能,这里采用 HttpRequest 的 requestInStream 对数据流进行分段下载。
首先创建 HttpRequest,这里使用 http 创建。
let httpRequest = http.createHttp();
复制代码
调用 http.requestInstream 方法,根据返回码来判断链接是否请求成功,同时需要配置一些网络请求参数。可根据需要配置。
let streamInfo: http.HttpRequestOptions = {
method: http.RequestMethod.GET, // 可选,默认为http.RequestMethod.GET
// 开发者根据自身业务需要添加header字段
header: {
'Content-Type': 'application/json'
},
expectDataType: http.HttpDataType.ARRAY_BUFFER, // 可选,指定返回数据的类型
usingCache: true, // 可选,默认为true
priority: 1, // 可选,默认为1
connectTimeout: 60000, // 可选,默认为60000ms
readTimeout: 60000, // 可选,默认为60000ms。若传输的数据较大,需要较长的时间,建议增大该参数以保证数据传输正常终止
}
httpRequest.requestInStream(imageUrl, streamInfo).then((data: number) => {
if (data == 200) {
} else {
fail()
}
})
复制代码
订阅数据接收方法 dataReceive,由于数据流是分段下载所有需要额外定义一个 ArrayBuffer 用来接收完整的数据流。
let res = new ArrayBuffer(0);
httpRequest.on('dataReceive', (data: ArrayBuffer) => {
const newRes = new ArrayBuffer(res.byteLength + data.byteLength);
const resView = new Uint8Array(newRes);
resView.set(new Uint8Array(res));
resView.set(new Uint8Array(data), res.byteLength);
res = newRes;
});
复制代码
当图片分段下载时,需要根据订阅 dataEnd 方法来判断所有的数据流接收完成,同时将完整的数据流回调出去。
httpRequest.on('dataEnd', () => {
success(res)
})
复制代码
当网络请求结束时,需要取消所有订阅的事件。
httpRequest.off('headerReceive')
httpRequest.off('dataEnd')
复制代码
判断图片类型
一般图片链接是以具体图片类型结尾,如 .png,.jep 等场景类型,但是也有没有直接给出图片类型的链接,甚至还有图片链接以 jep 结尾,但是图片确实 GIF 格式。这时如果仅仅根据链接判断容易判断失误。
1.先需要创建一个 PixelMap,这里根据网络请求返回的 ArrayBuffer 来创建 PixelMap。
let imageSource: image.ImageSource = image.createImageSource(buffer);
class tmp {
height: number = 100
width: number = 100
};
let options: Record<string, number | boolean | tmp> = {
'alphaType': 0, // 透明度
'editable': false, // 是否可编辑
'pixelFormat': 3, // 像素格式
'scaleMode': 1, // 缩略值
'size': { height: 100, width: 100 }
}; // 创建图片大小
imageSource.createPixelMap(options).then((pixelMap: PixelMap) => {
})
复制代码
2. 根据返回的 pixelMap 获取图片的 ImageInfo。
pixelMap.getImageInfo().then(async (info: image.ImageInfo) => {
}).catch((err: BusinessError) => {
})
复制代码
3. 根据返回的 image.ImageInfo 获取图片的 mimeType,如果是 GIF 图,则为 image/gif,如果时 png 则为 image/png。然后再将图片保存至相册时,指定图片的正确类型即可。
const context = getContext() as common.UIAbilityContext; // 获取getPhotoAccessHelper需要的context
const helper = photoAccessHelper.getPhotoAccessHelper(context); // 获取相册管理模块的实例
const uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, info.mimeType == "image/gif"?'gif':'jpg'); // 指定待创建的文件类型、后缀和创建选项,创建图片或视频资源
const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
let r = await fs.write(file.fd, buffer);
await fs.close(file.fd);
复制代码
这里为了方便处理,仅处理 GIF 的类型,其他类型都是 png。
总结
日常使用 http 请求能满足大部分的场景,但是遇到 GIF 图体积较大时,就必须使用分段式下载。将图片保存时必须获取图片信息的真实类型才能保存成功,否则很容易将 GIF 图保存成普通的图片,这一点在日常开发中特别重要,已经学会了的小伙伴,赶快动手试试吧。
评论