前端文件下载(二)

2023-09-19 10:45:10 浏览数 (1)

在上一篇文章 前端文件下载(一)中,我们介绍了如何进行「超链接文件」下载

本文,我们将通过案例,讲解如何将文件内容转成 Blob 下载。

Blod 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 用来操作数据。

本文因为已经将文件转为 Blob 了,这里可以忽略跨域请求。我们直接在同源下进行案例演示。(文末有给出理由~)

下面,我们新建一个简单的 SSR 应用。

案例的环境(Main) mac m1 node version - v14.18.1 Google Chrome: 版本 116.0.5845.187(正式版本) (arm64)

案例

我们依旧拿 test.txt.zip 文件作为演示案例文件:

代码语言:javascript复制
const Koa = require('koa');
const Router = require('koa-router');
const views = require('koa-views');
const path = require('path');
const fs = require('fs');

const app = new Koa();
const router = new Router();


// ejs template
app.use(
  views(path.join(__dirname, 'views'), {
    extension: 'ejs'
  })
);

router.get('/', async (ctx) => {
  await ctx.render('index'); // render
});

router.get('/download/file', async (ctx) => {
  const filePath = path.join(__dirname, 'public', 'test.txt.zip');

  ctx.attachment(filePath);
  ctx.type = 'application/octet-stream';
  ctx.body = fs.createReadStream(filePath); // create read stream
});

app.use(router.routes());

app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

我们在路由 / 中渲染了模版文件,然后在路由 /download/file 中,将文件 test.txt.zip 转为可读流返回。

渲染的模版内容如下:

代码语言:javascript复制
<!DOCTYPE html>
<html>
<head>
  <title>SSR Download File</title>
</head>
<body>
  <h1>Hello, Jimmy!</h1>
  <button id="download">Download File</button>
  <script>
    (function(){
      let downloadBtn = document.getElementById("download");
      downloadBtn.addEventListener('click', function(){
        fetch('/download/file')
          .then(response => response.blob())
          .then(blobData => {
            const downloadLink = document.createElement('a');
            downloadLink.href = URL.createObjectURL(blobData); // createObjectURL
            downloadLink.download = 'demo.txt.zip'; // should add, or filename extension not work
            downloadLink.click();
            URL.revokeObjectURL(downloadLink.href); // revoke
          })
      })
    })()
  </script>
</body>
</html>

在模版中,我们请求了接口 http://localhost:3000/download/file 返回的信息。然后 .then(response => response.blob()) 将响应的数据转换成为 Blob 对象。之后配合 createObjectURL 将数据对象转成一个 url,通过 a 标签进行下载。

为什么我们开篇说忽略跨域。因为 createObjectURL 转成的数据对象 url 是在当前域名下生成,这里是 http://localhost:3000/path/to,可以查看 downloadLink.href 的值。感兴趣读者可以在跨域下进行验证,比如: http://localhost:5500,生成的对象 url 将是 http://localhost:3000/path/to

触发下载按钮后,我们将看到下载过程自动启动,文件被下载下来。

总结

本文中,我们使用 Blob 和 createObjectURL,并集合了 fetch 进行文件的下载。它有以下的特点:

  • 不受同源策略的限制 - 同源和跨域都可
  • 需要设定 download 的名称,包含文件后缀,否则生成的文件没有后缀
  • 自动唤起浏览器的下载,下载进度由浏览器控制

0 人点赞