牛叔叔 的笔记

好好学习

2023-05-11 10:21

前后端分离项目中如何实现文件下载功能

牛叔叔

WEB前端

(2406)

(0)

收藏

在前后端分离的架构中,前端和后端交互通过HTTP API接口实现。对于文件下载功能,通常有两种常见的方式可以实现:


  1. 后端生成文件并返回URL: 后端可以根据请求参数生成需要下载的文件,并将文件保存到服务器的某个目录下。随后,后端将生成的文件路径或者访问地址等信息以JSON格式返回给前端。最后,前端使用类似a标签的下载组件发送GET请求到后端所返回的URL即可。


  2. 返回文件流数据:另一种方式是,后端处理请求后直接将文件的字节流发送给前端。一般来说,我们会通过response对象提供的OutputStream流将文件内容发送给前端。前端则可以封装一个基于xhr或fetch的异步请求功能函数,使用JavaScript读取后端发送过来的二进制数据,传递给Blob对象, 然后再通过new URL().createObjectURL()方法创建出Blob对象的网络请求地址,从而实现文件下载操作。



第1种方法无需多说,我们看第2种方法具体如何实现。


以下是一个基于Java Spring Boot框架的后端代码示例,用于返回文件流数据:

@GetMapping("/downloadFile")
public ResponseEntity<byte[]> downloadFile()throws IOException{
    File file = new File("your_file_path");
    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Disposition", "attachment;filename=" + file.getName());
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);
}


此代码中,downloadFile() 方法在接收到请求后,将服务器上指定路径下的文件读入内存,并以字节流的方式返回给前端。

在编写前端代码时,可以使用XMLHttpRequest(XHR)或Fetch API等来发出异步网络请求,并处理返回的文件流数据。下面是一个使用XHR实现的简单示例,你可以根据自己的需求进行调整和优化:

function downloadFile(){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/downloadFile', true);
    xhr.responseType = "blob";
    xhr.onload = function(e) {
        if (this.status == 200) {
            var blob = new Blob([this.response], {type: xhr.getResponseHeader('Content-Type')});
            var fileName = "";
            var contentDisposition = xhr.getResponseHeader('Content-Disposition');
            if(contentDisposition != null && contentDisposition.indexOf("=") > 0) {
                fileName = contentDisposition.substring(contentDisposition.indexOf("=") + 1);
            }
            var link = document.createElement("a");
            link.href = window.URL.createObjectURL(blob);
            link.download = decodeURIComponent(fileName.trim());
            link.click();
        }
    };
    xhr.send();
}

该函数将设定一个XHR对象,发出GET请求,responseType 设置为 blob, 并解析响应头信息中的文件名(fileName)和 Content-Type。在成功接收到服务器返回数据后,使用Blob对象创建通过URL生成下载地址的超链接,并模拟点击操作来下载文件。


使用 axios 实现文件下载,可以像下面这样编写:

import axios from 'axios';

function downloadFile() {
  // 发出GET请求,responseType设置为 'blob',并指定需要下载的文件路径
  axios({
    method: 'get',
    url: '/downloadFile',   // 这里填写文件下载接口地址
    responseType: 'blob'   // 响应类型为 blob
  }).then(res => {
    const contentDisposition = res.headers['content-disposition'];  // 获取响应头中的文件信息
    let fileName = 'wanmait.com.txt';  // 文件名
    if (contentDisposition) {
      const matchArray = contentDisposition.match(/filename=(.*?)(;|$)/); // 通过正则表达式解析出文件名
      if (matchArray && matchArray.length > 1) {
        fileName = decodeURIComponent(matchArray[1]);
      }
    }
    // 接收到二进制流后创建URL对象,并模拟点击下载操作
    const blob = new Blob([res.data], { type: res.headers['content-type'] });
    const objectUrl = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = objectUrl;
    a.download = fileName;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(objectUrl);
  }).catch(error => {
    console.error(error);
  });
}


在这个示例中,我们使用了axios发出一个GET请求,将响应的数据类型设置为blob。 在成功获取到响应后,通过xhr对象提供的response获取到响应的二进制流数据,并同步获取文件名和文件类型等信息,在前端通过blob对象和URL.createObjectURL()方法将响应内容转换为url地址,并在创建一个a标签模拟点击下载操作。


在前端中,为了提供方便的文件下载功能,我们通常采用的是基于浏览器默认行为的<a>标签实现,它可以直接传递指定 URL 规则作为href,通过 href 属性和html5中新增的download 属性来控制文件名下载等相关特性。


但是,如果以模拟点击的方式使用 JavaScript 的下载操作,就能完成异步获取文件数据后的下载需求。如:在创建一个a标签,设置a.href 和 a.download 表示要下载哪个文件,将其附加到DOM中,并通过调用a.click()方法来出发默认行为,从而实现下载文件的效果。

需要注意的是,在模拟点击中需要注意对对象URL的及时回收三必要性,避免因悬挂对象URL导致的性能问题或者对内存资源的浪费。


0条评论

点击登录参与评论