Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(platform): add multer support for fastify platform #4921

Closed
wants to merge 1 commit into from
Closed

feat(platform): add multer support for fastify platform #4921

wants to merge 1 commit into from

Conversation

gperdomor
Copy link
Contributor

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

[ ] Bugfix
[x] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:

What is the current behavior?

File upload using Multer is only supported for express adapter

Issue Number: N/A

What is the new behavior?

Add fastify-multer support in @nestjs/platform-fastify.

Does this PR introduce a breaking change?

[ ] Yes
[x] No

Other information

@coveralls
Copy link

Pull Request Test Coverage Report for Build c9974085-88e3-4546-b56a-9322d1eed8b8

  • 93 of 95 (97.89%) changed or added relevant lines in 9 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.06%) to 94.79%

Changes Missing Coverage Covered Lines Changed/Added Lines %
packages/platform-fastify/multer/interceptors/file-fields.interceptor.ts 15 17 88.24%
Totals Coverage Status
Change from base Build 539a93a9-8db4-434e-944d-b98d10267b0b: 0.06%
Covered Lines: 4931
Relevant Lines: 5202

💛 - Coveralls

@smhmayboudi
Copy link
Contributor

I am looking forward to using this feature <3

@eugenio165
Copy link

You beat me to it! I was doing literally the exact same thing today! Good stuff, hope this gets merged soon

/** Location of the uploaded file (DiskStorage) */
path: string;
/** A Buffer of the entire file (MemoryStorage) */
buffer: Buffer;
Copy link

@eugenio165 eugenio165 Jul 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fastify-multer adds another field here called stream of type NodeJS.ReadableStream

@@ -20,6 +20,8 @@
"fastify": "2.14.1",
"fastify-cors": "3.0.3",
"fastify-formbody": "3.2.0",
"fastify-multer": "^1.5.4",
"fastify-multipart": "^1.0.6",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont think this is used in the code added, the user has to register this package on the adapter in the main.ts file

@asc11cat
Copy link

@kamilmysliwiec any thoughts on PR? #2088 was closed due to fastify-multer being a too young library, but a year has passed since then

@AliYusuf95
Copy link

Any update on this pull? I think it should accepted @kamilmysliwiec

@emagnier
Copy link

emagnier commented Sep 9, 2020

Should we consider adding this support through the official core https://github.com/fastify/fastify-multipart package, instead of fastify-multer from the community?

This feature would be configured and used differently than multer for Express, but is it an issue?

@kamilmysliwiec what are your thoughts on that?

@kamilmysliwiec kamilmysliwiec added this to the 7.5.0 milestone Oct 5, 2020
@kamilmysliwiec kamilmysliwiec removed this from the 7.5.0 milestone Nov 2, 2020
@m-risto
Copy link

m-risto commented Feb 15, 2021

Is there a timeline for this feature? What is currently the best way to use the file upload with fastify in nestjs? The only blocker we have, which blocks the migration to nestjs with fastify.

@newradius
Copy link

Is there a timeline for this feature? What is currently the best way to use the file upload with fastify in nestjs? The only blocker we have, which blocks the migration to nestjs with fastify.

Same here, do you guys have a timeline?

@ghost
Copy link

ghost commented May 12, 2021

May I ask what is the status of this pull request? File upload is an essential part of our app and since we would like to use Fastify, the availability of an officially supported FileInterceptor is key.

@newradius
Copy link

Any news on this?

@kscerbiakas
Copy link

Also wondering what’s the status ?

@MickL
Copy link

MickL commented Jul 23, 2021

Would also like to know what needs to happen to make this PR to be merged

@thebergamo
Copy link

@kamilmysliwiec any chance of this getting merged? Is it just a case to fix merge conflicts?

@kscerbiakas
Copy link

Because if its long time not merged, then maybe package it up as a package for others to use? Because there is no response from @kamilmysliwiec and there are quite a few people who'd like to use it. What do you think @gperdomor ?

@MickL
Copy link

MickL commented Sep 14, 2021

