🔥利用koa实现前后端文件下载[完整前后端代码]

前言

文件下载功能是一个常用的功能,一个项目中或多或少都会用到,那么这么一个常用的功能,前后端代码如何实现呢?

为什么我们不通过url的方式直接下载?

如果我们使用a标签直接下载会有哪些问题?

  1. 跨域下载问题:如果要下载的文件与当前页面不在同一个域下,浏览器可能会阻止跨域下载。这是因为浏览器实施了同源策略,限制跨域资源的访问。
  2. 兼容性问题:使用<a>标签直接下载文件可能在不同浏览器之间存在兼容性问题。不同浏览器对文件下载的实现方式和行为可能有所不同。有的浏览器可能就是直接打开文件

上面的问题,有什么解决办法?

  1. 针对第一种问题,如果是非同源url,可以使用blob: URLs 和 data: URLs个(不建议使用base64这种方式,因为如果文件体积大的话,那么base64转化后会变为原体积的4/3,所以base64只适合小文件或者小文本)
  2. 第二种问题可以使用<a>标签模拟点击下载,这可以在一定程度上抹平浏览器差异,因为这种方法触发了浏览器的默认行为

不通过url的方式接受,那么我们就通过二进制的方式接受文件

后端是采用buffer还是stream呢?

其实两种都可以

fs和buffer,stream的关系是什么?

从Buffer到Stream再到fs模块,对数据处理的粒度是越来越大的。而Node.js在fs模块的实现上确实也是继承了上述两个模块的api来实现的。

  • 继承Stream的文件流,对外暴露fs.createReadStream与fs.createWriteStream

  • 对文件的同步与异步操作

    • fs.readFileSync与fs.writeFileSync
    • fs.readFile与fs.writeFile
  • 以上两种fs处理文件的形式,区别在于第一种的处理会将文件处理为流的形式。第二种会将文件视作一个整体,统一为整个文件分配内存大小,并将内容放入到一个大的缓冲区中。

前端注意设置responseType

在前端发起请求时,可以通过设置responseType(注意分清content-type和responseType,content-type是表示请求的中的实体主体的媒体类型,会显示在请求头中。 responseType是指定服务器返回数据的类型,是请求配置信息)属性来指定服务器返回数据的类型,以便浏览器正确解析响应内容。responseType属性可以设置为以下几种值:

  1. "" 或 "text":默认值,将响应数据解析为字符串。
  2. "arraybuffer":将响应数据解析为ArrayBuffer对象。适用于处理二进制数据,例如图片或音频。
  3. "blob":将响应数据解析为Blob对象。适用于处理二进制数据,例如文件下载。
  4. "document":将响应数据解析为Document对象。适用于处理XML或HTML响应。
  5. "json":将响应数据解析为JSON对象。适用于处理JSON格式的数据。

通过设置responseType属性,可以告诉浏览器如何解析响应数据,以便后续对数据进行处理。例如,如果需要下载图片文件,可以将responseType设置为"blob",将响应数据解析为Blob对象,然后可以通过创建URL或者使用FileReader等进行进一步处理。

需要注意的是,设置responseType属性并不会自动转换响应数据的格式,仅仅是告诉浏览器如何解析响应内容。如果服务器返回的数据格式与设置的responseType不一致,可能会导致解析错误或数据丢失。因此,需要根据服务器返回的数据类型来正确设置responseType属性。

所以:

    1. 如果我们使用stream传输的话,可以设置responseType为blob
    1. 如果我们使用buffer传输的话,可以设置responseType为arraybuffer

把上面的概念都理清了以后,那么代码就好写了

我们先开始使用stream传输

前端代码

js 复制代码
//返回二进制数据或文件流下载
const downLoadFile = async () => {
   const res = await axios({
    url: "http://localhost:9001/api/download/file",
    method: "get",
    responseType: "blob", // 返回的就是blob格式
  });
  // 获取blob
  let blob = res.data;
  let fileName = "12.jpeg";
  //
  const url = URL.createObjectURL(blob);
  // 创建一个隐藏的链接,并设置其下载属性和链接地址
  const link = document.createElement("a");
  link.style.display = "none";
  link.href = url;
  link.download = fileName;

  // 将链接添加到页面上,并触发点击事件进行下载
  document.body.appendChild(link);
  link.click();

  // 下载完成后,清除链接和释放URL对象
  document.body.removeChild(link);
  URL.revokeObjectURL(url);
};

后端代码

js 复制代码
router.get('/', (ctx: any) => {
  const filename = '1.jpeg';
  const filePath = path.resolve(__dirname, `../../public/upload/${filename}`);
  const stream = fs.createReadStream(filePath);
  ctx.body = stream;
});

使用buffer传输

前端代码

ini 复制代码
const res = await axios({
    url: "http://localhost:9001/api/download/file",
    method: "get",
    responseType: "arraybuffer",
  });
  // 获取buffer
  let blob = new Blob([res.data]);
  let fileName = "12.jpeg";
  //
  const url = URL.createObjectURL(blob);
  // 创建一个隐藏的链接,并设置其下载属性和链接地址
  const link = document.createElement("a");
  link.style.display = "none";
  link.href = url;
  link.download = fileName;

  // 将链接添加到页面上,并触发点击事件进行下载
  document.body.appendChild(link);
  link.click();

  // 下载完成后,清除链接和释放URL对象
  document.body.removeChild(link);
  URL.revokeObjectURL(url);

后端代码

js 复制代码
router.get('/', (ctx: any) => {
  const filename = '1.jpeg';
  const filePath = path.resolve(__dirname, `../../public/upload/${filename}`);
  const file = fs.readFileSync(filePath);
  ctx.body = file;
});

总结

  1. 上面我们讲了我们为什么不使用url的方式下载,讲了使用url下载的问题,以及针对这些问题我们应该如何去解决
  2. 讲解了后端采用buffer和stream两种方式获取文件二进制信息
  3. 讲了针对后端采用不同的方式,前端应该如何去接收

参考

相关推荐
斯晨的AI笔记3 小时前
在服务器上跑 npm ci 四十分钟没装完,我打包把 node_modules 扔了过去
服务器·node.js
bmxy小明同学5 小时前
2026-10-11-幻觉治理
服务器·前端·javascript
m0_587383005 小时前
无人自助拼豆系统落地开发实战指南 核心逻辑实现与部署经验分享
前端·javascript·vue.js·人工智能·经验分享·需求分析
zhaiyy5 小时前
HTML Popover API 实践
前端·javascript·html
cpolar技术支持5 小时前
第三方平台回调总说收不到?把本机 Webhook 收单页跑通,用 cpolar 固定二级子域名给对面看实时请求
node.js·内网穿透·webhook·cpolar·回调对接
OpsEye6 小时前
企业 AI 管理新需求:不仅要接入模型,还要看懂每一次调用
javascript·ai编程
牧艺6 小时前
SPA 也能像原生 App:View Transitions API 从原理到可上线实战
前端·javascript·css
Dovis(誓平步青云)6 小时前
现场问题先关闭还是先验收,建筑任务怎样处理依赖
前端·javascript·数据库·mysql·音视频
用户84298142418106 小时前
压缩 ≠ 加密:前端代码保护的三层实践与踩坑记录
前端·javascript
怕浪猫6 小时前
12个UI组件库,Star最低的下载最多
前端·javascript·面试