Hi I have a backend which receive a request with a picture and storage, I try it with postman and with the code below and works perfectly
var axios = require('axios')
var FormData = require('form-data')
var fs = require('fs')
var data = new FormData()
data.append('file', fs.createReadStream('index.png'))
console.log('HEADERS')
console.log(data.getHeaders())
let config = {
method: 'post',
url: 'http://localhost:5013/v1/business/honda/widget/test/',
headers: {
...data.getHeaders(),
},
data: data,
}
The problem is in my vue app I try to do it with the next code, I have 2 buttons with one load the image and the other to send it.
In the back end I have the follow error when try to pick 'file'
http: no such file
let imageData
//send the image to backend
function funtest() {
console.log('image')
const formData = new FormData()
const url = 'http://localhost:5013/v1/business/honda/widget/test/'
formData.append('file', imageData)
let config = {
method: 'post',
url: url,
headers: {
'Content-type': 'multipart/form-data',
},
data: formData,
}
axios(config)
.then((response) => {
console.log('RESPONSE')
console.log(response)
})
.catch((error) => {
console.log('ERROR')
console.log(error)
})
}
//function to read the image
function onImage(data) {
const reader = new FileReader()
reader.onload = (e) => {
imageData = e.target.result
console.log('imagen')
}
reader.readAsDataURL(data.target.files[0])
}
I think it's probably not reading the path to index.png file correctly here, fs.createReadStream('index.png')
Consider using path like this
const path = require('path');
const filePath = path.join(__dirname, 'index.png');
data.append('file', fs.createReadStream(filePath))
NB: This is just a quick and dirty suggestion, and it's not guaranteed to work but it's definitely worth a shot
Related
TDLR: Using s3 presigned post url to upload file to s3. Works fine on the browser but fails on the server.
I have a simple lambda function that generates presigned post url that can be consumed either in the browser or in the server.
During testing I noticed that the upload works fine one the browser but fails if I try to upload a file from a server even tho the code is identical.
The error i get is:
You must provide the Content-Length HTTP header
Detailed error:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>MissingContentLength</Code>
<Message>You must provide the Content-Length HTTP header.</Message>
<RequestId>JP75YMFARK0G3X5Z</RequestId>
<HostId>toHsKmxmVYYAtac94cQoy8wXoregKG3PNBm97c3gQewEmKxLggcumTAP882T/pJNWx/lxRgH98A=</HostId>
</Error>
Request failed with status code 411
I checked online and found many threads about this issue but unfortunately not a single suggestion helped me.
Code I am running in the server
const axios = require('axios');
const { createReadStream, readFileSync } = require('fs');
const FormData = require('form-data');
const getPostPresignedUrl = async () => {
var config = {
method: 'post',
url: LAMBDA_GET_URL,
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify({
key: 'test-2.jpg',
fileType: 'image/jpeg',
}),
};
const {
data: { data },
} = await axios(config);
return data;
};
const uploadFileToS3 = async (fields, url) => {
const formData = new FormData();
Object.entries(fields).map(([key, value]) => {
formData.append(key, value);
});
const file = createReadStream('./test-1.jpg');
formData.append('file', file);
try {
const { data } = await axios({
url,
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data: formData,
});
} catch (error) {
if (error instanceof axios.AxiosError) {
console.log(error.response.data);
}
console.log(error.message);
}
};
const init = async () => {
const { fields, url } = await getPostPresignedUrl();
await uploadFileToS3(fields, url);
};
init();
Code I am running in the browser:
const form = document.getElementById('form');
const input = document.getElementById('file');
const getPostPresignedUrl = async (name) => {
var config = {
method: 'post',
url: LAMBDA_GET_URL,
headers: {
'Content-Type': 'application/json',
},
data: JSON.stringify({
key: name,
fileType: 'image/jpeg',
}),
};
const {
data: { data },
} = await axios(config);
return data;
};
const uploadFileToS3 = async (fields, url, file) => {
const formData = new FormData();
Object.entries(fields).map(([key, value]) => {
formData.append(key, value);
});
formData.append('file', file);
try {
const { data } = await axios({
url,
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data: formData,
});
} catch (error) {
if (error instanceof axios.AxiosError) {
console.log(error.response.data);
}
console.log(error.message);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
const file = input.files[0];
const data = await getPostPresignedUrl(file.name);
await uploadFileToS3(data.fields, data.url, file);
};
form.onsubmit = handleSubmit;
I am trying to send text and formData in one fetch call to a Node.js backend using multer.
I can send formData on its own with no issues, but when I try and add text, the api call stays 'pending'.
Here is my fetch call that works just with formData:
const handleImage = async (e) => {
var formData = new FormData();
let file = e.target.files[0];
formData.append("image", file);
try {
const upload = await fetch(
`${process.env.NEXT_PUBLIC_SERVER_API}/uploadImage`,
{
method: "POST",
body: formData,
}
);
} catch (e) {
console.log("Something went wrong!");
}
};
Here is the same fetch call with text added that does not work:
const handleImage = async (e) => {
var formData = new FormData();
let file = e.target.files[0];
formData.append("image", file);
try {
const upload = await fetch(
`${process.env.NEXT_PUBLIC_SERVER_API}/uploadImage`,
{
method: "POST",
body: {formData, userId}
}
);
} catch (e) {
console.log("Something went wrong!");
}
};
It also doesn't work if I try and user JSON.stringify().
I do believe that you can't send a formData and json body at the same time (maybe there is a way somehow i don't know)
because multer will just take the file from formdata and the other property will be set to req.body so if you want to send userId you can try
const handleImage = async (e) => {
var formData = new FormData();
let file = e.target.files[0];
formData.append("image", file);
formData.append("userId", userId);
try {
const upload = await fetch(
`${process.env.NEXT_PUBLIC_SERVER_API}/uploadImage`,
{
method: "POST",
body: formData,
}
);
} catch (e) {
console.log("Something went wrong!");
}
};
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)
I think I have now tried everything and read every question on this matter, but still I can't make it to work..
Cart.vue
<template>
<div>
<h1>CART</h1>
<img :src="imgSrc" style="width:600px; height: 600px">
</div>
</template>
Cart.vue mounted()
mounted(){
const qr = firebase.functions().httpsCallable('sendPaymentRequest')
qr()
.then(res => {
const blob = new Blob([res.data], {type: 'image/jpg'})
console.log(blob);
const url = (window.URL || window.webkitURL).createObjectURL(blob)
console.log(url);
this.imgSrc = url;
})
Firebase functions
exports.sendPaymentRequest = functions.https.onCall((data, context) => {
const qr = async () => {
try {
const json = {
token: "umP7Eg2HT_OUId8Mc0FHPCxhX3Hkh4qI",
size: "300",
format: "jpg",
border: "0"
}
const response = await axios.post('https://mpc.getswish.net/qrg-swish/api/v1/commerce', json)
console.log('status', response.status)
if(response.status !== 200){throw new Error ('Error requesting QR code')}
return response.data
} catch (e){
console.log('error', e)
return e
}
}
return qr();
})
In my 2 console logs in the mounted() hook - the Blob and the URL - I get:
Looking pretty all right? There seem to be a Blob? And a URL?
however:
... sooo I tried changing the mounted() to
const qr = firebase.functions().httpsCallable('sendPaymentRequest')
qr()
.then(res => {
const self = this;
const blob = new Blob([res.data], {type: 'image/jpg'})
const reader = new FileReader();
reader.onloadend = function() {
self.imgSrc = reader.result
};
reader.readAsDataURL(blob);
})
.catch(e => console.log(e))
which also seem to work but.. well it's not.. Now I got a nice little base64-encoded string to my image instead of URL:
But still no image..
So I tried some other stuff I found while reading all of Internet.. moving from a callable function to onRequest function etc.. When I'm doing the exact same request with Postman I'm getting a fine QR code in the response..
If I'm loggin the response.headers in firebase functions I'm seeing
'content-type': 'image/jpeg',
'content-length': '31476',
So on the server I'm getting an image.. which I'm sending with return response.data
response.data being:
����JFIF��C
$.' ",#(7),01444'9=82<.342��C
2!!22222222222222222222222222222222222222222222222222�,,"��
and so on..
And that's where I'm at.. I'm getting .. frustrated.
Does anyone on here see what I'm doing wrong??
EDIT 1
for anyone running into this in the future - as #Kaiido points out on client I have to add
...
responseType: "blob"
}
but also on server, with firebase you need to move from
functions.https.onCall(async (data, context) => {
to
functions.https.onRequest(async (req, res) => {
call it on client with:
axios({
method: 'get',
url: 'http://localhost:5001/swook-4f328/us-central1/retrieveQr',
responseType: 'blob',
})
.then(async res => {
const url = (window.URL || window.webkitURL).createObjectURL(res.data)
this.imgSrc = url;
})
.catch(e => e)
and on server instead of axios use request (for some reason, but this works.. no idea why, but solves problem for now though I would be curious to why and I prefer axios to request)
works
const json = {
token: "umP7Eg2HT_OUId8Mc0FHPCxhX3Hkh4qI",
size: "300",
format: "png",
border: "0"
}
var requestSettings = {
url: 'https://mpc.getswish.net/qrg-swish/api/v1/commerce',
method: 'POST',
encoding: null,
json: true,
'content-type': 'application/json',
body: json,
};
request(requestSettings, (error, response, body) => {
res.set('Content-Type', 'image/png');
res.header({"Access-Control-Allow-Origin": "*"});
res.send(body);
});
does not work
const json = {
token: "umP7Eg2HT_OUId8Mc0FHPCxhX3Hkh4qI",
size: "300",
format: "png",
border: "0"
}
const response = await axios.post('https://mpc.getswish.net/qrg-swish/api/v1/commerce', json)
if(response.status !== 200){throw new Error ('Error requesting QR code')}
res.header({"Access-Control-Allow-Origin": "*"}).writeHead(200, {'Content-Type': 'image/png'}).end(response.data)
// neither this works:
// res.header({"Access-Control-Allow-Origin": "*"}).status(200).send(response.data)
You are receiving an utf8 encoded text, some bytes from the binary response have been mangled.
When doing your request, add an extra
...
responseType: "blob"
}
option to your axios request, this will ensure the binary data is not read as text but preserved correctly.
Plus now you don't need to build the Blob yourself, it is already one in response.data.
I have an API that receives uploads of APP files and images
To send from APP to the API I use fetch
const data = new FormData();
let i = 0;
export const dataPush = (fileUri, fileType, fileName) => {
data.append('file'+i, {
uri: fileUri,
type: fileType,
name: fileName
});
i++;
};
export const uploadFiles = () => {
console.log(data);
fetch('http://192.168.0.23/apiapp/public/api/annex', {
method: 'post',
body: data
}).then(res => {
console.log(res)
});
}
But I'd like to send in the same request data obtained from a form
But I did not find a way to do it, always or just send the data, or just send the files
Is it possible to send everything in the same request? And if possible, how?
You just append whatever data that you desire that isn't file data to the FormData object.
data.append("not_a_file", "This is a string");
I did so based on Quentin's response and it worked
const formData = new FormData();
const i = 0;
export const filePush = (fileUri, fileType, fileName) => {
formData.append('file'+i, {
uri: fileUri,
type: fileType,
name: fileName
});
i++;
};
export const dataPush = (name, content) => {
formData.append(name, content);
};
export const uploadFiles = () => {
fetch('http://192.168.0.23/apiapp/public/api/annex', {
method: 'post',
body: formData
}).then(res => {
console.log(res._bodyText)
}).catch(error => {
console.log(error.message)
});
}