Posted file has headers added to file content - node.js

I am trying to post files to server. It returns 200 OK response, but the content of files is not readable by programs. When I open those in text file, I see boundary and some headers are present in the content of file.
File starts with ----------------------------802523244934076832438189
Content-Disposition: form-data; name="file"; filename="Test1.png"
Content-Type: image/png
and Ends with ----------------------------802523244934076832438189--
My code is given below:
var formData = {
file:{
value: fs.createReadStream('./upload-folder/' + fileName),
options: {
filename: fileName,
contentType: req.body.attachment.mimeType //mimeType from JSON
}
}
};
var options = {
url: config.deployment.incidentUrl + '/attachment?filename=' + fileName,
method: "POST",
headers: { ContentType: "application/json"},
json: true,
formData: formData
};
request(options,
function (error, response, body) {
if (error) {
errorlog.error(`Error Message : PostAttachmentToCSMS : ${error}`);
}
else {
successlog.info(`Attachment posted for correlation Id: ${corIdFromJira}`);
}
});

I think it's because using formData is mutually exclusive with using json: true: the data is either multipart/form-data encoded or JSON-encoded, but it can't be both. The Content-Type header also isn't correct (although it's misspelled so technically not the problem).
Try this:
var options = {
url: config.deployment.incidentUrl + '/attachment?filename=' + fileName,
method: "POST",
formData: formData
};

Related

Download PDF received from API using Express

I'm currently calling an API that returns a pdf
This is my code.
const options = {
method: 'GET',
url: 'myurl',
headers: {
accept: 'application/pdf',
encoding: null,
authorization: 'Bearer ' + token
},
}
Then i make the request
try{
var file = await request(options,(error,response,body) => {
if(error) throw new Error(error)
})
}catch(e){
console.log(e)
}
console.log(file)
The console.log prints what i assume is the pdf encoded
/Filter /FlateDecode
/Length 15130
>>
stream
-qO�▲$�֤���� K�;�Cn��V��!R�♂�PA�D
// And prints a lot more of these characters
However when i try to download it as pdf, it downloads an empty pdf. I was doing this:
fs.writeFileSync("./report/report.pdf",file);
res.download("./report/report.pdf",'report.pdf')
If someone could help me,
Thanks!

how to send just the filename in response of multer file upload

hi I am using express and multer to receive multiple files in backend from react frontend my react query code is like this:
fileChangeHandler = (event) => {
const data = new FormData()
let files = event.target.files
for (let file of files) {
data.append('file', file)
}
let url = db.url + "/adminendpoint/uploadfile"
axios.post(url, data, {
headers: {
'Content-Type': 'application/json',
Authorization: this.props.token
},
}).then(r => console.log(r.names))
}
the backend data for multer is like this :
uploadFile = async (req,res,next)=>{
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/images')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' +file.originalname )
}
})
let upload = multer({storage:storage}).array('file')
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return res.json({err:err})
} else if (err) {
return res.json({err:err})
}
let names=[]
req.files.map(f=> names.push(f.filename))
console.log(names);
res.setHeader('Content-Type', 'application/json');
return res.json({names:names})
})
but the weird thing is that in the backend the names is an array holding the names of uploaded files but in the frontend the response is an object with a lot of data like this. it has the names array in it but I want to stop sending this chunk of data from my backend and only send the file names and the response format should only be json
config: {url: "http://localhost:8090/adminendpoint/uploadfile", method: "post", data: FormData, headers: {…}, transformRequest: Array(1), …}
data:
names: (2) ["1603483842517-2.png", "1603483842518-3.png"]
__proto__: Object
headers: {content-length: "54", content-type: "application/json; charset=utf-8"}
request: XMLHttpRequest {readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, onreadystatechange: ƒ, …}
status: 200
statusText: "OK"
__proto__: Object

​
You're all set, just use the data property from the Axios response object.
console.log(r.data.names)
Axios returns a response object that contains more than just the response body. You'll get status, headers, config, request. The complete field list is availabel in Axios docs.
Also, a few side notes. You wouldn't want to set 'Content-Type': 'application/json' in React, since you're using FormData - it'll set 'Content-Type': 'multipart/form-data' behind the scenes. And in Express, if you're using res.json(), you don't have to set content type at all since that function already sets it.

How to send a file in request node-fetch or Node?

