18

I want to send file along with JSON

{
    "comment" : "string",
    "outletId" : 1
}

The help I got from Documentation is

requestBody:
    content:
      multipart/form-data:
        schema:
          type: object
          properties:
            orderId:
              type: integer
            userId:
              type: integer
            fileName:
              type: string
              format: binary

I don't know where to put this schema. I have tried putting it inside @ApiProperty() in DTO as well as in @ApiOperations but could not resolve the issue.

Below is the function I want to capture file content in.

@Post('/punchin')
@ApiConsumes('multipart/form-data')
@ApiOperation({ summary: 'Attendance Punch In' })
@UseInterceptors(CrudRequestInterceptor, ClassSerializerInterceptor, FileInterceptor('file'))
@ApiImplicitFile({ name: 'file' })
async punchInAttendance( @Body() body: PunchInDto, @UploadedFile() file: Express.Multer.File ): Promise<Attendance> {
    const imageUrl = await this.s3FileUploadService.upload(file)
    console.log(body, imageUrl)
    return await this.service.punchInAttendance({
      comment: body.punchInComment,
      outletId: body.outletId,
      imgUrl: imageUrl,
    })
  }
Jamshaid Tariq
  • 551
  • 1
  • 8
  • 31

6 Answers6

55

use @ApiBody because body keeps your data.

  @Post('upload')
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        comment: { type: 'string' },
        outletId: { type: 'integer' },
        file: {
          type: 'string',
          format: 'binary',
        },
      },
    },
  })
  @UseInterceptors(FileExtender)
  @UseInterceptors(FileInterceptor('file'))
  uploadFile2(@UploadedFile('file') file) {
    console.log(file);
  }

screenshot

I get in console:

{
  fieldname: 'file',
  originalname: 'dart.txt',
  encoding: '7bit',
  mimetype: 'text/plain',
  buffer: <Buffer 20 0a 69 6d  ... 401 more bytes>,
  size: 451,
  comment: 'some comment',
  outletId: 123456
}

Because FileInterceptor removes body params, I used FileExtender interceptor, to pack comment and outletId in file properties.

@Injectable()
export class FileExtender implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const req = context.switchToHttp().getRequest();
    req.file['comment'] = req.body.comment;
    req.file['outletId'] = Number(req.body.outletId);
    return next.handle();
  }
}
HaaLeo
  • 10,065
  • 3
  • 44
  • 55
Daniil Loban
  • 4,165
  • 1
  • 14
  • 20
  • giving error at comment and outletId that `Type 'string is not assignable to type 'SchemaObject | ReferenceObject'.` – Jamshaid Tariq Mar 12 '21 at 19:27
  • try to change it from `'string'` to `String` - js object – Daniil Loban Mar 12 '21 at 19:39
  • doing this does not solve the issue... I assigned empty object `{ }` just for the sake of test.. error goes away but new one came saying `error TS2688: cannot find type definition file for 'loash'.` – Jamshaid Tariq Mar 12 '21 at 19:46
  • does it work if you remove both `comment : 'string', outletId : 'integer'` ? – Daniil Loban Mar 12 '21 at 19:49
  • removing comment and outlet id makes field as fileupload in swagger UI. now tell me how to include comment and outlet id too. – Jamshaid Tariq Mar 13 '21 at 12:35
  • Instead of putting Body properties like this, is there a way to define file properties along with Request Body DTO? – Sanchit Bhatnagar Jan 16 '22 at 10:19
  • @SanchitBhatnagar can you describe more specifically what you are looking for? I'm not sure there are other ways, although I don't deny it – Daniil Loban Jan 16 '22 at 11:11
  • @DaniilLoban ```properties: { comment: { type: 'string' }, outletId: { type: 'integer' }, file: { type: 'string', format: 'binary', }, },``` Here body properties are explicitly defined here only, Can somehow map a DTO class here for the body instead of defining it here manually? – Sanchit Bhatnagar Jan 17 '22 at 09:31
  • Maybe I don't know something, but I have a bad idea how you will interact with a swagger if the file is a field in a DTO object showed as JSON in the swagger interface except in base64. – Daniil Loban Jan 18 '22 at 19:19
  • @DaniilLoban what if we have a nested object of files, please do you have any idea of how to render that? – Israel Obanijesu Jun 21 '22 at 15:43
  • @IsraelObanijesu working with an array of files is also possible, but I think it would be better to put this in a separate question where you can give a full-fledged example of the necessary structure, you can add a link to your question here – Daniil Loban Jun 21 '22 at 18:06
  • how can i set swagger key param file for multi value . – lakshmankashyap Jun 27 '22 at 10:19
  • @lakshmankashyap write a little more about what you mean by multivalue – Daniil Loban Jun 27 '22 at 10:37
