How to submit a multipart image - Error: Multipart: Boundary not found - javascript

I have a client side javascript sdk that submits an image to a server side node.js api that uses the multer library to parse the image.
However ive noticed if i set a header to be content-type multipart-formdata multer will throw an error saying
Error: Multipart: Boundary not found
async submitDocument(id, side, image) {
const url = this.API_URL + "/api/document";
let formData = new FormData();
formData.set("image", image, "front.jpg");
formData.set("side", side);
let headers = new Headers();
headers.set("content-type", "multipart/form-data");
headers.set("Authorization", "Bearer " + this.API_KEY);
const request = {
method: "POST",
body: formData,
headers: headers,
};
try {
const response = await fetch(url, request);
const data = await response.json();
return data;
} catch (err) {
throw err;
}
}

As the error message says, a multipart/form-data content-type requires a boundary parameter.
Don't set the Content-Type yourself. Allow the browser to generate it from the formData object.

npm module connect-multiparty may helpful to you. From server-side node application.
server.js
const multipart = require('connect-multiparty');
const multipartMiddleware = multipart();
router.post('/api/document', multipartMiddleware);
router.post('/api/document', (req, res) => {
console.log(req.files)
})
post-man api test sample -
https://i.stack.imgur.com/vxBpz.png

Related

HTTP Post request not sending body or param data from ionic

HTTP post request is not sending body or param data to the server
Forgive me if this turns out to be a duplicate question. I've looked at several similar questions on stack overflow, but none of them have solved my problem. Also tried using a GET request instead of a POST request, but body data is still not sending.
Client side code:
// ionic code
homeUrl: string = 'http://localhost:80';
let obj = {"name": "Guest"};
let response = this.httpClient.post(this.homeUrl + '/admin-signup', JSON.stringify(obj));
response.subscribe(data => {
console.log('response: ', data);
//TODO: handle HTTP errors
});
Server side code:
server.post('/admin-signup', (req, res) => {
console.log('sign')
console.log(req.body);
// TODO: Process request
res
.status(200)
.send(JSON.parse('{"message": "Hello, signup!"}'))
.end();
});
First of all, import http client
import { HttpClient, HttpHeaders } from '#angular/common/http';
Then do the following
const header = new HttpHeaders({
'Content-Type': 'application/json',
Accept: 'application/json'
//api token (if need)
});
const options = {
headers: header
}
let response = this.httpClient.post(this.homeUrl + '/admin-signup', obj, options);
response.toPromise().then(data => {
console.log('response: ', data);
//TODO: handle HTTP errors
}).catch((err) =>{
console.log('error', err);
});
Hope it solve your problem.
I'm not familiar with ionic
but I'm guessing its a cors issue
can you try use cors?
const cors = require('cors');
app.use(cors());

Sending an image with axios to Node.js and then using it further

I am trying to upload an image from the front-end, post it with axios to back-end (node.js) and then from there post it again to the GroupMe image service.
The main thing is to avoid using the API token in the front-end and so I was trying to first send a request to the back-end and then send the actual API request to the GroupMe image service which expects to get FormData of an image and sends back converted image URL.
I have tried to send FormData directly to the GroupMe image service from the front-end and everything works fine. However, in order to do so, I had to store the token in the front-end, which is not a good idea I believe.
The working code below:
let config = {
headers : {
'X-Access-Token': myToken,
'Content-Type' : 'multipart/form-data'
}
}
let fd = new FormData()
fd.append('name', 'image')
fd.append('file', fileToUpload)
axios.post'(https://image.groupme.com/pictures', fd, config)
.then((response)=>{
console.log(response)
})
.catch(err =>{
console.log(err.response)
})
What I need to happen instead is to send the request to the back-end like so:
axios.post(process.env.baseUrl+'/messengerRequests/upload-file/', fd, config)
.then((response)=>{
console.log(response)
})
.catch(err =>{
console.log(err.response)
})
And now in the back-end somehow be able to get that FormData and then create another post request to the GroupMe image service as I initially did in the front-end.
sendMessage: async(req, res) => {
axios.post('https://image.groupme.com/pictures', ???, config)
.then((response)=>{
res.send(response)
})
.catch(err =>{
console.log(err.response)
})
}
I do not know where it appears in the axios request. There is nothing in the req.body or req.params so I am not able to simply pass it further for the next post.
Is there a way somehow pass this FormData again?
Or maybe there is a way to safely use the token in the frond-end?
So, it should be relatively straightforward to post the image to GroupMe using Node.js and Express / Multer / Request. I've gone for Request rather than Axios on the backend since I'm more familiar with the API, but it's the same difference really.
Node.js Code (index.js)
const request = require("request");
const express = require("express");
const multer = require("multer");
const upload = multer();
const app = express();
const port = 3000;
const myToken = "" // Your API token goes here.
app.use(express.static("./"));
/* Here we take the image from the client and pass it on to GroupMe */
app.post("/uploadFile", upload.any(), (req, res) => {
sendImageToGroupMe(req, res);
});
function sendImageToGroupMe(req, res) {
const options = {
uri: "https://image.groupme.com/pictures",
body: req.files[0].buffer,
method: "POST",
headers: {
"X-Access-Token" : myToken
}
}
request(options, (err, response, body) => {
console.log("Request complete: Response: ", body);
if (err) {
console.error("Request err: ", err);
res.status(500).send("Upload failed: ", err.message);
} else {
res.status(201).send("Upload successful: GroupMe response: " + body);
}
});
}
app.listen(port);
Client side
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
function uploadFile() {
var fileToUpload = document.querySelector('input[type=file]').files[0];
let config = {
headers : {
'Content-Type' : 'multipart/form-data'
}
}
let fd = new FormData()
fd.append('name', 'image')
fd.append('file', fileToUpload)
axios.post('http://localhost:3000/uploadFile', fd, config)
.then((response)=>{
console.log("Image posted successfully: ", response);
showOutput("Image posted successfully: " + response.data);
})
.catch(err =>{
console.error("Image post failed: ", err)
showOutput("Image post failed!");
})
}
function showOutput(html) {
document.getElementById("output").innerHTML = html;
}
</script>
</head>
<body style="margin:50px">
<input type="file" onchange="uploadFile()"><br>
<p id="output"></p>
</body>
</html>
All files go in the same directory. You can go to http://localhost:3000/ to test the index.html code, this will be served by the Node.js server as a static file.
I get a response like below from the GroupMe API:
{
"payload": {
"url": "https://i.groupme.com/157x168.png.940f20356cd048c98478da2b181ee971",
"picture_url": "https://i.groupme.com/157x168.png.940f20356cd048c98478da2b181ee971"
}
}
We'll serve locally on port 3000, so to start the server:
node index.js
If you are using Express, you will need something to process the FormData. I have used multer for something similar before. I had to save the files into local storage, then resend the file with axios.

