Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端根据后端返回实现下载或者预览的几种方式 #52

Open
18888628835 opened this issue Jun 28, 2021 · 6 comments
Open

Comments

@18888628835
Copy link
Owner

18888628835 commented Jun 28, 2021

1.处理后端传递过来的二进制流文件

最近遇到后端要直接传送文件流,让我把二进制文件流流转成图片的需求。

一开始以为是请求后得到一个图片的 src,但是当我打开响应数据时,发现这样的乱码
image
很明显,这是一个二进制文件流,那么我需要把他转成图片展示在页面上。应该如何做呢?

主要思路:还是用 Blob 对象的 createObjectURL这个 API,让返回的数据能够生成本地对象URL,然后我们将生成的 URl 放到 img 标签上就行了。

responseType

这里重点要介绍一下 responseType 这个XMLHttpRequest对象的属性,它允许我们手动的设置返回数据的类型。如果我们将它设置为一个空字符串,它将使用默认的"text"类型。

也就是说我们可以手动将返回的 response 数据转化成 blob 对象,以下是我用 axios 设置responseType发送请求的代码

axios({
        url: `${apiPrefix}/workflow/api/getCirculatePictures`,
        params: { processInstanceId },
        responseType: 'blob',
      })

使用 axios 可以很方便地手动设置这个属性。如果用原生XMLHttpRequest的话就是这样设置的

let request=new XMLHttpRequest()
request.open('GET',url)
xhr.responseType = 'blob'  //这里写
request.onreadystatechange=()=>{
  if(httpRequest.readystate===4 && httpRequest.status===200){
    console.log('success')
  }else{
    console.log('fail')
  }
}
request.send()

设置完之后我们打印出来看看
image

会发现默认type 是"text/xml",那么我们需要使用 new Blob 构造函数重新生成一个图片的 blob 对象。

blob 转化

语法
var aBlob = new Blob( array, options );

options 中有个 type 选项,可以设置 blob 的 MIME类型,我们需要转化成image/png

得到我们要的 blob 对象类型后,我们就可以使用 createObjectURL生成对象 URL。

实现代码

axios({
        url: `${apiPrefix}/workflow/api/getCirculatePictures`,
        params: { processInstanceId },
        responseType: 'blob',
      }).then((res) => {
        console.log(res);
        let blob = new Blob([res.data], { type: 'image/png' });
        let url = window.URL.createObjectURL(blob); //这个 url 就可以用啦
        setImgSrc(url);//这个函数用来设置 img 的 src 属性
      });

不过为了性能着想,这里最好再设置一下revokeObjectURL。当你结束使用某个 URL 对象之后,应该通过调用这个方法来让浏览器知道不用在内存中继续保留对这个文件的引用了。

        imgElement.onload = function () {
          window.URL.revokeObjectURL(this.src);
        };

将blob转回JSON

虽然上面的代码已经能够处理后端成功返回的文件流了,但是实际业务中客户不一定能够成功导出文件,比如有可能客户想导出100万条数据但是后端不允许。
所以我们还需要告诉客户为什么不能导出,这个错误原因自然也需要后端传递给前端。错误信息此时很有可能被responseType设置成blob对象,在这种情况下需要转回JSON。我们可以用fileReader去读取blob。
具体代码如下:

      var reader = new FileReader();
      reader.readAsText(blob, 'utf-8'); //将blob读成text
      reader.onload = function () {
        // 读完之后的结果给JSON转化一下
        let data = JSON.parse(reader.result);
       // 于是就可以愉快地打印后端的报错信息啦
        message.error(data.apiMessage);
      };

2. 根据图片、pdf、excel的下载地址实现纯下载

如果后端传递过来的是可以直接用来下载的链接,有时候我们会将其绑在a标签上,然后赋予download属性,这样就可以下载了。
但是浏览器有时候会自作聪明地帮我们实现常见的图片、pdf等预览~这就非常蛋疼了。

这是因为浏览器会根据Content-type属性来判断是否支持,如果支持时会尝试去在页面上展示该资源。我们也不能要求后端将Content-type改成'application/octet-stream'之类的绕过浏览器的机制,还是自己动手实现吧。

思路也很简单:

  • 将拿到的response中的数据改成blob对象
  • 用createObjectUrl将其传给一个创建出来的a标签
  • a标签直接用代码触发点击

下面直接贴代码

function download(href, filename = '') {
  const a = document.createElement('a');
  a.download = filename;
  a.href = href;
  document.body.appendChild(a);
  a.click();
  a.remove();
}

function downloadFile(url, filename = '') {
  fetch(url, {
    headers: new Headers({
      Origin: window.location.origin,
    }),
    mode: 'cors',
  })
    .then((res) => res.blob())
    .then((blob) => {
      const blobUrl = window.URL.createObjectURL(blob);
      download(blobUrl, filename);
      window.URL.revokeObjectURL(blobUrl);
    });
}
@18888628835 18888628835 changed the title 如何处理后端传递过来的图片二进制流? 前端根据后端返回实现下载或者预览的几种方式 Dec 3, 2021
@gaoyajiang
Copy link

你好,如果后端返回来一个 URL 地址,里面存储了文本内容,那怎么获取 URL 内存储的内容呢?

@18888628835
Copy link
Owner Author

18888628835 commented Mar 1, 2022

你好,如果后端返回来一个 URL 地址,里面存储了文本内容,那怎么获取 URL 内存储的内容呢?

@gaoyajiang 你好,你的问题我并不是很明白,具体看你这个URL是用来做什么的。一般我们工作分几种情况:

  1. 后端返回文件流,可以用base64或者Blob实现下载
  2. 后端返回下载链接,你只需要直接将这个链接给a标签,并给一个download即可
  3. 后端返回文本数据,你从response内直接拿到文本数据然后处理即可,比如拿到文本然后给一个富文本编辑器渲染成HTML

不知道你是哪种情况。

@gaoyajiang
Copy link

你好,我是第二种情况,但是我不下载文件,就只是想获取到文件里的内容

现在的一个新的问题是,文件链接是七牛云的,后端配置了允许跨域,但是在项目中请求七牛云的链接还是 CORS 的问题,但是在postman 就可以请求成功

@18888628835
Copy link
Owner Author

@gaoyajiang 七牛云需要配置cors,项目中需要配置proxy代理。

@gaoyajiang
Copy link

项目中要怎么配置啊😭

@18888628835
Copy link
Owner Author

@gaoyajiang 18888628835 加我v,我帮你看看

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants