I am creating a nodejs script to upload an image to twitter then tweet it. My tweeting code works, authentication is working and I can tweet (you can see a resemblance of my auth in this post, although it's been refactored a little). I'm using the same authentication approach for the media upload and I get { errors: [ { message: 'Could not authenticate you', code: 32 } ] }
From what I've seen, and what Twitter has shared, this could be from a malformed request. Below is my code...
const fetch = require('node-fetch');
const FormData = require('form-data');
console.log('getting image')
const imgResp = await fetch(newTweet.img)
const img = Buffer.from(await imgResp.arrayBuffer()).toString('base64') // img converts to a real image using online converters fine
const Authorization = getTwitterAuthHeader('https://upload.twitter.com/1.1/media/upload.json') // works when creating tweets
const twImg = await fetch('https://upload.twitter.com/1.1/media/upload.json', {
method: 'POST',
body: {media_data: img}, // other variations tried
headers: {
Authorization,
'content-type': "application/x-www-form-urlencoded",
'accept': "application/json"
}
})
const twImgResp = await twImg.json();
console.log(twImgResp)
my log looks like
getting image
{ errors: [ { message: 'Could not authenticate you', code: 32 } ] }
As noted, I tried using the formData like
const formData = new FormData()
formData.append('media_data', img)
const twImg = await fetch('https://upload.twitter.com/1.1/media/upload.json', {
method: 'POST',
body: formData
I've tried media instead of media_data as well.
I'm trying to upload on image on the Prestashop API with form-data/axios.
For that, i just need to send a post request with the images joined in an "image" parameter.
I did this simple node.js script (mine is much more complicated but i simplified for here):
const FormData = require("form-data");
const fs = require("fs");
const axios = require("axios");
// Read the image file into a buffer
const imageBuffer = fs.readFileSync("image.jpg");
// Create a FormData object
const form = new FormData();
// Append the image buffer to the form data
form.append("image", imageBuffer);
// Make an HTTP POST request to the PrestaShop API
axios({
method: "POST",
url: "https://XXX/api/images/products/15924",
data: form, // set the request body using the data field
params: {
ws_key: "XXX",
},
headers: {
"Content-Type": "multipart/form-data; boundary=" + form.getBoundary(),
},
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.error(error.response.data);
});
But i get this answer:
<message><![CDATA[Please set an "image" parameter with image data for value]]></message>
I tried a LOT of things. With fs.createReadStream instead of formData, with http instead of axios, with a buffer or a file, etc ... and i alway end with this error.
I would be glad if someone has an idea :-)
Thx !
I finally succeed!
Two changed were needed:
Adding the filename in the append (thanks Dmitriy Mozgovoy for pointing that out in the comment)
Adding the size in the headers with "Content-Length": formData.getLengthSync()
Without these 2 missing elements, it seems that PHP received an empty $_FILES variable.
Final version:
const FormData = require("form-data");
const fs = require("fs");
const axios = require("axios");
// Read the image file into a buffer
const imageBuffer = fs.readFileSync("image.jpg");
// Create a FormData object
const form = new FormData();
// Append the image buffer to the form data
form.append("image", imageBuffer, "image.jpg");
// Make an HTTP POST request to the PrestaShop API
axios
.post("https://example.com/api/images/products/15924", form, {
params: {
ws_key: "EXAMPLE",
},
headers: {
...form.getHeaders(),
"Content-Length": form.getLengthSync(),
},
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.error(error.response.data);
});
I am trying to upload a file to url using axios post request. But i am getting 500 internal server error.
If the same request I tried from postman file gets uploaded with 200 status code.
I am not sure what should be the content-Type here.
here is my code.
const axios = require('axios')
var FormData = require('form-data');
var fs = require("fs");
var request = require('request');
const formData = {
file: fs.createReadStream('myfile.txt')
}
const config = {
headers: {
'Content-Type': 'multipart/form-data'
}
};
axios.post('myurl',formData,config)
.then((res) => {
console.log(`statusCode: ${res.status}`)
console.log(res.data)
})
.catch((error) => {
console.log(error)
})
Please check your frontend, when you click submit, do you call event.preventDefault();
const formData = {
file: fs.createReadStream('myfile.txt')
}
It's a plain javascript object, not a ready-to-use formData. You just forget to transform it into a real FormData object. Just check for the document of form-data module.
Also it seems that when you post a real FormData, the "Content-type" header is unnecessary as axios will automaticly handle it for you.
I am trying to send a FormData containing a pdf file from a node.js script to a server. The request fail with a status code 400 in response. In the server side, it seems that he do not consider the data as a pdf file, or maybe that that the whole data is undefined.
This is what my node.js script does : (I've tried other ways to send the request but without success)
import axios from "axios"
import fs from "fs-extra"
import FormData from 'form-data'
const formData = new FormData()
const diplomaBuffer = await fs.readFile(`../data/fake/foo`+i+`.pdf`)
formData.append("diploma", diplomaBuffer)
formData.append("diplomaId", 1)
axios({
method: 'post',
processData: false,
contentType: 'multipart/form-data',
cache: false,
url: 'http://localhost:8080/certificates',
data: formData,
config: { headers: formData.getHeaders() }
})
// const response = await axios
// .post(`http://localhost:8080/certificates`, {
// body: formData
// })
// const response = await axios
// .post(`http://localhost:8080/certificates`
// , formData
// , {headers: formData.getHeaders()})
This is the function called in the server side :
app.post('/certificates', async (req, res) => {
const files = req.files
if(!files || !files.diploma || !files.diplomaId) {
res.status(400).send(new Error("No file provided"))
}
if(files.diploma.mimetype !== "application/pdf") {
res.status(400).send(new Error("diploma is not a pdf"))
}
// operations made with the received pdf (not relevant for the question)
const certificateId = uniqid()
const outputDiploma = `data/diplomas/${certificateId}.pdf`
await Promise.all([
fs.writeFile(outputDiploma, files.diploma.data),
])
await pdf.sign(outputDiploma, `${sha("sha256").update(certificateId + diplomaSecret).digest("hex")} : ${date()}`)
const diplomaBuffer = await fs.readFile(outputDiploma)
const certificate_hash = verify_pdf.hashBuffer(diplomaBuffer)
const certificateRegistry = await app.bizNetworkConnection.getAssetRegistry("consortium.supchain.assets.Certificate")
const certificate = app.factory.newResource("consortium.supchain.assets", "Certificate", certificateId)
certificate.diploma = app.factory.newRelationship("consortium.supchain.assets", "Diploma", req.body.diplomaId)
certificate.hashInfo = certificate_hash
await app.registry.certificate.add(certificate)
res.status(200).send("ok")
})
I have an API endpoint that lets the client post their csv to our server then post it to someone else server. I have done our server part which save uploaded file to our server, but I can't get the other part done. I keep getting error { message: 'File not found', code: 400 } which may mean the file never reach the server. I'm using axios as an agent, does anyone know how to get this done? Thanks.
// file = uploaded file
const form_data = new FormData();
form_data.append("file", fs.createReadStream(file.path));
const request_config = {
method: "post",
url: url,
headers: {
"Authorization": "Bearer " + access_token,
"Content-Type": "multipart/form-data"
},
data: form_data
};
return axios(request_config);
Update
As axios doc states as below and the API I'm trying to call requires a file
// data is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', and 'PATCH'
// When no transformRequest is set, must be of one of the following types:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
Is there any way to make axios send a file as a whole? Thanks.
The 2 oldest answers did not work for me. This, however, did the trick:
const FormData = require('form-data'); // npm install --save form-data
const form = new FormData();
form.append('file', fs.createReadStream(file.path));
const request_config = {
headers: {
'Authorization': `Bearer ${access_token}`,
...form.getHeaders()
}
};
return axios.post(url, form, request_config);
form.getHeaders() returns an Object with the content-type as well as the boundary.
For example:
{ "content-type": "multipart/form-data; boundary=-------------------0123456789" }
I'm thinking the createReadStream is your issue because its async. try this.
Since createReadStream extends the event emitter, we can "listen" for when it finishes/ends.
var newFile = fs.createReadStream(file.path);
// personally I'd function out the inner body here and just call
// to the function and pass in the newFile
newFile.on('end', function() {
const form_data = new FormData();
form_data.append("file", newFile, "filename.ext");
const request_config = {
method: "post",
url: url,
headers: {
"Authorization": "Bearer " + access_token,
"Content-Type": "multipart/form-data"
},
data: form_data
};
return axios(request_config);
});
This is what you really need:
const form_data = new FormData();
form_data.append("file", fs.createReadStream(file.path));
const request_config = {
headers: {
"Authorization": "Bearer " + access_token,
"Content-Type": "multipart/form-data"
},
data: form_data
};
return axios
.post(url, form_data, request_config);
In my case, fs.createReadStream(file.path) did not work.
I had to use buffer instead.
const form = new FormData();
form.append('file', fs.readFileSync(filePath), fileName);
const config = {
headers: {
Authorization: `Bearer ${auth.access_token}`,
...form.getHeaders(),
},
};
axios.post(api, form.getBuffer(), config);
I have made an interceptor you can connect to axios to handle this case in node: axios-form-data. Any feedback would be welcome.
npm i axios-form-data
example:
import axiosFormData from 'axios-form-data';
import axios from 'axios';
// connect axiosFormData interceptor to axios
axios.interceptors.request.use(axiosFormData);
// send request with a file in it, it automatically becomes form-data
const response = await axios.request({
method: 'POST',
url: 'http://httpbin.org/post',
data: {
nonfile: 'Non-file value',
// if there is at least one streamable value, the interceptor wraps the data into FormData
file: createReadStream('somefile'),
},
});
// response should show "files" with file content, "form" with other values
// and multipart/form-data with random boundary as request header
console.log(response.data);
I had a same issue, I had a "pdf-creator-service" for generate PDF document from html.
I use mustache template engine for create HTML document - https://www.npmjs.com/package/mustache
Mustache.render function returns html as a string what do I need to do to pass it to the pdf-generator-service ? So lets see my suggestion bellow
//...
async function getPdfDoc(props: {foo: string, bar: string}): Promise<Buffer> {
const temlateFile = readFileSync(joinPath(process.cwd(), 'file.html'))
mustache.render(temlateFile, props)
const readableStream = this.getReadableStreamFromString(htmlString)
const formData = new FormData() // from 'form-data'
formData.append('file', options.file, { filename: options.fileName })
const formHeaders = formData.getHeaders()
return await axios.send<Buffer>(
{
method: 'POST',
url: 'https://pdf-generator-service-url/pdf',
data: formData,
headers: {
...formHeaders,
},
responseType: 'arraybuffer', // ! important
},
)
}
getReadableStreamFromString(str: string): Readable {
const bufferHtmlString = Buffer.from(str)
const readableStream = new Readable() // from 'stream'
readableStream._read = () => null // workaround error
readableStream.push(bufferHtmlString)
readableStream.push(null) // mark end of stream
return readableStream
}
For anyone who wants to upload files from their local filesystem (actually from anywhere with the right streams architecture) with axios and doesn't want to use any external packages (like form-data).
Just create a readable stream and plug it right into axios request function like so:
await axios.put(
url,
fs.createReadStream(path_to_file)
)
Axios accepts data argument of type Stream in node context.
Works fine for me at least in Node v.16.13.1 and with axios v.0.27.2