# 前端下载普通文件与二进制流文件
前端下载文件通常会遇到这两种情况
- 文件上传到资源服务器,后端只保存了文件地址,前端拿到后端返回的文件地址直接下载。
- 文件就存在后端服务器上(通常是临时根据前端参数动态生成,用完就删),后端读取文件后向前端返回文件的二进制流。
下面以下载 excel 文件为例,分别模拟这是这两种情况。
# 通过文件地址直接下载
新建一个项目,在项目中新建一个空的文件夹 service 模拟一个服务,在文件夹内新建一个 test.xlsx,然后在根目录上新建一个 index.html 模拟前端。
安装 serve 用来启动静态资源服务器。
yarn global add serve
进入 service 目录,启动服务
cd service
serve -s
此时在页面中放置一个 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", // 返回 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!");
});
启动该服务:
node service.js
返回项目根目录,我们引入 axios 来做 ajax 请求。
yarn add axios
我们修改 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。