前端下载普通文件与二进制流文件
前端下载文件通常会遇到这两种情况
- 文件上传到资源服务器,后端只保存了文件地址,前端拿到后端返回的文件地址直接下载。
- 文件就存在后端服务器上(通常是临时根据前端参数动态生成,用完就删),后端读取文件后向前端返回文件的二进制流。
下面以下载 excel 文件为例,分别模拟这是这两种情况。
通过文件地址直接下载
新建一个项目,在项目中新建一个空的文件夹 service 模拟一个服务,在文件夹内新建一个 test.xlsx,然后在根目录上新建一个 index.html 模拟前端。

安装 serve 用来启动静态资源服务器。
进入 service 目录,启动服务

此时在页面中放置一个 a 标签,并写上 download 属性,在浏览器中打开点击下载。

这种相当于一个 get 请求,浏览器直接访问该静态资源地址,download 属性告诉这个浏览器这个 a 标签不是打开页面预览而是直接下载。
这与通常在实际项目中通过 ajax 请求接口无关,只需要按照请求,因为后端返回的只是文件的地址,那到底之后绑定在 a 标签上或者通过 window.open()
都可以进行下载。
二进制流文件下载
这种情况一般就是在开发中通过 ajax 请求接口的方式,比如 post 请求,前端传递若干参数,后端返回一个二进制流。
关闭之前启动的静态服务,新建 service.js 文件,我们用 node 来写一个简单的服务。
const http = require("http"); const fs = require("fs");
const server = http.createServer((req, res) => { if (req.url === "/download") { res.writeHead(200, { "Content-type": "application/vnd.ms-excel", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "content-type", });
fs.readFile("test.xlsx", (err, data) => { res.end(data); }); } });
server.listen(3000, () => { console.log("Node.js server is running at port 3000!"); });
|
启动该服务:
返回项目根目录,我们引入 axios 来做 ajax 请求。
我们修改 index.html,移除 a 标签,替换为一个按钮,并为按钮增加点击事件:
<body> <button id="btn">下载</button> </body> <script src="node_modules/axios/dist/axios.min.js"></script> <script> const btn = document.getElementById("btn");
btn.onclick = function () { axios({ method: "post", url: "http://localhost:3000/download", data: { test: "test data", }, }).then((res) => { console.log(res.data); }); }; </script>
|
此时点击下载,可以看到打印出来是一堆乱码。

其实原理和上面直接通过文件地址访问是一样的,现在我们的目标便是将这些二进制数据转换成一个文件 url。
我们修改一下 axios的配置:
<body> <button id="btn">下载</button> </body> <script src="node_modules/axios/dist/axios.min.js"></script> <script> const btn = document.getElementById("btn");
btn.onclick = function () { axios({ method: "post", url: "http://localhost:3000/download", data: { test: "test data", }, responseType: 'blob' }).then((res) => { const url = window.URL.createObjectURL(res.data); const a = document.createElement("a"); a.href = url; a.download = "test.xlsx"; document.body.appendChild(a); a.click(); a.remove(); }); }; </script>
|
点击下载,成功转换成文件。
ps:这种转换有一个问题在于格式不一定会正确,因为是文件系统自动转换的,如果想要做到精确转换,则 responseType 使用 arraybuffer 格式,然后手动生成 blob。