使用 nestjs 和 multer 上传文件
Upload file using nestjs and multer
由于 nestjs 是一个 express 应用程序,因此可以使用任何库来处理使用 nest 的上传,并且由于它提供了 Midlewares,因此也可以使用 multer。我的问题是:使用 nestjs 处理文件上传的最佳方式是什么?
正如@Kamyl 在问题 https://github.com/nestjs/nest/issues/262 上所告知的那样,因为 v4.6.0
可以使用通用文件拦截器使用 multer 将文件上传到 nestjs。
import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
...
@UseInterceptors(FileInterceptor('file'))
async upload( @UploadedFile() file) {
console.log(file)
}
这样变量 file
就会有一个 buffer
使用 Multer 选项
还需要字段名作为第一个参数,然后是一个带有 Multer Options 的数组
import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
import { diskStorage } from 'multer'
import { extname } from 'path'
...
@UseInterceptors(FileInterceptor('file', {
storage: diskStorage({
destination: './uploads'
, filename: (req, file, cb) => {
// Generating a 32 random chars long string
const randomName = Array(32).fill(null).map(() => (Math.round(Math.random() * 16)).toString(16)).join('')
//Calling the callback passing the random name generated with the original extension name
cb(null, `${randomName}${extname(file.originalname)}`)
}
})
}))
async upload( @UploadedFile() file) {
console.log(file)
}
这样变量 file
将具有 filename
、destination
和 path
。
diskStorage
中的 destination
参数也可以是一个函数,其参数和期望的回调与 filename
相同。通过传递 diskStorage
文件将自动保存到指定文件名的目的地。
也可以使用 @UploadedFiles
和 FilesInterceptor
(复数)处理多个文件
更简洁的方法是将配置提取到单独的文件中,然后在拦截器方法中调用它
import { extname } from 'path';
import { existsSync, mkdirSync } from 'fs';
import { diskStorage } from 'multer';
import { v4 as uuid } from 'uuid';
import { HttpException, HttpStatus } from '@nestjs/common';
// Multer configuration
export const multerConfig = {
dest: process.env.UPLOAD_LOCATION,
};
// Multer upload options
export const multerOptions = {
// Enable file size limits
limits: {
fileSize: +process.env.MAX_FILE_SIZE,
},
// Check the mimetypes to allow for upload
fileFilter: (req: any, file: any, cb: any) => {
if (file.mimetype.match(/\/(jpg|jpeg|png|gif)$/)) {
// Allow storage of file
cb(null, true);
} else {
// Reject file
cb(new HttpException(`Unsupported file type ${extname(file.originalname)}`, HttpStatus.BAD_REQUEST), false);
}
},
// Storage properties
storage: diskStorage({
// Destination storage path details
destination: (req: any, file: any, cb: any) => {
const uploadPath = multerConfig.dest;
// Create folder if doesn't exist
if (!existsSync(uploadPath)) {
mkdirSync(uploadPath);
}
cb(null, uploadPath);
},
// File modification details
filename: (req: any, file: any, cb: any) => {
// Calling the callback passing the random name generated with the original extension name
cb(null, `${uuid()}${extname(file.originalname)}`);
},
}),
};
然后像这样在拦截器下调用
import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
import { diskStorage } from 'multer'
import { extname } from 'path'
import { multerOptions } from 'src/config/multer.config';
...
@Post('/action/upload')
@UseInterceptors(FileInterceptor('file', multerOptions))
async upload( @UploadedFile() file) {
console.log(file)
}
一个简单的方法是使用控制器。您需要定义一个上传控制器并将其添加到您的 app.module,这是一个控制器应该是什么的示例(后端):
@Controller()
export class Uploader {
@Post('sampleName')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file) {
// file name selection
const path = `desired path`;
const writeStream = fs.createWriteStream(path);
writeStream.write(file.buffer);
writeStream.end();
return {
result: [res],
};
}
}
并在前端通过 fetch 调用您的控制器:
fetch('controller address', {
method: 'POST',
body: data,
})
.then((response) => response.json())
.then((success) => {
// What to do when succeed
});
})
.catch((error) => console.log('Error in uploading file: ', error));
使用 Multer 选项的最简洁的实现
感谢@VictorIvens 提供的最佳答案。
但是,我在代码中发现了以下问题。
- 在最新版本的 NestJS 中
@nestjs/common
包中不存在名为 FileInterceptor
的导入。
- 代码在我看来有点太混乱了。
所以,为了简化事情,我提出了以下解决方案。
storage.config.ts
export const storage = diskStorage({
destination: "./uploads",
filename: (req, file, callback) => {
callback(null, generateFilename(file));
}
});
function generateFilename(file) {
return `${Date.now()}.${extname(file.originalname)}`;
}
你的-controller.controller.ts
import {
Controller,
Post,
UseInterceptors,
UploadedFile
} from "@nestjs/common";
import { FileInterceptor } from "@nestjs/platform-express";
import { diskStorage } from "multer";
import { extname } from "path";
import { storage } from "./storage.config"
@Controller()
export class YourController {
@Post("upload") // API path
@UseInterceptors(
FileInterceptor(
"file", // name of the field being passed
{ storage }
)
)
async upload(@UploadedFile() file) {
return file;
}
}
如果您通过 API 调用从用户那里获取数据,您可以将数据保存为缓冲区并使用 adm-zip 访问内容。下面是 nest.js.
中的控制器方法实现
@Post("/blackBoardUpload")
@UseInterceptors(
FileInterceptor('image', {
storage: memoryStorage(),
fileFilter: zipFileFilter,
}),
)
async uploadedFile(@UploadedFile() file) {
console.log(file)
const response = {
originalname: file.originalname,
filename: file.filename,
};
var AdmZip = require('adm-zip');
var zip = new AdmZip(file.buffer);
var zipEntries = zip.getEntries();
console.log(zipEntries.length);
return {
status: HttpStatus.OK,
message: 'Received Zip file successfully!',
data: response,
};
}
**
2021 年更新
**
现在要做到这一点,您需要像这样导入 FileInterceptor...
import { FileInterceptor } from '@nestjs/platform-express';
Create a helper.ts file that rename your file and contains path
export class Helper {
static customFileName(req, file, cb) {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
let fileExtension = "";
if(file.mimetype.indexOf("jpeg") > -1){
fileExtension = "jpg"
}else if(file.mimetype.indexOf("png") > -1){
fileExtension = "png";
}
const originalName = file.originalname.split(".")[0];
cb(null, originalName + '-' + uniqueSuffix+"."+fileExtension);
}
static destinationPath(req, file, cb) {
cb(null, 'uploads/')
}
}
code for controller
import { Helper } from '../service/Helper';
import { diskStorage } from 'multer';
import {FileInterceptor} from '@nestjs/platform-express'
import {Controller, Post, Body, UseInterceptors, UploadedFile} from '@nestjs/common'
@Post('upload')
@UseInterceptors(
FileInterceptor('picture', {
storage: diskStorage({
destination: Helper.destinationPath,
filename: Helper.customFileName,
}),
}),
)
uploadFile(@UploadedFile() file: Express.Multer.File) {
console.log(file);
}
由于 nestjs 是一个 express 应用程序,因此可以使用任何库来处理使用 nest 的上传,并且由于它提供了 Midlewares,因此也可以使用 multer。我的问题是:使用 nestjs 处理文件上传的最佳方式是什么?
正如@Kamyl 在问题 https://github.com/nestjs/nest/issues/262 上所告知的那样,因为 v4.6.0
可以使用通用文件拦截器使用 multer 将文件上传到 nestjs。
import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
...
@UseInterceptors(FileInterceptor('file'))
async upload( @UploadedFile() file) {
console.log(file)
}
这样变量 file
就会有一个 buffer
使用 Multer 选项
还需要字段名作为第一个参数,然后是一个带有 Multer Options 的数组
import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
import { diskStorage } from 'multer'
import { extname } from 'path'
...
@UseInterceptors(FileInterceptor('file', {
storage: diskStorage({
destination: './uploads'
, filename: (req, file, cb) => {
// Generating a 32 random chars long string
const randomName = Array(32).fill(null).map(() => (Math.round(Math.random() * 16)).toString(16)).join('')
//Calling the callback passing the random name generated with the original extension name
cb(null, `${randomName}${extname(file.originalname)}`)
}
})
}))
async upload( @UploadedFile() file) {
console.log(file)
}
这样变量 file
将具有 filename
、destination
和 path
。
diskStorage
中的 destination
参数也可以是一个函数,其参数和期望的回调与 filename
相同。通过传递 diskStorage
文件将自动保存到指定文件名的目的地。
也可以使用 @UploadedFiles
和 FilesInterceptor
(复数)处理多个文件
更简洁的方法是将配置提取到单独的文件中,然后在拦截器方法中调用它
import { extname } from 'path';
import { existsSync, mkdirSync } from 'fs';
import { diskStorage } from 'multer';
import { v4 as uuid } from 'uuid';
import { HttpException, HttpStatus } from '@nestjs/common';
// Multer configuration
export const multerConfig = {
dest: process.env.UPLOAD_LOCATION,
};
// Multer upload options
export const multerOptions = {
// Enable file size limits
limits: {
fileSize: +process.env.MAX_FILE_SIZE,
},
// Check the mimetypes to allow for upload
fileFilter: (req: any, file: any, cb: any) => {
if (file.mimetype.match(/\/(jpg|jpeg|png|gif)$/)) {
// Allow storage of file
cb(null, true);
} else {
// Reject file
cb(new HttpException(`Unsupported file type ${extname(file.originalname)}`, HttpStatus.BAD_REQUEST), false);
}
},
// Storage properties
storage: diskStorage({
// Destination storage path details
destination: (req: any, file: any, cb: any) => {
const uploadPath = multerConfig.dest;
// Create folder if doesn't exist
if (!existsSync(uploadPath)) {
mkdirSync(uploadPath);
}
cb(null, uploadPath);
},
// File modification details
filename: (req: any, file: any, cb: any) => {
// Calling the callback passing the random name generated with the original extension name
cb(null, `${uuid()}${extname(file.originalname)}`);
},
}),
};
然后像这样在拦截器下调用
import { ... , UseInterceptors, FileInterceptor, UploadedFile } from '@nestjs/common'
import { diskStorage } from 'multer'
import { extname } from 'path'
import { multerOptions } from 'src/config/multer.config';
...
@Post('/action/upload')
@UseInterceptors(FileInterceptor('file', multerOptions))
async upload( @UploadedFile() file) {
console.log(file)
}
一个简单的方法是使用控制器。您需要定义一个上传控制器并将其添加到您的 app.module,这是一个控制器应该是什么的示例(后端):
@Controller()
export class Uploader {
@Post('sampleName')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file) {
// file name selection
const path = `desired path`;
const writeStream = fs.createWriteStream(path);
writeStream.write(file.buffer);
writeStream.end();
return {
result: [res],
};
}
}
并在前端通过 fetch 调用您的控制器:
fetch('controller address', {
method: 'POST',
body: data,
})
.then((response) => response.json())
.then((success) => {
// What to do when succeed
});
})
.catch((error) => console.log('Error in uploading file: ', error));
使用 Multer 选项的最简洁的实现
感谢@VictorIvens 提供的最佳答案。
但是,我在代码中发现了以下问题。
- 在最新版本的 NestJS 中
@nestjs/common
包中不存在名为FileInterceptor
的导入。 - 代码在我看来有点太混乱了。
所以,为了简化事情,我提出了以下解决方案。
storage.config.ts
export const storage = diskStorage({
destination: "./uploads",
filename: (req, file, callback) => {
callback(null, generateFilename(file));
}
});
function generateFilename(file) {
return `${Date.now()}.${extname(file.originalname)}`;
}
你的-controller.controller.ts
import {
Controller,
Post,
UseInterceptors,
UploadedFile
} from "@nestjs/common";
import { FileInterceptor } from "@nestjs/platform-express";
import { diskStorage } from "multer";
import { extname } from "path";
import { storage } from "./storage.config"
@Controller()
export class YourController {
@Post("upload") // API path
@UseInterceptors(
FileInterceptor(
"file", // name of the field being passed
{ storage }
)
)
async upload(@UploadedFile() file) {
return file;
}
}
如果您通过 API 调用从用户那里获取数据,您可以将数据保存为缓冲区并使用 adm-zip 访问内容。下面是 nest.js.
中的控制器方法实现@Post("/blackBoardUpload")
@UseInterceptors(
FileInterceptor('image', {
storage: memoryStorage(),
fileFilter: zipFileFilter,
}),
)
async uploadedFile(@UploadedFile() file) {
console.log(file)
const response = {
originalname: file.originalname,
filename: file.filename,
};
var AdmZip = require('adm-zip');
var zip = new AdmZip(file.buffer);
var zipEntries = zip.getEntries();
console.log(zipEntries.length);
return {
status: HttpStatus.OK,
message: 'Received Zip file successfully!',
data: response,
};
}
**
2021 年更新
**
现在要做到这一点,您需要像这样导入 FileInterceptor...
import { FileInterceptor } from '@nestjs/platform-express';
Create a helper.ts file that rename your file and contains path
export class Helper {
static customFileName(req, file, cb) {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
let fileExtension = "";
if(file.mimetype.indexOf("jpeg") > -1){
fileExtension = "jpg"
}else if(file.mimetype.indexOf("png") > -1){
fileExtension = "png";
}
const originalName = file.originalname.split(".")[0];
cb(null, originalName + '-' + uniqueSuffix+"."+fileExtension);
}
static destinationPath(req, file, cb) {
cb(null, 'uploads/')
}
}
code for controller
import { Helper } from '../service/Helper';
import { diskStorage } from 'multer';
import {FileInterceptor} from '@nestjs/platform-express'
import {Controller, Post, Body, UseInterceptors, UploadedFile} from '@nestjs/common'
@Post('upload')
@UseInterceptors(
FileInterceptor('picture', {
storage: diskStorage({
destination: Helper.destinationPath,
filename: Helper.customFileName,
}),
}),
)
uploadFile(@UploadedFile() file: Express.Multer.File) {
console.log(file);
}