首页 > 其他分享 >Nestjs系列 文件上传(一)

Nestjs系列 文件上传(一)

时间:2024-03-11 17:25:48浏览次数:16  
标签:files 文件 系列 req Nestjs file const 上传

Express 中的文件上传

Nest 的文件上传是基于 Express 中间件 multer 实现的,所以需要先了解下 multer 包的使用。

npm init -y # 创建一个 package.json

npm install express multer cors  # 安装依赖包

cors 是处理跨域用的。

创建 index.js 文件,并写入内容

const express = require("express");
const multer = require("multer");
const cors = require("cors");

const app = express();
app.use(cors());

const upload = multer({ dest: "uploads/" });

app.post("/files", upload.single("myfiles"), function (req, res, next) {
  console.log("req.file", req.file);
  console.log("req.body", req.body);
});

app.listen(8888, () => {
  console.log("服务启动,地址: http://localhost:8888");
});

启动服务,运行 node index.js
新建 index.html,用于写前端的文件上传交互

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input id="fileControll" type="file" />
  </body>
  <script src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>

  <script>
    const fileInput = document.querySelector("#fileControll");
    fileInput.addEventListener("change", async (event) => {
      const file = event.target.files[0];
      const data = new FormData();
      data.append("myfiles", file);

      const res = await axios.post("http://localhost:8888/files", data);
      console.log(res);
    });
  </script>
</html>

此时点击文件上传,可以查看终端的打印和文件目录

image

多文件上传

新增一个路由,来实现多文件上传

image

更改前端的交互

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input id="fileControll" type="file" multiple />  // 标识为多选
  </body>
  <script src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>

  <script>
    const fileInput = document.querySelector("#fileControll");
    fileInput.addEventListener("change", async (event) => {
      const files = event.target.files;
      const data = new FormData();
      // files 是一个 FileList 伪数组,需转成数组
      Array.from(files).forEach((file) => data.append("multfiles", file));

      const res = await axios.post("http://localhost:8888/mult-files", data);
    });
  </script>
</html>

image

限制文件上传数量

image

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

多字段多文件上传

当存在多个字段都会上传文件,而且限制也不同时,可以采用 upload.fields([...])

app.post(
  "/mult-files-fields",
  upload.fields([
    { name: "a", maxCount: 3 },
    { name: "b", maxCount: 2 },
  ]),  // 这里配置成数组
  function (req, res, next) {
    console.log("req.files", req.files);
    console.log("req.body", req.body);
  },
  function (err, req, res, next) {
    if (err instanceof multer.MulterError && err.code === "LIMIT_UNEXPECTED_FILE") {
      res.status(400).end("超出文件最大上传数量");
    }
  }
);

image

通过 fields 方法指定每个字段的名字和最大数量,然后接收到请求后通过 req.files['a']req.files['b'] 这种格式来取对应的文件信息

不确定的文件字段名

当不确定,或者不限制文件的上传名时,可以使用 upload.any()

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

image

此时的 files 就不是以 key value 格式传输的,而是把字段名添加到每一项的 fieldname 中,需要自行取值

更改上传文件的文件名

express 内置存储的文件名,仅仅是一串随机 uuid,如果想让文件变得更有规律和可读性,可以自定义文件名。

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

const upload = multer({ storage })

此时的文件名就不能使用 dest 直接指定了。multer.distkStorage 是磁盘存储,通过 destinationfilename 的参数分别指定保存的目录和文件名。

image

两个文件是不同的字段名,分别为 a b,后边是时间戳+随机数,最后面就是文件的源文件名。

Nest 中的文件上传

在了解完 express 上传之后,就可以使用 Nest 来进行使用了,因为 Nest 本身支持的就是 express 的文件上传,若要使用 fastify 的文件上传需进行封装,是社区实现的。

安装 multer 的类型包

pnpm i @types/multer -D

访问静态资源