Sending form data in nodejs using https.request

I am trying to post a request with my nodejs server to another server and then I have to save response in a file. I am using nodejs https.request module.
This is my request:
var formData = new FormData();
formData.append('first',3);
formData.append('second', '25');
formData.append('source_file', fs.createReadStream(sourcefile));
formData.append('source_image', fs.createReadStream(sourceimage));
var options = {
hostname: 'ip',
path: '/api/path',
method: 'POST'
}
var file = fs.createWriteStream("file.pdf");
var req = https.request(options, (response) => {
response.pipe(file);
console.log("File saved");
response.send("done")
});
req.on('error', (e) => {
console.error(e);
});
req.write(formData);
req.end();
But I am getting the error
First argument must be a string or Buffer
I tried sending my files using formData.toString() but on using this, error disappears but My files are not working and also I have sent data like this:
var formData = new FormData();
formData = {
first: 3,
second: '25',
source_file: fs.createReadStream(sourcefile),
source_image: fs.createReadStream(sourceimage)
};
How can I send my files to other server using this request.
Thanks
I assume you are using form-data.
To fix the First argument must be a string or Buffer error replace:
req.write(formData);
req.end();
with
formData.pipe(req);
(formData behaves like a Node.js ReadableStream)
You should also add headers to your request:
var options = {
hostname: 'ip',
path: '/api/path',
method: 'POST',
headers: formData.getHeaders()
}
Source: https://github.com/form-data/form-data#alternative-submission-methods
I once faced an issue similar to this. I resolved it using the form-data package available on NPM here with the axios package here
the snippet below worked for me
const FormData = require("form-data");
const axios = require("axios");
const form = new FormData();
form.append("first", 3);
// other data should go here
form.append("file", fs.createReadStream("filePath"));
axios({
method: "post",
url: "url",
data: form,
headers: { ...form.getHeaders() }
});
You can use node inbuilt body-parser module to parse the form data into JSON and
you have to use
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true }));
And when you do req.body then it will your form data into an object form.

Upload file to S3 with PUT and form-data