How do I attach a file in Node or Node Fetch POST request? I am trying to invoke an API which will import a CSV or XLS file. Is this possible using Node or Node Fetch?
README.md says:
Use native stream for body, on both request and response.
And sources indicate it supports several types, like Stream, Buffer, Blob... and also will try to coerce as String for other types.
Below snippet shows 3 examples, all work, with v1.7.1 or 2.0.0-alpha5 (see also other example further down with FormData):
let fetch = require('node-fetch');
let fs = require('fs');
const stats = fs.statSync("foo.txt");
const fileSizeInBytes = stats.size;
// You can pass any of the 3 objects below as body
let readStream = fs.createReadStream('foo.txt');
//var stringContent = fs.readFileSync('foo.txt', 'utf8');
//var bufferContent = fs.readFileSync('foo.txt');
fetch('http://httpbin.org/post', {
method: 'POST',
headers: {
"Content-length": fileSizeInBytes
},
body: readStream // Here, stringContent or bufferContent would also work
})
.then(function(res) {
return res.json();
}).then(function(json) {
console.log(json);
});
Here is foo.txt:
hello world!
how do you do?
Note: http://httpbin.org/post replies with JSON that contains details on request that was sent.
Result:
{
"args": {},
"data": "hello world!\nhow do you do?\n",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip,deflate",
"Connection": "close",
"Content-Length": "28",
"Host": "httpbin.org",
"User-Agent": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
},
"json": null,
"origin": "86.247.18.156",
"url": "http://httpbin.org/post"
}
If you need to send a file as part of a form with more parameters, you can try:
npm install form-data
pass a FormData object as body (FormData is a kind of Stream, via CombinedStream library)
do not pass header in the options (unlike examples above)
and then this works:
const formData = new FormData();
formData.append('file', fs.createReadStream('foo.txt'));
formData.append('blah', 42);
fetch('http://httpbin.org/post', {
method: 'POST',
body: formData
})
Result (just showing what is sent):
----------------------------802616704485543852140629
Content-Disposition: form-data; name="file"; filename="foo.txt"
Content-Type: text/plain
hello world!
how do you do?
----------------------------802616704485543852140629
Content-Disposition: form-data; name="blah"
42
----------------------------802616704485543852140629--
I was looking for how to use node-fetch to upload files via multipart/form-data and their GitHub docs actually shows how to do this. Below is modified example showing how to attach a buffer to FormData and upload that.
const FormData = require('form-data');
const form = new FormData();
const buffer = // e.g. `fs.readFileSync('./fileLocation');
const fileName = 'test.txt';
form.append('file', buffer, {
contentType: 'text/plain',
name: 'file',
filename: fileName,
});
fetch('https://httpbin.org/post', { method: 'POST', body: form })
.then(res => res.json())
.then(json => console.log(json));
Sharing this for anyone else who googled "node-fetch upload file multipart" like I did.

Unsupported media type when uploading form data to spring server

I am trying to upload a file to a remote spring server via an API, and I keep getting an unsupported media type error (415) even though I have already made the data into form data.
Here is the express http post request:
var FormData = require('form-data');
var fs = require('fs');
var form = new FormData();
form.append('pid', params.pid);
form.append('deliveryAttachment', fs.createReadStream(params.deliveryAttachment.path));
var url = someDomain + '/proj/new/deliveryAttachment';
requestLib({
url: url,
method: "POST",
jar: getJar(),
form: form
},function (error, response, body){
console.log(body)
});
And here is the Java Spring controller for reference:
#RequestMapping(value = "proj/new/deliveryAttachment", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
public String insertDeliveryAttachment(#RequestParam("pid") long pid,
#RequestParam("deliveryAttachment") MultipartFile file) {
try {
DeliveryAttachment a = new DeliveryAttachment(file.getOriginalFilename(), pid);
ps.insertDeliveryAttachment(a, file.getBytes());
return String.valueOf(a.id);
} catch (IOException e) {
return "-1";
}
}
This is the form data console log:
And the 415 response:
{
"timestamp": 1494671395688,
"status": 415,
"error": "Unsupported Media Type",
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException",
"message": "Content type 'application/x-www-form-urlencoded' not supported",
"path": "/proj/new/deliveryAttachment"
}
--UPDATE--
Alright, I just found out after reading request's docs that if you use form as the holder for the data, it will treat the data as application/x-www-form-urlencoded
e.g.; request.post({url:'http://service.com/upload', form: {key:'value'}}, function(err,httpResponse,body){ ... });
Meanwhile the correct key for multipart/form-data is formData
e.g.; request.post({url:'http://service.com/upload', formData: formData}, function optionalCallback(err, httpResponse, body) { ... });
I tried it, and now it's giving me a new error:
TypeError: Cannot read property 'name' of null at FormData._getContentDisposition
It seems that you are sending a POST request with Content-Type: 'application/x-www-form-urlencoded', and your SpringController insertDeliveryAttachment() consumes multipart/form-data mime-type.
I would recommend you to change the consumes mime-type on your insertDeliveryAttachment() method to MediaType.APPLICATION_FORM_URLENCODED_VALUE
I've solved it. I did not use FormData and just went with inserting the values inside an object and it worked.
var data = {
pid: params.pid,
deliveryAttachment: fs.createReadStream(params.deliveryAttachment[0].path)
};
var url = wfDomain + '/proj/new/deliveryAttachment';
requestLib({
url: url,
method: "POST",
headers: {
'Content-Type': 'multipart/form-data'
},
jar: getJar(),
formData: data
},function (error, response, body){ ... });

