Nest使用multer实现文件上传,并实现大文件分片上传(上)

Nest 的文件上传是基于 Express 的中间件 multer 实现的,所以在学习 Nest 文件上传之前,我们先学习下 multer 包的使用。

一,Express如何使用multer实现文件上传

Nest 的文件上传是基于 Express 的中间件 multer 实现的,所以在学习 Nest 文件上传之前,我们先学习下 multer 包的使用。

新建目录,npm init -y 创建 package.json

 然后安装 express 和 multer 还有 cors 包:

npm install express multer cors

这个 cors 包是处理跨域 header 的。

1,单文件上传

创建index.js文件

const express = require('express')
const multer = require('multer')
const cors = require('cors')

const app = express()
// 使用中间件 cors 来处理跨域
app.use(cors())
// 用 multer 处理文件上传,指定保存目录为 uploads/。
const upload = multer({dest:'uploads/'})

app.post('/uploadfile',upload.single('file'),function (req,res,nex) {
  console.log('req.file', req.file);
  console.log('req.body', req.body);
})

app.listen(3333);

创建index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script>
</head>

<body>
  <input id="fileInput" type="file" />
  <script>
    const fileInput = document.querySelector('#fileInput');

    async function formData() {
      const data = new FormData();
      data.set('name', '张三');
      data.set('age', 24);
      data.set('file', fileInput.files[0]);

      const res = await axios.post('http://localhost:3333/uploadfile', data);
      console.log(res);
    }

    fileInput.onchange = formData;
  </script>
</body>

</html>

通过 FormData + axios 上传文件,指定内容的传输格式 content-type 为 multipart/form-data。(这里 axios 会自动根据内容指定 content-type,不需要手动指定)

 然后用 node 把 server 跑起来,并且用 http-server 把静态服务跑起来:

 浏览器访问下:

选择文件上传, 这时候在 devtools 可以看到 aaa 请求的 body 是多个 boundary 分隔的格式:

而分隔符是在 Content-Type 指定的:

这就是 form-data 的传输格式。

然后去服务端看看:

req.file 可以拿到文件字段,其余非文件字段在 req.body。

并且服务端多了 uploads 目录,下面就保存着我们上传的文件:

2,多文件上传

单文件上传我们会了,现在我们来添加一个路由实现多文件上传

// 多文件上传
app.post('/uploadfiles',upload.array('files',2),function (req,res,nex) {
  console.log('req.file', req.files);
  console.log('req.body', req.body);
})
//uploadfiles路由通过 array 方法来取上传的文件,并且指定最大数量的限制。
//上传的文件通过 req.files 来取

前端这边我们需要这样来写:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script>
</head>

<body>
  <input id="fileInput" type="file" multiple/>
  <script>
    const fileInput = document.querySelector('#fileInput');

    async function formDatas() {
      const data = new FormData();
      data.set('name', '张三');
      data.set('age', 24);
      [...fileInput.files].forEach(item => {
        data.append('files',item)
      })

      const res = await axios.post('http://localhost:3333/uploadfiles', data);
      console.log(res);
    }

    fileInput.onchange = formDatas;
  </script>
</body>

</html>

input 标签添加 multiple 属性允许多选。

onchange 的时候取出每个 file,通过 append 方法添加到 files字段。(这里fileInput.files 是一个伪数组,要转成数组才能用 forEach 方法)

这样 files实际上就保存着上传的多个文件了。

接下来我们在页面上传两个文件:

 node 服务的 req.files 接收到了多个文件:

并且 uploads 目录下也多了俩文件:

这就是多文件上传。

3,错误处理中间件

如果传了超过 2 个文件呢?这时候就会报错,我们添加一个错误处理中间件:

// 多文件上传
app.post('/uploadfiles', upload.array('files', 2), function (req, res, nex) {
  console.log('req.file', req.files);
  console.log('req.body', req.body);
}, function (err, req, res, next) {
  console.log('err', err)
})