I am trying to upload an image file (jpeg) to AWS S3 via the PUT interface, and I am getting the error SignatureDoesNotMatch.
On my server, I have an Express node.js app with an endpoint to create a signed url.
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const config = require('./config');
// Load the AWS SDK for Node.js
const AWS = require('aws-sdk');
AWS.config.update({
accessKeyId: config.AWS_ACCESS_KEY_ID,
secretAccessKey: config.AWS_SECRET_ACCESS_KEY,
region: 'us-east-1'
});
const s3 = new AWS.S3();
const app = express();
const awsS3Router = express.Router();
// parse application/json
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
// AWS S3 REST endpoints
awsS3Router.get('/getImageDrop', function(req, res) {
if(!req.query.filename) {
res.status(400).send('Request query is empty!');
}
const s3Params = {
Bucket: config.S3_BUCKET,
ContentType: 'image/jpeg',
ACL: 'public-read',
Key: req.query.filename,
Expires: 6000
};
s3.getSignedUrl('putObject', s3Params, function(err, data) {
if(err){
console.error('ERROR: ' + err);
return res.end();
}
const returnData = {
signedRequest: data,
url: 'https://' + config.S3_BUCKET + '.s3.amazonaws.com/' + req.query.filename
};
app.locals.s3SignedUrl = returnData.signedRequest;
res.write(JSON.stringify(returnData));
res.end();
});
});
app.use('/aws/s3', awsS3Router);
module.exports = app;
On the client side, I can call this endpoint and get a signed S3 url back. The response url format is:
https://[bucket name].s3.amazonaws.com/878CF5A4-D013-435F-BF7D-F45AB69E580F.jpg?AWSAccessKeyId=[AWS access key]&Content-Type=image%2Fjpeg&Expires=1521244920&Signature=[Signature]&x-amz-acl=public-read
The client code has a function to upload the file to the signed S3 url.
async uploadImageToS3BucketAsync(imageFileUri, fileSize, signedUrl) {
const fileName = PathParse(imageFileUri).base;
let form = new FormData();
form.append('files[0]', {
'uri': imageFileUri,
'name': fileName,
'type': 'image/jpeg'
});
//form.append('photo', imageFileUri);
console.info('INFO: PUT ' + signedUrl.signedRequest + ': Request: ' + JSON.stringify(form));
return fetch((signedUrl.signedRequest), {
method: 'PUT',
headers: { 'Content-Type': 'image/jpeg', 'Content-Length': fileSize },
body: form
})
.then(function(res) {
if (res.ok) {
console.info('INFO: PUT ' + JSON.stringify(signedUrl) + ': Response: ' + JSON.stringify(res));
return res.json();
} else {
console.error('Failed to upload image to S3 bucket!');
console.error('ERROR: ' + JSON.stringify(res));
alert('Failed to upload image to S3 bucket!!');
}
})
.catch(function(err) {
console.error('ERROR: Request failed', err);
});
}
Unfortunately, the upload fails systematically with a 403 error:
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
I am guessing I am missing something in the request headers of the PUT call, but I am not sure what it is. Has anyone found a solution for this in node.js?
The trick is to retrieve the file from the FormData instance after appending it.
const formData = new FormData();
formData.append('File', selectedFile);
fetch(
presignedPutUrl,
{
method: 'PUT',
body: formData.get("File"),
}
)
Thanks #[Michael - sqlbot] for the comment that got me here.
You don't use PUT with a form structure. PUT expects the raw binary body.

Unexpected token - while parsing json request

I have two node servers and I am trying to send files between them using a rest api. However when I am sending the data I get a "Unexpected token -"on the receiving server. On the sender I get an [Error: write after end].
My router code:
var express = require('express');
var multer = require('multer');
var path = require('path');
var Router = express.Router;
const MODULES_PACKAGES_UPLOAD_DIR = path.resolve('/tmp');
module.exports = function() {
var router = new Router();
var storage = multer.diskStorage({
destination: function(req, file, cb){
cb(null, MODULES_PACKAGES_UPLOAD_DIR);
}
});
var upload = multer({storage: storage});
router.post('/fileUpload', upload.array(), function(req, res){
debug('We have a a file');
//Send the ok response
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('\n');
}
The sending code:
var Util = require('util');
var http = require('request-promise');
var request = require('request');
var fs = require('fs');
var Post = require('http');
var FormData = require('form-data');
//Generate the form data
var formdata = modules.map(function(fileName){
return fs.createReadStream('/opt/files/'+fileName);
});
var data = getData(); //Gets the body of the code as a promise
return Promise.all(data)
.then(function(dataResults){
var options = {
method: 'POST',
uri: 'https://' + name +'/file',
rejectUnauthorized: false,
timeout: 2000,
body: {
keys: keyResults,
modules: modules,
},
formData: { <====== If I remove this section everything works
'module-package': formdata,
},
json: true // Automatically stringifies the body to JSON
};
request.post(options, function(err, response){
if( err){
debug('Error: ',err);
}
else{
debug('We posted');
}
});
The weird thing is that if I remove the formData section then everything works but when it is there I get an exception that says:
SyntaxError: Unexpected token -
at parse (/home/.../projects/node_modules/body-parser/lib/types/json.js:83:15)
Does anyone have any idea what I could be doing wrong??
Just in case anyone in the future comes with the same problem. As #Bergi mentioned. You cant have both json data and form data. You need to choose either one. The solution is to just pass the json data as apart of the form like.
var options = {
method: 'POST',
uri: 'https://' + name +'/file',
rejectUnauthorized: false,
timeout: 2000,
body: {
},
formData: {
'module-package': formdata,
keys: keyResults,
modules: modules,
},
json: true // Automatically stringifies the body to JSON
};
request.post(options, function(err, response){
if( err){
debug('Error: ',err);
}
else{
debug('We posted');
}
});
In my case, the header of the HTTP Request contained "Content-Type" as "application/json".
So here are the things to check:
Send only either form-data or json body. NOT BOTH.
Check for Headers if the "Content-Type" is mentioned. Remove that.

Categories