首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Nestjs -使用fastify multipart上传文件

Nestjs -使用fastify multipart上传文件
EN

Stack Overflow用户
提问于 2020-09-03 20:59:03
回答 2查看 4.2K关注 0票数 3

我正在尝试使用fastify适配器上传多个带有nestjs的文件。我可以按照此链接中的教程执行此操作-article on upload

现在,它使用fastify-multipart完成了文件上传工作,但是在上传之前我不能使用请求验证,例如,这里是我的规则-文件-模型(后来我想保存到postgre)

代码语言:javascript
复制
    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;
    }

我想验证多部分请求,看看这些设置是否正确。我不能使用基于事件订阅的方法。下面是我尝试过的一些事情--添加拦截器来检查请求

代码语言:javascript
复制
    @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文档中提供的那样在预处理程序中获取请求数据

代码语言:javascript
复制
    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))

我尝试通过注册我自己的预处理程序钩子来获得via,如下所示(执行为Life.)

代码语言:javascript
复制
    (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}`);
      });
    })();

这是预处理程序,

代码语言:javascript
复制
    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应用程序中工作。我试过了

空的fastify服务器:

代码语言:javascript
复制
    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();

这是我的路径文件

代码语言:javascript
复制
    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',
        preHandler: fileInterceptor,
        handler: FILE_CONTROLLER_INSTANCE.fileReadHandler,
    };
    
    const apiRoutes = [testRoute, fileRoute, fileUploadRoute];
    export default apiRoutes;

谁能告诉我获取字段名的正确方法,在Nestjs中调用服务之前验证它们

EN

回答 2

Stack Overflow用户

发布于 2021-02-24 01:22:21

嗯,我做过这样的事情,它对我很有效。也许它也可以为你工作。

代码语言:javascript
复制
// main.ts
import multipart from "fastify-multipart";

const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
);
app.register(multipart);
代码语言:javascript
复制
// 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;
    }
}
代码语言:javascript
复制
// 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
    },
);
代码语言:javascript
复制
// post controller
@Post("upload")
@UseGuards(UploadGuard)
uploadFile(@File() file: Storage.MultipartFile) {
    console.log(file); // logs MultipartFile from "fastify-multipart"
    return "File uploaded"
}

最后是我的打字文件

代码语言:javascript
复制
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;
    }
}
票数 10
EN

Stack Overflow用户

发布于 2020-09-04 21:43:37

所以我找到了一个更简单的替代方案。我开始使用fastify-multer。我将它与这个很棒的库一起使用--这使得我使用multer来进行fastify @webundsoehne/nest fastify file-upload

这些是我所做的更改。我注册了multer内容进程。

代码语言:javascript
复制
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);

控制器中的THen -我按照nestjs文档所说的那样使用它。这实际上使fasitfy与multer一起工作

代码语言:javascript
复制
@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);
  }

奖励-将文件存储在本地,并将其存储在数据库中-请参阅

github link

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63724194

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档