I have a NestJs project in my controller I accept an Express.Multer.File but I need to send it to another server. How can I convert it to Blob for passing to FormData?
#action in my controller
#Post('/upload')
#UseInterceptors(FileInterceptor('avatar'))
async uploadUserProfileAvatar(#UploadedFile() file: Express.Multer.File){
console.log(file)
let blob = new Blob(file.mimetype)
let formData = new FormData()
//can't set because file is not Blob
formData.append('file',file)
let request$ = this.httpService.post('http://test_server/file',formData,{headers:{
'Content-Type':'multipart/form-data'
}}).pipe(
map(response => response.data)
)
request$.subscribe( response => {
console.log(response)
return response
})
}
I will be grateful for every answer!
EDIT:
Thanks again for your help! As a result, I managed to successfully send the code in this way:
#Post('/upload')
#UseInterceptors(FileInterceptor('avatar'))
async uploadUserProfileAvatar(#UploadedFile() file: Express.Multer.File){
const FormData = require('form-data');
const formData = new FormData();
formData.append('file', Buffer.from(file.buffer) , file.originalname);
let request$ = this.httpService.post('http://nginx_files/file',formData,{headers:formData.getHeaders()}).pipe(
map(response => response.data)
)
request$.subscribe( response => {
console.log(response)
return response
})
}
Use formdata package like this:
const FormData = require('form-data');
const formData = new FormData();
formData.append('file', fileContent, 'file_name.ext');
let request$ = this.httpService.post('http://test_server/file',
formData,
{ headers: formData.getHeaders() }
).pipe(
map(response => response.data)
)
#MiyRon
You get the Error "TypeError: source.on is not a function", because formData accepts only three types of elements. String, Buffer and Stream.
So you can fix it with:
formData.append('file', JSON.stringify(fileContent), 'file_name.ext');
Encountered same issue, but initial answer didn't work for me, had to add Content-Length header and contentType, so code looks something like this:
import * as FormData from 'form-data';
// ...
processFile(file: Express.Multer.File) {
const formData = new FormData();
formData.append('file', Buffer.from(file.buffer), {
filename: file.originalname,
contentType: file.mimetype,
});
return this.httpService
.post('http://test_server/file', formData, {
headers: {
...formData.getHeaders(),
'Content-Length': `${formData.getLengthSync()}`,
},
})
.pipe(map((response) => response.data));
}
Based on this comment
Related
I used the following js code to load a file with a Chinese filename into FormData and then upload it and found the filename garbled on the server side.
export async function uploadFile(file, url) {
let formData = new FormData()
formData.append('file', file.file)
file.status = 'loading'
let response = await fetch(url, { method: 'POST', body: formData})
file.status = response.ok
return response
}
How can I solve the filename garbling problem because the filenames in formdata seem to be ASCII encoded?
This question is used to share my solution. The code is below. I encoded the file name with encodeURIComponent and created a new File Object from it.
export async function uploadFile(file, url) {
let formData = new FormData()
const file_ = new File([file.file], encodeURIComponent(file.filename), {type: file.file.type});
formData.append('file', file_)
file.status = 'loading'
let response = await fetch(url, { method: 'POST', body: formData})
file.status = response.ok
return response
}
Afterwards I decode the filename with decodeURIComponent on the server side.
I'm trying to use the native fetch() API in NodeJS to upload a file to a REST API.
So far, I've made other GET and POST requests successfully, but this file upload is causing me a lot of trouble.
I have the following function -
async function uploadDocumentToHub(hub_entity_id, document_path) {
let formData = new FormData();
formData.append("type", "Document");
formData.append("name", "ap_test_document.pdf");
formData.append("file", fs.createReadStream("ap_test_document.pdf"));
formData.append("entity_object_id", hub_entity_id);
const form_headers = {
Authorization: auth_code,
...formData.getHeaders(),
};
console.log(
`Uploading document ap_test_document.pdf to hub (${hub_entity_id}) `
);
console.log(formData);
let raw_response = await fetch(urls.attachments, {
method: "POST",
headers: form_headers,
body: formData,
});
console.log(raw_response);
}
which I then run with the following code -
async function async_main() {
......
.......
await uploadDocumentToHub(hub_entity_id, document_path);
}
// main();
async_main();
And I keep getting the following error -
node:internal/deps/undici/undici:5536
p.reject(Object.assign(new TypeError("fetch failed"), { cause: response.error }));
^
TypeError: fetch failed
at Object.processResponse (node:internal/deps/undici/undici:5536:34)
at node:internal/deps/undici/undici:5858:42
at node:internal/process/task_queues:140:7
at AsyncResource.runInAsyncScope (node:async_hooks:202:9)
at AsyncResource.runMicrotask (node:internal/process/task_queues:137:8)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
cause: TypeError: object2 is not iterable
at action (node:internal/deps/undici/undici:1660:39)
at action.next (<anonymous>)
at Object.pull (node:internal/deps/undici/undici:1708:52)
at ensureIsPromise (node:internal/webstreams/util:172:19)
at readableStreamDefaultControllerCallPullIfNeeded (node:internal/webstreams/readablestream:1884:5)
at node:internal/webstreams/readablestream:1974:7
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
}
I'm baffled about what's going on and what this error is about. Any ideas?
The following code correctly uploads the file (auto-generated from postman, some data <removed> for security) -
var axios = require('axios');
var FormData = require('form-data');
var fs = require('fs');
var data = new FormData();
data.append('type', 'Document');
data.append('name', 'ap_test_document.pdf');
data.append('file', fs.createReadStream('kX3bdHb1G/ap_test_document.pdf'));
data.append('entity_object_id', '<id>');
var config = {
method: 'post',
url: '<url>',
headers: {
'Authorization': '<token>',
...data.getHeaders()
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
Some help would be much appreciated.
Thanks!
You can append a file to the Node 18 native Fetch by reading the file with fs.readFile and converting the contents to a Blob.
The following is some pseudo-code I whipped up in TypeScript (YMMV):
import fs from 'fs/promises';
async function createBlobFromFile(path: string): Promise<Blob> {
const file = await fs.readFile(path);
return new Blob([file]);
}
const path = '/path/to/file.txt';
const formData = new FormData();
formData.append('file', await createBlobFromFile(path), 'file.txt');
const response = await fetch('https://some.url', {
method: 'POST',
body: formData
});
You can't use the fetch API in Node.js, either you can install axios or node-fetch.
import fetch from 'node-fetch';
const body = {a: 1};
const response = await fetch('https://httpbin.org/post', {
method: 'post',
body: JSON.stringify(body),
headers: {'Content-Type': 'application/json'}
});
const data = await response.json();
console.log(data);```
https://www.npmjs.com/package/node-fetch
https://www.npmjs.com/package/axios
Hi I am new to React Native, and I was trying to call my API hosted on Heroku my code is below:
const path = RNFS.ExternalDirectoryPath + '/newFile.jpg';
const handleUploadFile = async () => {
const token = await AsyncStorage.getItem('authtoken')
const file = await RNFS.readFile(path, "base64");
let url = `${host}/api/docs/add?card=${value}&number=${myFileId}`;
console.log(url);
let imageData = {
uri: path,
type: 'image/jpg', //the mime type of the file
name: 'newFile'
}
let formData = new FormData();
formData.append('file', imageData);
const response = await fetch(url, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'authtoken': token,
'Content-Type': 'multipart/form-data'
},
body: formData
});
const output = await response.json();
console.log(output);
}
And while programming the server-side I tested my code as
But while calling API from react-native I was getting the below error:
Please help me in uploading file.
after you reciving the file as base64 you should convert it to a file and after that you can append it to your formData:
const base64File = await RNFS.readFile(path, "base64");
const blobResult = await fetch(base64File);
const file = new File([blobResult], "newFile",{ type: "image/jpg" });
const formData = new FormData();
formData.append('file', file);
Using raw HTML when I post a file to a flask server using the following I can access files from the flask request global:
<form id="uploadForm" action='upload_file' role="form" method="post" enctype=multipart/form-data>
<input type="file" id="file" name="file">
<input type=submit value=Upload>
</form>
In flask:
def post(self):
if 'file' in request.files:
....
When I try to do the same with Axios the flask request global is empty:
<form id="uploadForm" enctype="multipart/form-data" v-on:change="uploadFile">
<input type="file" id="file" name="file">
</form>
uploadFile: function (event) {
const file = event.target.files[0]
axios.post('upload_file', file, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
If I use the same uploadFile function above but remove the headers json from the axios.post method I get in the form key of my flask request object a csv list of string values (file is a .csv).
How can I get a file object sent via axios?
Add the file to a formData object, and set the Content-Type header to multipart/form-data.
var formData = new FormData();
var imagefile = document.querySelector('#file');
formData.append("image", imagefile.files[0]);
axios.post('upload_file', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
Sample application using Vue. Requires a backend server running on localhost to process the request:
var app = new Vue({
el: "#app",
data: {
file: ''
},
methods: {
submitFile() {
let formData = new FormData();
formData.append('file', this.file);
console.log('>> formData >> ', formData);
// You should have a server side REST API
axios.post('http://localhost:8080/restapi/fileupload',
formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function () {
console.log('SUCCESS!!');
})
.catch(function () {
console.log('FAILURE!!');
});
},
handleFileUpload() {
this.file = this.$refs.file.files[0];
console.log('>>>> 1st element in files array >>>> ', this.file);
}
}
});
https://codepen.io/pmarimuthu/pen/MqqaOE
If you don't want to use a FormData object (e.g. your API takes specific content-type signatures and multipart/formdata isn't one of them) then you can do this instead:
uploadFile: function (event) {
const file = event.target.files[0]
axios.post('upload_file', file, {
headers: {
'Content-Type': file.type
}
})
}
Sharing my experience with React & HTML input
Define input field
<input type="file" onChange={onChange} accept ="image/*"/>
Define onChange listener
const onChange = (e) => {
let url = "https://<server-url>/api/upload";
let file = e.target.files[0];
uploadFile(url, file);
};
const uploadFile = (url, file) => {
let formData = new FormData();
formData.append("file", file);
axios.post(url, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
}).then((response) => {
fnSuccess(response);
}).catch((error) => {
fnFail(error);
});
};
const fnSuccess = (response) => {
//Add success handling
};
const fnFail = (error) => {
//Add failed handling
};
This works for me, I hope helps to someone.
var frm = $('#frm');
let formData = new FormData(frm[0]);
axios.post('your-url', formData)
.then(res => {
console.log({res});
}).catch(err => {
console.error({err});
});
this is my way:
var formData = new FormData(formElement);
// formData.append("image", imgFile.files[0]);
const res = await axios.post(
"link-handle",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
How to post file using an object in memory (like a JSON object):
import axios from 'axios';
import * as FormData from 'form-data'
async function sendData(jsonData){
// const payload = JSON.stringify({ hello: 'world'});
const payload = JSON.stringify(jsonData);
const bufferObject = Buffer.from(payload, 'utf-8');
const file = new FormData();
file.append('upload_file', bufferObject, "b.json");
const response = await axios.post(
lovelyURL,
file,
headers: file.getHeaders()
).toPromise();
console.log(response?.data);
}
There is an issue with Axios version 0.25.0 > to 0.27.2 where FormData object in a PUT request is not handled correctly if you have appended more than one field but is fine with one field containing a file, POST works fine.
Also Axios 0.25.0+ automatically sets the correct headers so there is no need to specify Content-Type.
For me the error was the actual parameter name in my controller... Took me a while to figure out, perhaps it will help someone. Im using Next.js / .Net 6
Client:
export const test = async (event: any) => {
const token = useAuthStore.getState().token;
console.log(event + 'the event')
if (token) {
const formData = new FormData();
formData.append("img", event);
const res = await axios.post(baseUrl + '/products/uploadproductimage', formData, {
headers: {
'Authorization': `bearer ${token}`
}
})
return res
}
return null
}
Server:
[HttpPost("uploadproductimage")]
public async Task<ActionResult> UploadProductImage([FromForm] IFormFile image)
{
return Ok();
}
Error here because server is expecting param "image" and not "img:
formData.append("img", event);
public async Task<ActionResult> UploadProductImage([FromForm] IFormFile image)
Hope you're having a good day. So I'm using Fetch API to upload files to server but I also want to include a string as JSON for my backend. But I'm unable to do so.
Right now this is what my upload-file function looks like. This works perfectly if I want to just upload a file and not include a string as JSON:
const uploadFile = ( e ) => {
const uploadedFile = e.target.files[ 0 ];
const formData = new FormData();
formData.append( 'data', uploadedFile );
const rawResponse = await fetch( '/upload-file', {
method: 'POST',
body: formData
});
};
uploadFile();
Getting the file in my NodeJS backend with req.files.data;
But if I include a string as JSON and try to upload file then I get undefined in my NodeJS backend. This is how it looks like with string as JSON:
const uploadFile = ( e ) => {
const uploadedFile = e.target.files[ 0 ];
const formData = new FormData();
const str = 'from client';
formData.append( 'data', uploadedFile );
const rawResponse = await fetch( '/upload-file', {
method: 'POST',
body: {
file: formData,
str
}
});
};
uploadFile();
Now if I want to get the file in my backend with:
req.files.file, I get undefined.
req.body, I get { 'object Object': '' }.
req.files.data, I get UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'data' of undefined.
req.body.data, I get undefined.
What is going wrong here?
you append it to FormData, not body
const uploadFile = ( e ) => {
const uploadedFile = e.target.files[ 0 ];
const formData = new FormData();
const str = 'from client';
formData.append( 'data', uploadedFile );
formData.append( 'str', str );
const rawResponse = await fetch( '/upload-file', {
method: 'POST',
body: formData
});
};
uploadFile();
Just append the value to the FormData object, same as you did with the file already
formData.append( 'str', '["some JSON here"]' );
And then keep sending just that object, body: formData