在 express 里,约定有 4 个参数的中间件为错误处理中间件。

一旦某个中间件出了错,express 就会向后找错误处理中间件来调用,如果没有,那就用默认错误处理中间件,返回 500 响应。

接下来我们多传几个文件:

就可以看到服务端打印的报错了:

 这样我们只要返回对应的响应就好了:

// 多文件上传
app.post('/uploadfiles', upload.array('files', 2), function (req, res, nex) {
  console.log('req.file', req.files);
  console.log('req.body', req.body);
}, function (err, req, res, next) {
  if(err instanceof MulterError && err.code === 'LIMIT_UNEXPECTED_FILE') {
    res.status(400).end('Too many files uploaded!')
  }
})

这样再次上传超过 2 个文件,就会收到服务端的 400 的响应,提示文件上传过多:

 更复杂一点的情况,如果多个字段都会上传文件,而且限制也都不同呢?

如果是多个字段都上传文件的话,那可以这样处理:

// 多个字段都上传文件
app.post('/uploadfiles', upload.fields([
  { name: 'file1', maxCount: 3 },
  { name: 'file2', maxCount: 2 }
]), function (req, res, next) {
  console.log('req.files', req.files);
  console.log('req.body', req.body);
})

 通过 fields 方法指定每个字段的名字和最大数量,然后接收到请求后通过 req.files['xxx'] 来取对应的文件信息。其他非文件字段,同样是通过 req.body 来取。

前端代码:

async function formDatas() {
      const data = new FormData();
      data.set('name', '张三');
      data.set('age', 24);
      data.append('file1', fileInput.files[0]);
      data.append('file2', fileInput.files[1]);
      data.append('file1', fileInput.files[2]);
      data.append('file2', fileInput.files[3]);

      const res = await axios.post('http://localhost:3333/uploadfiles', data);
      console.log(res);
    }

这里本来要写两个 file input 分别上传 file1、file2的文件,这里为了测试方便简化了下。

我们在页面测试一下:

然后可以在文件夹下看到四个文件了

服务端也接收到了file1和file2这两文件

此外,如果我们并不知道有哪些字段是 file 的时候,可以用 any 来接收:

app.post('/uploadfiles', upload.any(), function (req, res, next) {
  console.log('req.files', req.files);
  console.log('req.body', req.body);
})

改下前端代码,这次设置 file1、file2、file3、file4 4 个 file 字段:

async function formDatas() {
      const data = new FormData();
      data.set('name', '张三');
      data.set('age', 24);
      data.append('file1', fileInput.files[0]);
      data.append('file2', fileInput.files[1]);
      data.append('file3', fileInput.files[2]);
      data.append('file4', fileInput.files[3]);

      const res = await axios.post('http://localhost:3333/uploadfiles', data);
      console.log(res);
    }

再次上传 4 个文件,可以看到服务端接收到了:

只不过这时候不是 key、value 的形式了,需要自己遍历数组来查找。

4,修改保存的文件名

之前是通过 dest 指定了保存的目录:

现在可以这样写:

const fs = require('fs');
const path = require('path');

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        try {
            fs.mkdirSync(path.join(process.cwd(), 'my-uploads'));
        }catch(e) {}
        cb(null, path.join(process.cwd(), 'my-uploads'))
    },
    filename: function (req, file, cb) {
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9) + '-' + file.originalname
        cb(null, file.fieldname + '-' + uniqueSuffix)
    }
});

自己指定怎么存储,multer.distkStorage 是磁盘存储,通过 destination、filename 的参数分别指定保存的目录和文件名。这里要先创建下用到的目录,然后再返回它的路径。我们用时间戳 Date.now() 加上Math.random() 乘以 10 的 9 次方,然后取整,之后加上原来的文件名,然后浏览器再次上传,就可以看到目录和文件名都修改了

下一章,我们一起来学习在Nest中使用multer实现文件上传和大文件切片上传。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

safe030

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值