pnpm i @nestjs/serve-static
// app.module.ts
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'client'),  // 标识静态资源在 client 文件夹下
    }),
    // ...
  ],
  controllers: [AppController],
  providers: [AppService],
})
// client/index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <input type="file" id="fileControll" />
  </body>
  <script src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>
  <script>
    const fileInput = document.querySelector('#fileControll');

    fileInput.addEventListener('change', async (event) => {
      const file = event.target.files[0];
      const data = new FormData();
      data.append('filename', file);

      const res = await axios.post('/person/upload', data);
      console.log(res);
    });
  </script>
</html>

单文件上传

在 controller 中进行文件接收,字段名和接口名与前端保持一致

  @Post('upload')
  @UseInterceptors(FileInterceptor('filename', { dest: 'uploads' }))
  uploadFile(@UploadedFile() file: Express.Multer.File, @Body() body) {
    return { file, body };
  }

此时上传文件,前端拿到的结果就是

image

后台的指定文件夹中也新增了一个文件

image

多文件上传和限制上传数量

FileInterceptor 换成 FilesInterceptor,区别就是多加了个 s,此时第二个参数是上传的数量限制,第三个参数才和 FileInterceptor 一样是指定的上传目录。
同时,controller 入参的装饰器 @UploadedFile() 要改为 @UploadedFiles(),也是加个 s

  @Post('upload')
  @UseInterceptors(FilesInterceptor('filename', 3, { dest: 'uploads' }))
  uploadFile(@UploadedFiles() file: Express.Multer.File[], @Body() body) {
    return { file, body };
  }

image

以上是文件上传了两个,在 controller 中,制定了文件最大的上传数量 3,此时若上传四个

image

直接就是请求报错。

多文件多字段上传

和 express 中是类似的,不过要把 controller 上的装饰器 @FilesInterceptor 换成 @FileFieldsInterceptor

  @Post('upload')
  @UseInterceptors(
    FileFieldsInterceptor(
      [
        { name: 'a', maxCount: 1 },
        { name: 'b', maxCount: 2 },
      ],
      { dest: 'uploads' },
    ),
  )
  uploadFile(@UploadedFiles() files: Express.Multer.File[], @Body() body) {
    return { files, body };
  }

后台接收到的 files 就是这样子的

image

和 express 完全一样

不确定的文件字段名

当可以使用随机字段名上传文件时,可以使用 AnyFilesInterceptor

  @Post('upload')
  @UseInterceptors(AnyFilesInterceptor({ dest: 'uploads' }))
  uploadFile(@UploadedFiles() files: Express.Multer.File[], @Body() body) {
    return { files, body };
  }

此时,任意的文件字段名,都会被 Nest 承认。

更改上传文件的文件名

Nest 也同样可以在上传时更改文件的存储文件名。

// storage.ts
import * as multer from 'multer';
import fs from 'fs';
import path from '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);
  },
});

export { storage };

在 controller 中使用

image

限制文件大小和类型(ParseFilePipe)

Nest 内置的管道 ParseFilePipe 就是专门做文件的校验的

image

MaxFileSizeValidator 是校验文件大小,FileTypeValidator 是校验文件类型。

既然是 Pipe,当然也提供了自定义错误和状态码的能力

image

此外,FileTypeValidator 还支持正则匹配:new FileTypeValidator( { fileType : /^image\/(jpg|jpeg|png|gif)$/ } )

自定义文件上传的校验

只要继承 FileValidator ,就可以实现自定义的 validator

import { FileValidator } from '@nestjs/common';

export class MyFileSizeValidator extends FileValidator {
  constructor(options) {
    super(options);
  }

  isValid(file: Express.Multer.File): boolean | Promise<boolean> {
    if (file.size > 1000000) {
      return false;
    }
    return true;
  }
  buildErrorMessage(file: Express.Multer.File): string {
    return `文件 ${file.originalname} 大小超出 1000k`;
  }
}

image

