I am using the express server as an API gateway to my microservices, in which I am processing images and store them in a database.
the problem summarizes as follows:
in the express server, I need to forward the image received in a post request from the client and forward it to the web service that can handle image processing.
It turns out that I need to parse the image before I can forward it to the next API, the error I am getting is
Unexpected token - in JSON at position 0
Her is my client that sends the image to the express server(my gateway)
function uploadWithFormData() {
const formData = new FormData();
formData.append("file", file);
fetch('/upload', {
method: "POST",
headers: {
"content-type": "application/json",
},
body: data
}).then(handleResponse)
.catch(handleError);
uploadImage(formData);
}
and this is my express serve that should handle the request and forward it to another web service
app.post("/upload", function (req, res) {
const form = formidable({ multiples: true });
form.parse(req, (err, fields, files) => {
const response = res.json({ fields, files });
let formData = new FormData();
formData.append('file', fs.createReadStream(req.files.file.path), req.files.file.name);
uploadImageAPI(response).then(
res.status(201).send()
).cache(res.status(412).send())
});
I have tried to make some consol.log inside the function to see the req but the request fails before it enters the function, because express could not pars the image.
I saw some people doing this using the multer library but I could not mange that in my case
any suggestions?
You're posting a FormData object, which will be converted to multipart/form-data.
You aren't posting JSON.
You have set "content-type": "application/json" so you claim you are posting JSON.
The server tries to decode the "JSON", fails, and throws the error.
Don't override the Content-Type header. fetch will generate a suitable one automatically when you pass it a FormData object.
Do use a body parsing module which supports multipart requests. body-parser does not, but its documentation links to several that do.
Part of the solution is to remove the JSON header as #Quentin said in his answer. However, the code that solved the problem is as following :
const multer = require('multer');
const upload = multer();
app.post("/upload", upload.any(), (req, res) => {
const { headers, files } = req;
const { buffer, originalname: filename } = files[0];
headers['Content-Type'] = 'multipart/form-data';
const formFile = new FormData();
formFile.append('file', buffer, { filename });
fetch(URL, {
method: "POST",
body: data
}).then(handleResponse)
.catch(handleError);
});
Related
In my web application I need to upload the files.I have used axios to send file as requests from client to server(api) with form data append and also necessary headers.Now the ask is that I have to pass the file from server(api) to another endpoint api.Since I am using next js,both client and server api handling is done from my side
The client request is fine with binary data and all.But I don't know how to pass it from the server(api) to another server(api) using axios with the request got from client.As this is a very unusual requirement, there are no available solutions in online.
I would be glad if I can have some solution or an implementation
import axios from 'axios';
async function sendFileToEndpoint(file) {
try {
const formData = new FormData();
formData.append('file', file);
const response = await axios.request({
method: 'POST',
url: 'http://endpoint-api.com/upload',
headers: {
'Content-Type': 'multipart/form-data',
},
data: formData,
});
console.log(response);
} catch (error) {
console.error(error);
}
}
In this example, the sendFileToEndpoint function accepts a file as an argument and sends it to the endpoint API using a POST request with multipart/form-data content type. You can adjust the request options as needed to meet the requirements of the endpoint API.
It's up to you to manage the server.
I'm trying to build a form to upload an image and later set it as a Featured Image for a post that will be created after the upload is complete.
I'm using WordPress as backend and Next.js as front-end.
My code sort of works. The image gets uploaded and I get a response with a generated attachment ID. It even shows the correct KB size on the server, but it doesn't generate the WordPress image sizes. When downloading the image from the server, its blank. Appears to be corrupted.
For security reasons I need to send the form-data to a custom Next.js API endpoint where I can use JWT authentication token to authorize the command.
Form.js
const handleFormSubmit = async (data) => {
const formData = new FormData()
formData.append('file', data.locationImage[0])
const addImage = await fetch('/api/add-image', {
method: 'POST',
body: formData,
headers: new Headers({
'Content-Type': 'multipart/form-data',
'Content-Disposition': `attachment; filename=${data.locationImage[0].name}`,
}),
})
const imageID = await addImage.json()
console.log(imageID)
}
pages/api/add-image.js
import { fetchRefreshToken } from 'graphql/api'
export default async function handler(req, res) {
const file = req.body
const getToken = await fetchRefreshToken()
const refreshToken = getToken?.login.refreshToken
const uploadImage = await fetch(
'https://mywordpresswebsite.com/wp-json/wp/v2/media',
{
body: file,
method: 'POST',
headers: new Headers({
Authorization: `Bearer ${refreshToken}`,
'Content-Disposition': `${req.headers['content-disposition']}`,
}),
},
)
const uploadedImage = await uploadImage.json()
res.status(200).json({ result: uploadedImage })
}
When I add the Authorization to Form.js and make the request without sending the data to Next.js API Endpoint, the image gets uploaded, WordPress generates custom sizes and its there. But this seems to be unsecure and not a solution.
So, my question is, what is causing this and how can I upload images securely using Next.js?
Am I missing a setting on the Headers object? Does the formData become a different data type when it goes through the Next.js API? Is it a Next.js problem?
Someone could help me, I'm trying to send a photo and caption into a group, but isn't working!
I'd like send the photo that I recieve as base64 and send to a facebook group api.
What I'm doing? I got the base64 then convert into buffer and write it on a local disk, then I read it into a formdata.
I load the data into a form this way =>
const form = new FormData();
const fileContent = Buffer.from(url as any, 'base64');
fs.writeFile('../tmp', fileContent, (err) => {
if (err) return console.log(err)
})
form.append('groupId', groupId)
form.append('caption', caption)
form.append('image ', fs.createReadStream('../tmp'))
In below is the axios configurations and request
await client.post(`${config.facebook.BASE_URL}/${groupId}/photos`, form, {
headers: {
...form.getHeaders(),
Authorization: `OAuth ${config.facebook.token}`,
'content-type': 'multipart/form-data',
file_type: "image/jpeg",
}
})
Note: This way I got the Error: Request failed with status code 500
I already resolved this although I changed de way that the file came to me, instead of receiving the image in base64 I'm receiving a signed url from google storage, and on the form.append, it's necessary to pass the name and the extension of the file, like this => form.append('source', image, 'file.jpg');
of course together with the other params and axios' configurations
I am trying to get this working now for days -.-
Using a simple NodeJS express server, I want to upload an image to a Django instance through Post request, but I just can't figure out, how to prepare the request and embed the file.
Later I would like to post the image, created from a canvas on the client side,
but for testing I was trying to just upload an existing image from the nodeJS server.
app.post('/images', function(req, res) {
const filename = "Download.png"; // existing local file on server
// using formData to create a multipart/form-data content-type
let formData = new FormData();
let buffer = fs.readFileSync(filename);
formData.append("data", buffer); // appending the file a buffer, alternatively could read as utf-8 string and append as text
formData.append('name', 'var name here'); // someone told me, I need to specify a name
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
axios.post("http://django:8000/images/", formData, config)
.then(response => {
console.log("success!"); // never happens :(
})
.catch(error => {
console.log(error.response.data); // no file was submitted
});
});
What am I doing wrong or did I just miss something?
EDIT
I just found a nice snippet with a slighlty other approach on the npm form-data page, on the very bottom (npmjs.com/package/form-data):
const filename = "Download.png"; // existing local file on server
let formData = new FormData();
let stream = fs.createReadStream(filename);
formData.append('data', stream)
let formHeaders = formData.getHeaders()
axios.post('http://django:8000/images/', formData, {
headers: {
...formHeaders,
},
})
.then(response => {
console.log("success!"); // never happens :(
})
.catch(error => {
console.log(error.response.data); // no file was submitted
});
sadly, this doesn't change anything :( I still receive only Bad Request: No file was submitted
I don't really have much Django code just a basic setup using the rest_framework with an image model:
class Image(models.Model):
data = models.ImageField(upload_to='images/')
def __str__(self):
return "Image Resource"
which are also registered in the admin.py,
a serializer:
from .models import Image
class ImageSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Image
fields = ('id', 'data')
using automatic URL routing.
I wrote a simple test script and put the same image on the django server, to verify that image uploads works, and it does:
import requests
url = "http://127.0.0.1:8000/images/"
file = {'data': open('Download.png', 'rb')}
response = requests.post(url, files=file)
print(response.status_code) # 201
I had a similar problem: I used the same Django endpoint to upload a file using axios 1) from the client side and 2) from the server side. From the client side it worked without any problem, but from the server side, the request body was always empty.
My solution was to use the following code:
const fileBuffer = await readFile(file.filepath)
const formData = new FormData()
formData.append('file', fileBuffer, file.originalFilename)
const response = await fetch(
urlJoin(BACKEND_URL),
{
method: 'POST',
body: formData,
headers: {
...formData.getHeaders(),
},
}
)
A few relevant references that I found useful:
This blog post, even though it seems the author manages to send form data from the server side using axios, I did not manage to reproduce it on my case.
This issue report in the axio repository, where one comment suggests to use fetch.
In your node.js express server instead of adding the image to the form data, try directly sending the stream in the API post.
const filename = "Download.png"; // existing local file on server
//let formData = new FormData();
let stream = fs.createReadStream(filename);
//formData.append('data', stream)
let formHeaders = formData.getHeaders()
axios.post('http://django:8000/images/', stream, {
headers: {
...formHeaders,
},
})
.then(response => {
console.log("success!"); // never happens :(
})
.catch(error => {
console.log(error.response.data); // no file was submitted
});
I still didn't manage to get this working with axios so I tried another package for sending files as post requests, namely unirest, which worked out of the box for me.
Also it is smaller, requires less code and does everything I needed:
const filename = "Download.png"; // existing local file on server
unirest
.post(url)
.attach('data', filename) // reads directly from local file
//.attach('data', fs.createReadStream(filename)) // creates a read stream
//.attach('data', fs.readFileSync(filename)) // 400 - The submitted data was not a file. Check the encoding type on the form. -> maybe check encoding?
.then(function (response) {
console.log(response.body) // 201
})
.catch((error) => console.log(error.response.data));
If I have some spare time in the future I may look into what was wrong with my axios implementation or someone does know a solution pls let me know :D
I'm trying to POST data to my server using the fetch API and every time I do I end up with an empty body.
As far as I can tell from the fetch documentation, to post data you need to pass an object with a method key set to "POST" and a body key with a string or FormData object (or some others).
Here's a simple example that is just not working as I would expect:
var formData = new FormData();
formData.append("test", "woohoo");
formData.append("foo", "bar")
// prints out contents of formData to show that it has contents!
formData.forEach(function(key, value) {
console.log(`${key}: ${value}`);
});
fetch("/fetch", {
method: "POST",
body: formData
}).then(function(response) {
return response.json();
}).then(function(json) {
// Logs empty object
console.log(json);
}).catch(function(err) {
console.log(err);
});
I also have an express server on the back of this that logs the request body and echoes it in the response.
app.post("/fetch", function(req, res) {
// Logs empty object :(
console.log(req.body);
res.send(JSON.stringify(req.body));
});
I add the formData as the body of the fetch request, but nothing ever seems to go through.
Please find my test files in this gist here and help!
This is because your node server isn't understanding mulitpart forms correctly (which is what FormData) will send by default.
You can use node-muliparty on your server you can read the form easily without touching the frontend code.
Something like:
var multiparty = require('multiparty');
var app = express();
app.post("/fetch", function(req, res) {
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
console.log(fields);
res.send(JSON.stringify(fields));
});
});
in your index.js will show you it working correctly. There might be a way to make this work with body-parser but I don't know how myself.