I had to revert back a project from fastify to express because multer integration was too complex / would have taken too much time :(

@gperdomor
Copy link
Contributor Author

Because if its long time not merged, then maybe package it up as a package for others to use? Because there is no response from @kamilmysliwiec and there are quite a few people who'd like to use it. What do you think @gperdomor ?

A new MR is in progress: #6935

@thebergamo
Copy link

For those who are stuck in this issues with mulher I found a way to workaround it fastify-multer and it worked quite fine.

@vytautas-pranskunas-
Copy link

any progress on this?

@EvilCheetah
Copy link

@kamilmysliwiec
Any updates on this pull request?

@jmcdo29
Copy link
Member

jmcdo29 commented Aug 5, 2022

Any updates on this pull request?

@EvilCheetah If you need this, I've created @nest-lab/fastify-multer as an intermediary and to allow for file uploads with fastify as the HTTP adapter. Almost identical API except that the FastifyMulterModule must be imported at least once to ensure that the body parser for multipart/form-data gets registered

@necm1
Copy link

necm1 commented Aug 5, 2022

@jmcdo29 thank you for creating this package!

Why is this MR not getting the attention as it should? 2 years passed since the MR and it still didn't get merge.

@kamilmysliwiec
Copy link
Member

Because it relies on a tiny package that is out of our control.

@fpmanuel
Copy link

fpmanuel commented Apr 29, 2023

Since unfortunatelly this PR hasn't been merged and all the current solutions I found in the web don't fit my needs or are unstable, I decided to create my own solution based on Interceptors and '@fastify/multipart`. I leave the code here just in case someone came here in the future :)

main.ts

 import multipart from '@fastify/multipart';

  const fastify = new FastifyAdapter(fastifyOptions);
  fastify.register(multipart as FastifyPluginCallback<any, any>);

  const app = await NestFactory.create<NestFastifyApplication>(AppModule, fastify);

UploadService.ts | NOTE: The localstack variables are just for testing, you can use localstack or a production S3.

import { Injectable } from '@nestjs/common';
import { S3Client, DeleteObjectCommand, PutObjectCommandInputType } from '@aws-sdk/client-s3';
import { Upload } from '@aws-sdk/lib-storage';
import dotenv from 'dotenv';

dotenv.config();

const s3 = new S3Client({
  region: process.env.AWS_REGION,
  endpoint: process.env.LOCALSTACK_S3_ENDPOINT,
  forcePathStyle: !!process.env.LOCALSTACK_S3_ENDPOINT,
});

@Injectable()
export class UploadService {
  async upload(stream: PutObjectCommandInputType['Body'], name: string, prefix: string) {
    return new Upload({
      client: s3,
      params: {
        Bucket: process.env.BUCKET,
        Key: name,
        Body: stream,
      },
    }).done() as Promise<{
      Location: string;
      Key: string;
      Bucket: string;
      ETag: string;
    }>;
  }

  async remove(key: string) {
    return s3.send(
      new DeleteObjectCommand({
        Bucket: process.env.BUCKET,
        Key: key,
      })
    );
  }
}

UploadInterceptor.ts

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Writable } from 'stream';
import pump from 'pump';
import type { UploadService } from '../modules/upload/upload.service';

@Injectable()
export class UploadInterceptor implements NestInterceptor {
  names: string[] | string;

  prefix: string;

  constructor(private uploadService: UploadService, names: string[] | string, prefix: string) {
    this.names = names;
    this.prefix = prefix;
  }

  async intercept(context: ExecutionContext, next: CallHandler) {
    const req = context.switchToHttp().getRequest();

    req.fileStorage = {};
    req.body = req.body || {};

    const parts = req.parts();

    for await (const part of parts) {
      if (part.type !== 'file') {
        req.body[part.fieldname] = part.value;
        continue;
      }

      if (!this.names.includes(part.fieldname) && this.names !== '*') {
        // If there is a file that is not in the list of allowed files, pipe the stream to nowhere so the loop doesn't hang.
        const writable = new Writable();
        writable._write = (_, __, _next) => _next();
        pump(part.file, writable);
        continue;
      }

      const newFile = await this.uploadService.upload(part.file, part.filename, this.prefix);

      req.fileStorage[part.fieldname] = req.fileStorage[part.fieldname] || [];

      req.fileStorage[part.fieldname].push({
        fieldname: part.fieldname,
        filename: part.filename,
        encoding: part.encoding,
        mimetype: part.mimetype,
        location: newFile.Location,
        key: newFile.Key,
        bucket: newFile.Bucket,
        etag: newFile.ETag,
      });
    }

    return next.handle();
  }
}

ExampleController.ts

  @UseInterceptors(new UploadInterceptor(new UploadService(), '*', 'prefix'))
  async create@Req() req: FastifyRequest): Promise<GroupModel> {
    console.log(req.fileStorage)
  }

@gperdomor gperdomor closed this by deleting the head repository May 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.