Koa开发:入门、进阶与实战
上QQ阅读APP看书,第一时间看更新

2.7 文件上传

文件的上传和下载在实际应用场景中会经常遇到,2.6节介绍过如何获取post请求的数据,文件上传也是通过post请求实现的,那么可以直接通过ctx.request.body获取文件内容吗?答案是不可以,文件上传需要通过另外一个中间件koa-body来实现,文件下载可以通过koa-send中间件来实现。本节带领读者使用这两个中间件。

首先,文件上传需要先在前端实现一个上传文件的标签和点击按钮,然后利用FormData对象进行封装,最后通过XMLHttp-Request对象发送。具体页面代码如下。

<!-- static/upload.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>上传</title>
</head>
<body>
  <input type="file" />
  <button>点击上传</button>
</body>
<script>
  document.querySelector('button').onclick = function () {
  // 这里会获取一个files数组对象, 因为是单文件上传, 所以获取第一
     个即可
  let file = document.querySelector('input').files[0];
  let xhr = new XMLHttpRequest();
  xhr.open('post', '/upload', true);
  xhr.onload = function () {
    let res = JSON.parse(xhr.responseText);
    console.log(res);
  }

  let form = new FormData();
  form.append('file', file); // 对应key value
  xhr.send(form);
}

</script>
</html>

在页面上的呈现效果如图2-16所示。

0

图2-16 上传页面效果图

经过前面几节的学习,读者应该学会了静态服务器以及路由的使用,文件上传用到的中间件是koa-body,需要读者自行安装,其他的中间件都是之前介绍过的,不再赘述,下面是Koa上传文件的实现逻辑。

// app.js
const Koa = require('koa')
const path = require('path')
const fs = require('fs')
const static = require('koa-static')
const Router = require('koa-router')
const koaBody = require('koa-body');
const app = new Koa()
const router = new Router()

const staticPath = './static'

app.use(koaBody({
  multipart: true,
  formidable: {
    maxFileSize: 200*1024*1024 // 设置上传文件的限制, 默认2MB
  }
}));

app.use(static(
  path.join( __dirname,  staticPath)
))

app.use(router.routes())

router.post('/upload', async ( ctx ) => {
  // 获取文件对象
  const file = ctx.request.files.file
  // 读取文件内容
  const data = fs.readFileSync(file.path);
  // 保存到服务端
  fs.writeFileSync(path.join(__dirname, file.name), data);
  ctx.body = { message: '上传成功!' };
})

app.listen(4000, () => {
  console.log('server is running, port is 4000')
})

至此,简单的文件上传功能就实现了,读者可以自己运行一下。上传后的文件将保存在app.js同级目录下。

接下来实现下载的功能,对于前端来说,下载可以通过window.open来实现,代码如下。

<!-- download.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>下载</title>
</head>
<body>
  <button onclick="handleClick()">立即下载</button>
</body>
<script>
  const handleClick = () => {
    window.open('/download/1.png');
  }
</script>
</html>

假设在之前上传了图片1.png到服务端,现在要将其下载到客户端,在Koa中是如何实现的呢?下载需要安装一个中间件koa-send,那么在app.js文件中增加下载的逻辑,代码如下。

// app.js
const send = require('koa-send');

router.get('/download/:name', async (ctx) => {
  const name = ctx.params.name;
  const path = `${name}`;
  ctx.attachment(path);
  await send(ctx, path);
})

整体思路就是前端点击下载,调用Koa中对应的路由,将文件回传到浏览器。下载的效果如图2-17所示。

0

图2-17 文件下载效果