标签:files,文件,系列,req,Nestjs,file,const,上传
From: https://www.cnblogs.com/jsonq/p/18065735

相关文章

  • django中的上传文件
    用django写接口的时候,不可避免的会涉及到上传文件环境python版本django版本djangorestframework版本drf-spectacular版本3.10.43.23.14.00.27.1编写模型fromdjango.dbimportmodelsclassTimeMixin(models.Model):"""时间混入类,为模型添加创......
  • [Open3d系列]--点云曲线拟合
    Open3d:点云曲线拟合因为项目需要分析点云数据,此文总结其中拟合点云的部分。拟合首先定一个曲线方程:deffunc(x,a,b,c):returna*x**2+b*x+c然后将点云数据结构转换为numpy数组:points=np.asarray(pcd.points)读取点数组中,x轴、y轴的数组:xy_points......
  • 代码实现上传Base64图片到七牛云OSS
    依赖<!--https://mvnrepository.com/artifact/com.qiniu/qiniu-java-sdk--><dependency> <groupId>com.qiniu</groupId> <artifactId>qiniu-java-sdk</artifactId> <version>7.14.0</version></dependency><......
  • 解决uni-app在App端上传图片时路径转Base64的问题
    解决uni-app在App端上传图片时路径转Base64的问题在用uni-app开发项目的时候大家都会遇到这么一个问题,就是上传图片时在App上拿到的是文件路径,然而后端要接收的却是Base64字符串,这就尴尬了,在App端又无法调用WebApi(例如:BlobfileReader等),自己写插件的话又很麻烦,因此我找了很久才......
  • 多线程系列(十六) -常用并发原子类详解
    一、简介在Java的java.util.concurrent包中,除了提供底层锁、并发同步等工具类以外,还提供了一组原子操作类,大多以Atomic开头,他们位于java.util.concurrent.atomic包下。所谓原子类操作,顾名思义,就是这个操作要么全部执行成功,要么全部执行失败,是保证并发编程安全的重要一环。相......
  • Logstash系列---【centos7离线安装logstash7.8.0】
    1.安装包下载地址一般根据es的版本来确定logstash的版本,一般保持一致即可。Logstash和es版本对应关系:https://www.elastic.co/cn/support/matrix#matrix_compatibility。Logstash下载地址:https://www.elastic.co/cn/downloads/past-releases/logstash-7-8-02.解压并复制配置......
  • Nestjs系列 Nestjs基础(四)
    Nest中的middlewaremiddware基础用法已经在Nest的AOP架构章节中存在。此次记录middleware的更详细用法新建项目,然后创建一个middleware模板nestgmiddlewaretest--no-spec--flat可以看到此时的req和res都是any,可以对其进行明确的类型标注,express就从......
  • 解密prompt系列26. 人类思考vs模型思考:抽象和发散思维
    在ChainofThought出来后,出现过许多的优化方案例如Treeofthought,GraphofThought,AlgorithmofThought等等,不过这些优化的出发点都更加"MachineLike",而非"HumanLike",哈哈不是说机器化不好,仅仅是对AGI的一些个人偏好而已。所以如果我们从人类思考的角度出发,能否把当......
  • Go Gin框架实现上传100G超大文件
    GoGin框架实现上传100G超大文件原创 云原生Go 源自开发者 2024-02-0718:11 广东 1人听过源自开发者专注于提供关于Go语言的实用教程、案例分析、最新趋势,以及云原生技术的深度解析和实践经验分享。135篇原创内容公众号在本文中,我们将深入探讨如......
  • 国产光刻机系列产品
    SSX600系列光刻机 SSX600系列步进扫描投影光刻机采用四倍缩小倍率的投影物镜、工艺自适应调焦调平技术,以及高速高精的自减振六自由度工件台掩模台技术,可满足IC前道制造90nm、110nm、280nm关键层和非关键层的光刻工艺需求。该设备可用于8寸线或12寸线的大规模工业生产。SSB50......