likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 46

前言

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

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

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

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

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

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

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

后端是采用buffer还是stream呢?

其实两种都可以

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

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

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

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

    • fs.readFileSyncfs.writeFileSync
    • fs.readFilefs.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传输的话,可以设置responseTypeblob
    1. 如果我们使用buffer传输的话,可以设置responseTypearraybuffer

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

我们先开始使用stream传输

前端代码

//返回二进制数据或文件流下载
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);
};

后端代码

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传输

前端代码

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);

后端代码

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. 讲了针对后端采用不同的方式,前端应该如何去接收

参考

转载自:https://juejin.cn/post/7291496138191061050
评论
请登录