NestJS FilesInterceptor does not parse files from Axios request

1.3k Views Asked by At

I have a controller that is using FilesInterceptor to process multipart/form-data uploads.

  @Post('/upload/:serial')
  @UseInterceptors(FilesInterceptor('files[]'))
  uploadLogFiles(
    @UploadedFiles() files: UploadLog[],
    @Param('serial') serial: number,
    @Req() request: Request
  ): LogUploadResponse {
    const upLoadedfiles = this.logPersistenceService.persistFiles(
      files,
      serial
    );
    return { files: upLoadedfiles };
  }
}

When I submit files via a request created with Postman the files are parsed out of the request successfully.

However, when I try to create a request with Nest using the Axios based HttpService and the Form-Data library I cannot get the files from the request.

const formData = new FormData();
formData .append('files[]', 'a,b,c', fileName);

this.httpService
      .post<LogUploadResponse>(
        `${this.restUrl}/api/logging/upload/${serial}`,
        formData,
        {
          headers: formData.getHeaders()
        }
      )

I have verified that the controller is receiving the request but files is empty. I have piped formData to a WriteStream and the contents look good and the boundary also matches what is in the header.

----------------------------347967411467094575699495
Content-Disposition: form-data; name="files[]"; filename="a.log"
Content-Type: text/plain

a,b,c
----------------------------347967411467094575699495--
REQUEST Headers { accept: 'application/json, text/plain, */*',
  'content-type':
   'multipart/form-data; boundary=--------------------------347967411467094575699495',
  referer: 'http://localhost/',
  'user-agent':
   'Mozilla/5.0 (win32) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/15.2.1',
  'accept-language': 'en',
  origin: 'http://localhost',
  host: 'localhost:8081',
  'accept-encoding': 'gzip, deflate',
  'content-length': '17',
  connection: 'keep-alive' }

Update I am able to make it work if I use node http module directly rather than NestJS/Axios

Works

    const form = new FormData();

    for (const file of Object.keys(files)) {
      form.append('files[]', files[file], file);
    }
    return new Promise<LogUploadResponse>((resolve, reject) => {
      const req = request(
        {
          method: 'POST',
          hostname: 'localhost',
          port: 8081,
          path: `/api/logging/upload/${serial}`,
          headers: form.getHeaders()
        },
        res => {
          res.on('error', r => {
            reject(r.message);
          });
          res.on('data', r => {
            console.log('**r', r.toString());
            resolve(r.toString());
          });
        }
      );
    form.pipe(req);

Does not work

    const form = new FormData();

    for (const file of Object.keys(files)) {
      form.append('files[]', files[file], file);
    }
    const req = this.httpService.request<LogUploadResponse>({
      baseURL: 'http://localhost:8081',
      url: `/api/logging/upload/${serial}`,
      method: 'POST',
      data: form,
      headers: form.getHeaders()
    });

    return req
      .pipe(
        tap(resp => console.log('status', resp.status)),
        map(resp => resp.data),
        catchError(_err => of({ files: [] }))
      )
      .toPromise();

I took a look at Axios source for http.js in GitHub and it looks like it is doing a pipe on the stream data but I didn't dig too deeply.

1

There are 1 best solutions below

0
steve dunning On

Was never able to get the Axios version working and just implemented the node http version for this specific request in my application.