Uploading file using POST request in Node.js

I have problem uploading file using POST request in Node.js. I have to use request module to accomplish that (no external npms). Server needs it to be multipart request with the file field containing file's data. What seems to be easy it's pretty hard to do in Node.js without using any external module.
I've tried using this example but without success:
request.post({
uri: url,
method: 'POST',
multipart: [{
body: '<FILE_DATA>'
}]
}, function (err, resp, body) {
if (err) {
console.log('Error!');
} else {
console.log('URL: ' + body);
}
});
Looks like you're already using request module.
in this case all you need to post multipart/form-data is to use its form feature:
var req = request.post(url, function (err, resp, body) {
if (err) {
console.log('Error!');
} else {
console.log('URL: ' + body);
}
});
var form = req.form();
form.append('file', '<FILE_DATA>', {
filename: 'myfile.txt',
contentType: 'text/plain'
});
but if you want to post some existing file from your file system, then you may simply pass it as a readable stream:
form.append('file', fs.createReadStream(filepath));
request will extract all related metadata by itself.
For more information on posting multipart/form-data see node-form-data module, which is internally used by request.
An undocumented feature of the formData field that request implements is the ability to pass options to the form-data module it uses:
request({
url: 'http://example.com',
method: 'POST',
formData: {
'regularField': 'someValue',
'regularFile': someFileStream,
'customBufferFile': {
value: fileBufferData,
options: {
filename: 'myfile.bin'
}
}
}
}, handleResponse);
This is useful if you need to avoid calling requestObj.form() but need to upload a buffer as a file. The form-data module also accepts contentType (the MIME type) and knownLength options.
This change was added in October 2014 (so 2 months after this question was asked), so it should be safe to use now (in 2017+). This equates to version v2.46.0 or above of request.
Leonid Beschastny's answer works but I also had to convert ArrayBuffer to Buffer that is used in the Node's request module. After uploading file to the server I had it in the same format that comes from the HTML5 FileAPI (I'm using Meteor). Full code below - maybe it will be helpful for others.
function toBuffer(ab) {
var buffer = new Buffer(ab.byteLength);
var view = new Uint8Array(ab);
for (var i = 0; i < buffer.length; ++i) {
buffer[i] = view[i];
}
return buffer;
}
var req = request.post(url, function (err, resp, body) {
if (err) {
console.log('Error!');
} else {
console.log('URL: ' + body);
}
});
var form = req.form();
form.append('file', toBuffer(file.data), {
filename: file.name,
contentType: file.type
});
You can also use the "custom options" support from the request library. This format allows you to create a multi-part form upload, but with a combined entry for both the file and extra form information, like filename or content-type. I have found that some libraries expect to receive file uploads using this format, specifically libraries like multer.
This approach is officially documented in the forms section of the request docs - https://github.com/request/request#forms
//toUpload is the name of the input file: <input type="file" name="toUpload">
let fileToUpload = req.file;
let formData = {
toUpload: {
value: fs.createReadStream(path.join(__dirname, '..', '..','upload', fileToUpload.filename)),
options: {
filename: fileToUpload.originalname,
contentType: fileToUpload.mimeType
}
}
};
let options = {
url: url,
method: 'POST',
formData: formData
}
request(options, function (err, resp, body) {
if (err)
cb(err);
if (!err && resp.statusCode == 200) {
cb(null, body);
}
});
I did it like this:
// Open file as a readable stream
const fileStream = fs.createReadStream('./my-file.ext');
const form = new FormData();
// Pass file stream directly to form
form.append('my file', fileStream, 'my-file.ext');
const remoteReq = request({
method: 'POST',
uri: 'http://host.com/api/upload',
headers: {
'Authorization': 'Bearer ' + req.query.token,
'Content-Type': req.headers['content-type'] || 'multipart/form-data;'
}
})
req.pipe(remoteReq);
remoteReq.pipe(res);

Resources