react通过fetch下载二进制图片并使用blob展示

环境说明

前端框架: react 16.8.6、umi 3.2.20、antd-mobile 2.3.1
后端框架: jdk1.8、spring boot 2.3.4
浏览器:chrome 85.0.4183.121

业务场景:
原有页面是展示一堆图片,这些图片从java后台获取的,后台直接往outputStream输出图片的二进制数据,前端通过img标签的src属性来显示这些图片

现在由于增加了鉴权功能,使用的jwt,往http header里面放jwt来鉴权的,然后就不能使用img的src来做了,现在前端请求使用的umi-request(axios也可以的),配置了拦截器自动添加header里的jwt,所以想通过前端fetch(当然ajax也是可以的)去请求图片,然后通过blob去展示图片

关于前端Blob

Binary Large Object的缩写,代表二进制类型的大对象。Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。
Blob常用于文件上传下载、图片显示、资源分段上传、读取本地文件

Blob URL/Object URL
Blob URL/Object URL 是一种伪协议,允许 Blob 和 File 对象用作图像,下载二进制数据链接等的 URL 源。在浏览器中,我们使用 URL.createObjectURL 方法来创建 Blob URL,该方法接收一个 Blob 对象,并为其创建一个唯一的 URL,对应的示例如下:
blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f641
浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个 URL → Blob 映射。因此,此类 URL 较短,但可以访问 Blob。生成的 URL 仅在当前文档打开的状态下才有效。它允许引用 <img>、<a> 中的 Blob,但如果你访问的 Blob URL 不再存在,则会从浏览器中收到 404 错误。

URL.revokeObjectURL(url)
上述的 Blob URL 看似很不错,但实际上它也有副作用。虽然存储了 URL → Blob 的映射,但 Blob 本身仍驻留在内存中,浏览器无法释放它。映射在文档卸载时自动清除,因此 Blob 对象随后被释放。但是,如果应用程序寿命很长,那不会很快发生。因此,如果我们创建一个 Blob URL,即使不再需要该 Blob,它也会存在内存中。
针对这个问题,我们可以调用 URL.revokeObjectURL(url) 方法,从内部映射中删除引用,从而允许删除 Blob(如果没有其他引用),并释放内存。接下来,我们来看一下 Blob 文件下载的具体示例。

实操

首先java端代码,主要就是从sftp里面获取到图片二进制数据,然后输出

@RequestMapping("/getSendImage/{indexId}")
@ResponseBody
@SuppressWarnings("unchecked")
public void getStaticImage(@PathVariable("indexId") Integer indexId,
                           HttpServletRequest request, HttpServletResponse response) throws IOException {
    
    
    Map<String, String> payload = (Map<String, String>) request.getAttribute(JwtParams.JWT_PAYLOAD.getValue());

    List<Map<String, Object>> contents = objectMapper.readValue(payload.get(JwtParams.CONTENTS.getValue()), List.class);
    Long contId = Long.parseLong(String.valueOf(contents.get(indexId).get(JwtParams.CONT_ID.getValue())));

    response.setHeader("Content-type", "application/octet-stream;charset=UTF-8");
    response.setCharacterEncoding("UTF-8");
    ServletOutputStream outputStream = response.getOutputStream();
    MMSSendContent sendContent = mmsSendContentService.get(contId);
    SFTPTool.downLoadFileForStream("MMS_SEND_RECORD" + File.separator + sendContent.getContPath(), sendContent.getContSrc(), outputStream);
    outputStream.flush();
    outputStream.close();
}

后台有个返回图片的接口,直接将数据推到OutputStream,原来使用的img src就可以直接接受这种类型的图片,现在由于增加了jwt鉴权,后端没有接收到jwt会在后台拦截器返回401。需要前端在header里面设置jwt,然后通过react的umi-request去发送请求,如果jwt验签不通过,也会在前端的拦截器自动跳转到401页面

接下来就是js的请求

export async function getStaticImage(indexId : number) {
    
    
  return request(`/aimms/app/mms/getSendImage/${
      
      indexId}`, {
    
    
    method: 'POST',
    requestType: 'form',
    responseType: 'blob'
  });
};

最关键的是这个responseType: ‘blob’,这样配置后,请求后返回的是一个blob,如果用的是axios去请求,那么也可以请求的时候在option里带上responseType: ‘blob’,需要设置这个参数,blob才能正常保存二进制数据
在这里插入图片描述
上面是responseType可以设置的值

private fetchStaticImage(index: number) {
    
    
  return new Promise((resolve, reject) => {
    
    
    this.props.dispatch({
    
    
      type: `${
      
      namespace}/getStaticImage`,
      payload: index,
      callback: (data: any) => {
    
    
        let mmsData = this.state.mmsData;
        let imageUrl = URL.createObjectURL(data);
        mmsData[index] = {
    
    src: imageUrl, type: 'img'};
        this.setState({
    
    
          mmsData,
        });
        resolve();
      },
    });
  })
}

然后我们拿到这个blob,就可以create URL了,这里我将生成后的URL放进了react的state保存,方便下面读取

这一步遇到的问题:
很奇怪的是原来返回的是二进制,没有加responseType: ‘blob’,然后通过const blob = new Blob([content]);的形式再去创建url,url就无法访问,img上看图片就裂开了,可能是我使用方法不对吧。
后面考虑过后台直接返回图片的base64编码,前端直接使用base64编码来展示图片,但是由于业务的原因,这里图片可能会很大(超过1M),这样转base64后会造成html文本膨胀,影响性能。
所以关键还是要加responseType: ‘blob‘

{
    
    
  this.state.mmsData.map((e, index) => {
    
    
    if(e.type === 'img') {
    
    
      return <div className="text-center" key={
    
    index}>
        <span className={
    
    styles.content}>{
    
    index + 1}:</span>
        <img src={
    
    this.state.mmsData[index].src}
             style={
    
    {
    
    width: '100%'}}/>
      </div>
  })
}

然后在页面上遍历这个集合,渲染出来即可,img的动态src指向生成的blob URL即可
在这里插入图片描述
运行起来就可以看到post请求了三张图片,生成url后,再去通过blob的url访问了三张图片
在这里插入图片描述
这是post接口返回的数据,可以发现是二进制数据
在这里插入图片描述
然后blob返回的,虽然Content-Type: application/octet-stream,但是可以看到图片了

参考&推荐阅读

Web API 接口参考Blob
聊聊JS的二进制家族:Blob、ArrayBuffer和Buffer
你不知道的 Blob

猜你喜欢

转载自blog.csdn.net/w57685321/article/details/109010380