问题描述
我正在尝试使用fastify适配器使用nestjs上传多个文件。我可以按照此链接中的教程进行操作-article on upload
现在这可以使用fastify-multipart来完成文件上传的工作,但是我无法在上传之前利用请求验证, 例如,这是我的规则文件模型(以后我想保存到postgre)
import {IsUUID,Length,IsEnum,Isstring,Matches,IsOptional} from "class-validator";
import { FileExtEnum } from "./enums/file-ext.enum";
import { Updatable } from "./updatable.model";
import {Expose,Type} from "class-transformer";
export class RuleFile {
@Expose()
@IsUUID("4",{ always: true })
id: string;
@Expose()
@Length(2,50,{
always: true,each: true,context: {
errorCode: "REQ-000",message: `Filename shouldbe within 2 and can reach a max of 50 characters`,},})
fileNames: string[];
@Expose()
@IsEnum(FileExtEnum,{ always: true,each: true })
fileExts: string[];
@IsOptional({each: true,message: 'File is corrupated'})
@Type(() => Buffer)
file: Buffer;
}
export class RuleFileDetail extends RuleFile implements Updatable {
@Isstring()
@Matches(/[aA]{1}[\w]{6}/)
recUpdUser: string;
}
我想验证分段请求,看看是否设置正确。 我无法使其与基于事件订阅的方法一起使用。这是我尝试过的一些操作-添加拦截器,以检查请求
@Injectable()
export class FileUploadValidationInterceptor implements nestInterceptor {
intercept(context: ExecutionContext,next: CallHandler): Observable<any> {
const req: FastifyRequest = context.switchToHttp().getRequest();
console.log('inside interceptor',req.body);
// content type cmes with multipart/form-data;boundary----. we dont need to valdidate the boundary
// Todo: handle split errors based on semicolon
const contentType = req.headers['content-type'].split(APP_CONSTANTS.CHAR.SEMI_COLON)[0];
console.log(APP_CONSTANTS.REGEX.MULTIPART_CONTENT_TYPE.test(contentType));
const isHeaderMultipart = contentType != null?
this.headerValidation(contentType): this.throwError(contentType);
**// CANNOT check fir req.file() inside this,as it throws undefined**
return next.handle();
}
headerValidation(contentType) {
return APP_CONSTANTS.REGEX.MULTIPART_CONTENT_TYPE.test(contentType) ? true : this.throwError(contentType);
}
throwError(contentType: string) {
throw AppConfigService.getCustomError('FID-HEADERS',`Request header does not contain multipart type:
Provided incorrect type - ${contentType}`);
}
}
我无法在上述拦截器中检查req.file()。抛出未定义。我试图遵循fastify-multipart
但是我无法按照fastify-multipart文档中的规定在预处理器中获取请求数据
fastify.post('/',async function (req,reply) {
// process a single file
// also,consider that if you allow to upload multiple files
// you must consume all files othwise the promise will never fulfill
const data = await req.file()
data.file // stream
data.fields // other parsed parts
data.fieldname
data.filename
data.encoding
data.mimetype
// to accumulate the file in memory! Be careful!
//
// await data.toBuffer() // Buffer
//
// or
await pump(data.file,fs.createWriteStream(data.filename))
我试图通过注册这样一个我自己的预处理程序钩子(执行为iife)来获得通行证
(async function bootstrap() {
const appConfig = AppConfigService.getAppCommonConfig();
const fastifyInstance = SERVERADAPTERINSTANCE.configureFastifyServer();
// @ts-ignore
const fastifyAdapter = new FastifyAdapter(fastifyInstance);
app = await nestFactory.create<nestFastifyApplication>(
AppModule,fastifyAdapter
).catch((err) => {
console.log("err in creating adapter",err);
process.exit(1);
});
.....
app.useGlobalPipes(
new ValidationPipe({
errorHttpStatusCode: 500,transform: true,validationError: {
target: true,value: true,exceptionFactory: (errors: ValidationError[]) => {
// send it to the global exception filter\
AppConfigService.validationExceptionFactory(errors);
},}),);
app.register(require('fastify-multipart'),{
limits: {
fieldNameSize: 100,// Max field name size in bytes
fieldSize: 1000000,// Max field value size in bytes
fields: 10,// Max number of non-file fields
fileSize: 100000000000,// For multipart forms,the max file size
files: 3,// Max number of file fields
headerPairs: 2000,// Max number of header key=>value pairs
},});
(app.getHttpAdapter().getInstance() as FastifyInstance).addHook('onRoute',(routeOptions) => {
console.log('all urls:',routeOptions.url);
if(routeOptions.url.includes('upload')) {
// The registration actually works,but I cant use the req.file() in the prehandler
console.log('###########################');
app.getHttpAdapter().getInstance().addHook('preHandler',FilePrehandlerService.fileHandler);
}
});
SERVERADAPTERINSTANCE.configureSecurity(app);
//Connect to database
await SERVERADAPTERINSTANCE.configureDbConn(app);
app.useStaticAssets({
root: join(__dirname,"..","public"),prefix: "/public/",});
app.setViewEngine({
engine: {
handlebars: require("handlebars"),templates: join(__dirname,"views"),});
await app.listen(appConfig.port,appConfig.host,() => {
console.log(`Server listening on port - ${appConfig.port}`);
});
})();
这是预处理程序,
export class FilePrehandlerService {
constructor() {}
static fileHandler = async (req,reply) => {
console.log('coming inside prehandler');
console.log('req is a multipart req',await req.file);
const data = await req.file();
console.log('data received -filename:',data.filename);
console.log('data received- fieldname:',data.fieldname);
console.log('data received- fields:',data.fields);
return;
};
}
这种使用preHandler处理文件和获取文件的模式可以在裸露的fastify应用程序中使用。我试过了
裸机服务器:
export class FileController {
constructor() {}
async testHandler(req: FastifyRequest,reply: FastifyReply) {
reply.send('test reading dne');
}
async fileReadHandler(req,reply: FastifyReply) {
const data = await req.file();
console.log('field val:',data.fields);
console.log('field filename:',data.filename);
console.log('field fieldname:',data.fieldname);
reply.send('done');
}
}
export const FILE_CONTROLLER_INSTANCE = new FileController();
这是我的路线文件
const testRoute: RouteOptions<Server,IncomingMessage,ServerResponse,RouteGenericInterface,unkNown> = {
method: 'GET',url: '/test',handler: TESTCONTROLLER_INSTANCE.testMethodRouteHandler,};
const fileRoute: RouteOptions = {
method: 'GET',url: '/fileTest',preHandler: fileInterceptor,handler: FILE_CONTROLLER_INSTANCE.testHandler,};
const fileUploadRoute: RouteOptions = {
method: 'POST',url: '/fileUpload',handler: FILE_CONTROLLER_INSTANCE.fileReadHandler,};
const apiRoutes = [testRoute,fileRoute,fileUploadRoute];
export default apiRoutes;
有人可以让我知道获取字段名称的正确方法,然后在nestjs中调用的服务之前对其进行验证
解决方法
嗯,我做过这样的事情,它对我很有用。也许它也适合你。
// main.ts
import multipart from "fastify-multipart";
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,new FastifyAdapter(),);
app.register(multipart);
// upload.guard.ts
import {
Injectable,CanActivate,ExecutionContext,BadRequestException,} from "@nestjs/common";
import { FastifyRequest } from "fastify";
@Injectable()
export class UploadGuard implements CanActivate {
public async canActivate(ctx: ExecutionContext): Promise<boolean> {
const req = ctx.switchToHttp().getRequest() as FastifyRequest;
const isMultipart = req.isMultipart();
if (!isMultipart)
throw new BadRequestException("multipart/form-data expected.");
const file = await req.file();
if (!file) throw new BadRequestException("file expected");
req.incomingFile = file;
return true;
}
}
// file.decorator.ts
import { createParamDecorator,ExecutionContext } from "@nestjs/common";
import { FastifyRequest } from "fastify";
export const File = createParamDecorator(
(_data: unknown,ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest() as FastifyRequest;
const file = req.incomingFile;
return file
},);
// post controller
@Post("upload")
@UseGuards(UploadGuard)
uploadFile(@File() file: Storage.MultipartFile) {
console.log(file); // logs MultipartFile from "fastify-multipart"
return "File uploaded"
}
最后是我的打字文件
declare global {
namespace Storage {
interface MultipartFile {
toBuffer: () => Promise<Buffer>;
file: NodeJS.ReadableStream;
filepath: string;
fieldname: string;
filename: string;
encoding: string;
mimetype: string;
fields: import("fastify-multipart").MultipartFields;
}
}
}
declare module "fastify" {
interface FastifyRequest {
incomingFile: Storage.MultipartFile;
}
}
,
所以我找到了一个更简单的选择。我开始使用fastify-multer。我将它与这个很棒的库一起使用-使我使用了multer进行固定-@ webundsoehne / nest-fastify-file-upload
这些是我所做的更改。我注册了multer内容流程。
app.register(multer( {dest:path.join(process.cwd()+'/upload'),limits:{
fields: 5,//Number of non-file fields allowed
files: 1,fileSize: 2097152,// 2 MB,}}).contentParser);
然后在控制器中-我按nestjs文档所说使用它。实际上,这使得与multer轻松工作
@UseInterceptors(FileUploadValidationInterceptor,FileInterceptor('file'))
@Post('/multerSample')
async multerUploadFiles(@UploadedFile() file,@Body() ruleFileCreate: RuleFileCreate) {
console.log('data sent',ruleFileCreate);
console.log(file);
// getting the original name of the file - no matter what
ruleFileCreate.originalName = file.originalname;
return await this.fileService.fileUpload(file.buffer,ruleFileCreate);
}
奖金-将文件存储在本地并将其存储在数据库中-请参阅