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.
Was never able to get the Axios version working and just implemented the node http version for this specific request in my application.