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

前言

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

实现效果

需求分析

  • 图片下载突破最大 5M 限制。
  • 图片链接以 jpg结尾但是张 GIF。

技术实现

下载突破 5M 限制

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

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

全部评论

相关推荐

06-28 19:03
已编辑
产品经理
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务