14

The solution that works for me was to create a class containing the API references I will be using and to set one of those fields as the File.

storage-object.dto.ts

export class StorageObjectDto {
    @ApiProperty({ required: false })
    @IsString()
    comment?: string

    @ApiProperty({ type: 'string', format: 'number', required: false })
    @IsNumber()
    outletId?: number

    @ApiProperty({ type: 'string', format: 'binary', required: true })
    file: Express.Multer.File
}

Using the implementation suggested on the nestJs docs, I can extract the file based on the associated key within the object. In this case, the key is file

object.controller.ts

@Version('1')
@Post('upload')
@ApiConsumes('multipart/form-data')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@Body() data: StorageObjectDto, @UploadedFile() file: Express.Multer.File): void {
    console.log({ data, file })
}

Once you call the endpoint you should see the following output in your console log

{
  data: FileDataDto {
    comment: 'This is a test comment',
    outletID: 123
  },
  file: {
    fieldname: 'file',
    originalname: 'placeholder.png',
    encoding: '7bit',
    mimetype: 'image/png',
    buffer: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 01 f4 00 00 01 f4 04 03 00 00 00 39 f8 c2 b9 00 00 00 1b 50 4c 54 45 cc cc cc 96 96 96 9c 9c 9c ... 1069 more bytes>,
    size: 1119
  }
}
Al Fahad
  • 2,378
  • 5
  • 28
  • 37
2

another way, you can do like this

@ApiProperty({
  type: 'array',
  items: {
    type: 'string',
    format: 'binary',
  },
  required: false,
})
file: Express.Multer.File;
Sigit
  • 728
  • 7
  • 12
1

You can upload like this:

@ApiConsumes('multipart/form-data')
@ApiBody({
  schema: {
    type: 'object',
    properties: {
      media: {
        type: 'string',
        format: 'binary',
      },
    },
  },
})

Documentation

user16217248
  • 3,119
  • 19
  • 19
  • 37
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 29 '23 at 00:42
  • Worked for me. Thanks a lot! Note that the "media" is a property name which you can set to whatever name you want. – ArtemNovikov Jun 06 '23 at 18:33
0

This works for me

import { ApiImplicitFile } from '@nestjs/swagger/dist/decorators/api-implicit-file.decorator';

@ApiImplicitFile({ name: 'avatar', required: true, description: 'Avatar' })
Roman
  • 151
  • 7
-1

We can send the file along with other data. Here are my implementation to upload the file and send other data like the body:

Here is the request body DTO:

export class ReqBodyDto {
  @ApiProperty({ required: true })
  @IsNotEmpty()
  MACode: string;

  @ApiProperty({ required: true })
  @IsNotEmpty()
  chunkSize: string;
}

Here is the API implementation in NEST.JS

  @UseGuards(StrictAuthGuard)
  @Post("/v1/upload")
  @UseInterceptors(FileInterceptor('file', multerOptions))
  async upload(@UploadedFile() file, @Body() body: ReqBodyDto) {
    console.log(`body : ${JSON.stringify(body)}`);
    if (body?.MACode !== MACode) {
      return "MACode is invalid. Please provide the correct MACode";
    }
    if (!file) {
      throw new HttpException(
        `Please provide correct file name`,
        400
      );
    }
    console.log(`Migration file: ${JSON.stringify(file)}`);
    return this.migrations(file, body);
  }

This is how you can send the data using postman:

enter image description here

Shubham Verma
  • 8,783
  • 6
  • 58
  • 79