写点什么

HarmonyOS 实战:GIF 图下载突破 5M 限制保存到相册

作者:IT小码哥
  • 2025-06-28
    北京
  • 本文字数:2095 字

    阅读完需:约 7 分钟

前言

最近在鸿蒙项目开发过程中,测试提出图片预览时无法下载的 bug,这么简单的功能怎么会有问题。一开始还以为是手机的问题或者网络不好,拿到测试机复现问题的时候发现下载的 Gif 图确实无法下载成功,报了个 2300023 错误码,查看鸿蒙文档才发现图片下载时使用的是 http 请求,但是 http 请求限制了流的大小为 5M,而 Gif 的大小超过了限制,本篇文章将详细带你一步步解决这个问题,同时解决图片链接为 png 但是下载后是 Gif 图的问题,建议点赞收藏!


需求分析

  • 图片下载突破最大 5M 限制。

  • 图片链接以 jpg 结尾但是张 GIF。

技术实现

下载突破 5M 限制

官方的 http 请求对数据流大小有限制,最大为 5M,所以需要采用其他方案实现下载功能,这里采用 HttpRequest 的 requestInStream 对数据流进行分段下载。


  1. 首先创建 HttpRequest,这里使用 http 创建。


let httpRequest = http.createHttp();
复制代码


  1. 调用 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()      }    })
复制代码


  1. 订阅数据接收方法 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;
});
复制代码


  1. 当图片分段下载时,需要根据订阅 dataEnd 方法来判断所有的数据流接收完成,同时将完整的数据流回调出去。


 httpRequest.on('dataEnd', () => {      success(res)    })
复制代码


  1. 当网络请求结束时,需要取消所有订阅的事件。


 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 图保存成普通的图片,这一点在日常开发中特别重要,已经学会了的小伙伴,赶快动手试试吧。


发布于: 刚刚阅读数: 4
用户头像

IT小码哥

关注

还未添加个人签名 2021-04-29 加入

还未添加个人简介

评论

发布
暂无评论
HarmonyOS实战:GIF图下载突破5M限制保存到相册_IT小码哥_InfoQ写作社区