新建
nest new nest-multer-upload-demo -p npm
npm install -D @types/multer
controller 更新
@Post('aaa')
@UseInterceptors(FileInterceptor('aaa', {
dest: 'uploads'
}))
uploadFile(@UploadedFile() file: Express.Multer.File, @Body() body) {
console.log('body', body);
console.log('file', file);
}
使用 FileInterceptor 来提取 aaa 字段,然后通过 UploadedFile 装饰器把它作为参数传入
让 nest 服务支持跨域,单独跑 http-server 来提供静态服务
新建 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" multiple/>
<script>
const fileInput = document.querySelector('#fileInput');
async function formData() {
const data = new FormData();
data.set('name','fuhao');
data.set('age', 20);
data.set('aaa', fileInput.files[0]);
const res = await axios.post('http://localhost:3000/aaa', data);
console.log(res);
}
fileInput.onchange = formData;
</script>
</body>
</html>
上传
文件夹更新了
其他字段通过 @Body 装饰器获取
如果是多文件 ?
@Post('bbb')
@UseInterceptors(FilesInterceptor('bbb', 2, {
dest: 'uploads'
}))
uploadFiles(@UploadedFiles() files: Array<Express.Multer.File>, @Body() body) {
console.log('body', body);
console.log('files', files);
async function formData2() {
const data = new FormData();
data.set('name','fuhao');
data.set('age', 20);
[...fileInput.files].forEach(item => {
data.append('bbb', item)
})
const res = await axios.post('http://localhost:3000/bbb', data, {
headers: { 'content-type': 'multipart/form-data' }
});
console.log(res);
}
fileInput.onchange = formData2;
可以指定 storage
const multer = require('multer')
const path = require('path')
export const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, 'uploads/')
},
filename(req, file, cb) {
// 原始文件名
const ext = path.extname(file.originalname)
const name = path.basename(file.originalname, ext)
// 自定义规则
const newName = `${name}-${Date.now()}${ext}`
cb(null, newName)
}
})
controller中
@Post('ddd')
@UseInterceptors(AnyFilesInterceptor({
storage: storage
}))
uploadAnyFiles(@UploadedFiles() files: Array<Express.Multer.File>, @Body() body) {
console.log('body', body);
console.log('files', files);
}
上传试试
Nest 上传文件的方式和直接使用 multer 很像,它就是对 multer 做了一层简单的封装
如果需要对文件大小、类型做校验,可以用pipe
nest g pipe file-validation-pipe --no-spec --flat
import { PipeTransform, Injectable, ArgumentMetadata, HttpException, HttpStatus } from '@nestjs/common';
@Injectable()
export class FileValidationPipe implements PipeTransform {
transform(value: Express.Multer.File, metadata: ArgumentMetadata) {
if(value.size > 20 * 1024) {
throw new HttpException('文件大于 20k', HttpStatus.BAD_REQUEST);
}
return value;
}
}
controller 使用
@Post('eee')
@UseInterceptors(FileInterceptor('aaa',{
dest: 'uploads'
}))
uploadAnyFiles2(@UploadedFile(FileValidationPipe) files: Array<Express.Multer.File>, @Body() body) {
console.log('body', body);
console.log('files', files);
}
index.html 中使用
async function formData() {
const data = new FormData();
data.set('name','fuhao');
data.set('age', 20);
data.set('aaa', fileInput.files[0]);
const res = await axios.post('http://localhost:3000/eee', data);
console.log(res);
}
fileInput.onchange = formData;
这种 Nest 内部 用封装好可以直接使用
@Post('fff')
@UseInterceptors(FileInterceptor('aaa', {
dest: 'uploads'
}))
uploadFile3(@UploadedFile(new ParseFilePipe({
validators: [
new MaxFileSizeValidator({ maxSize: 500 }),
new FileTypeValidator({ fileType: 'image/jpeg' }),
],
})) file: Express.Multer.File, @Body() body) {
console.log('body', body);
console.log('file', file);
}
自定义校验
import { FileValidator } from "@nestjs/common";
export class MyFileValidator extends FileValidator{
constructor(options) {
super(options);
}
isValid(file: Express.Multer.File): boolean | Promise<boolean> {
if(file.size > 50000) {
return false;
}
return true;
}
buildErrorMessage(file: Express.Multer.File): string {
return `文件 ${file.originalname} 大小超出 50k`;
}
}
nice!