How do I send a form with a request promise? - javascript

I am trying to use the request-promise library or anything similar to send files via a post request from node to another machine that is running Node. Using the normal request module I could so something like
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'
});
This code is from the question:
Uploading file using POST request in Node.js however it is not using promises.
Can anyone explain how to do the same thing but with the request-promise library or if there is any other way to promisify this?

According to the docs that are linked from the answer you already found, you don't need to use a .form() method on the resulting request object, but can simply pass the form as the formData option to request. You'll be able to do the same with request-promise:
requestPromise.post({url: url, formData: {
file: {
value: '<FILE_DATA>',
options: {
filename: 'myfile.txt',
contentType: 'text/plain'
}
}
}).then(function(body) {
console.log('URL: ' + body);
}, function(err) {
console.log('Error!');
});
Alternatively, request-promise still seems to return request instances (just decorated with then/catch/promise methods), so the form function should still be available:
var req = requestPromise.post(url);
var form = req.form();
form.append('file', '<FILE_DATA>', {
filename: 'myfile.txt',
contentType: 'text/plain'
});
req.then(function(body) {
console.log('URL: ' + body);
}, function(err) {
console.log('Error!');
});

Related

Next.js API Forward FormData to External API [duplicate]

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);

Sending file to Slack using 'https' in node.js

So I wrote Slack reporter for my automated tests and wanted to switch from deprecated module 'request' to 'https' module. I changed the request sending a normal message but I don't know how to create a request for sending a file. I can't find any example in node documentation (no POST examples for 'https' there) nor any example of that kind of use on the internet. Can anyone help me with this?
That's the working request:
function sendLogFile() {
console.log("Sending log file to slack...");
return new Promise((resolve, reject) => {
request.post(
{
url: fileUploadUrl,
formData: {
token: token,
method: "POST",
title: "Test Log File",
filename: "testLog.txt",
filetype: "auto",
channels: "***",
file: fs.createReadStream("testLog.txt")
}
},
function(err, response) {
if (response.body.includes("created"))
resolve("File send successfully!");
if (response.body.includes("error")) reject(response.body);
if (err) reject(err);
}
);
});
}
And this is kinda (SEE THE EDIT BELOW) what I want (but it's not working):
function sendLogFile() {
return new Promise((resolve, reject) => {
const requestOptions = {
url: fileUploadUrl,
headers: headers,
formData: {
token: token,
method: "POST",
title: "Test Log File",
filename: "testLog.txt",
filetype: "auto",
channels: "***",
file: fs.createReadStream("testLog.txt")
}
};
const req = https.request(requestOptions, res => {
res.on("data", d => resolve(d));
});
req.on("error", e => {
reject(e);
});
// Probably that's the part where I'm stuck:
req.write('????????')
req.end();
});
}
I know there is slackapi for node but the thing is I need this reporter to be without any additional packages. And I know it's possible with request.promise or xhr but I need this to be 'https'.
EDIT:
Ok, so I was trying to get somewhere and I think it should look more like:
const file = fs.createReadStream("testLog.txt");
const options = {
channels: "***",
hostname: "slack.com",
port: 443,
path: '/api/files.upload',
method: 'POST',
headers: {
'Authorization': "Bearer ***",
'Content-Type': 'application/json; charset=utf-8',
}
}
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
process.stdout.write(d)
})
})
req.on('error', error => {
console.error(error)
})
req.write(data)
req.end()
But I have no idea how to past file to req.write(data) since 'data' has to be string, Buffer, ArrayBuffer, Array, or Array-like Object.
So, for now, the response I get is:
statusCode: 200 {"ok":false,"error":"no_file_data"}%
And I'm also not sure if it's possible because slack API says the header should be formData but this response suggests this approach is fine I guess.
Anyone, please?
If you refer to https documentation you can see that options object does not accept such property as formData.
Instead, you should try to send the post data like in this answer.

How to debug a POST request to a Node.js Express application?

I'm new to nodejs and I'm migrating my current API from python to nodejs using express.
What I'm trying to do is to make a request to an external API. I'm pretty sure my API call is right, since I copied from the external API example:
exports.getBalance = function() {
return new Promise(function(resolve, reject) {
var command_url = "/v1/transaction/getBalance";
var full_url = API_URL + command_url;
var nonce = getNonce();
var data = "username=" + convertUsername(API_USER) + "&nonce=" + nonce;
const signature = makeSignature(nonce, data, command_url);
var form = {
username: API_USER,
nonce: nonce
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
var headers = {
"X-API-KEY": API_KEY,
"X-API-SIGN": signature,
"X-API-NONCE": nonce,
"Content-Length": contentLength,
"Content-Type": "application/x-www-form-urlencoded"
};
request(
{
url: full_url,
method: "POST",
headers: headers,
body: formData
},
function(error, response, body) {
if (!error) {
body = JSON.parse(body);
if (response.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
} else {
console.log("error:", error);
reject(error);
}
}
);
});
This is my express route:
routes.post("/balance", mistertango.getBalance);
However, when I try to POST to this route, I don't receive nothing. I use Insomnia to run API tests, so Insomnia keeps running with no response from my express API.
I'd like to know how can I debug my code? I'd like to make an API call using Insomnia to my express API, and check if I'm getting a response from my external API request.
Thanks
mistertango.getBlance returns a Promise but express doesn't handle promises by default. You need to call res.send(data) to actually send a response to the client.
routes.post("/balance", async (req, res, next) => {
try {
const balance = await mistertango.getBalance()
res.send({ balance })
} catch (error) {
next(error)
}
})
Or without async/await:
routes.post("/balance", (req, res, next) => {
mistertango.getBalance()
.then(balance => res.send({ balance }))
.catch(error => next(error))
})
Note 1: You might be able to use res.send(balance) instead of res.send({ balance }) as long as balance is not a number. (Response body cannot be a raw number, so I've wrapped it in an object).
Note 2: In both cases, we have to use .catch or try/catch to handle any errors because express won't handle rejected promises on its own. You can use express-promise-router to fix that!

Header section has more than 10240 bytes (maybe it is not properly terminated)

I'm using NodeJs to try to upload an attachment to a Jira Issue via the Jira Rest API.
The api expects multipart/form-data so this is how I'm calling it in Node:
function uploadAttachments(supportFormData, callback) {
const url =
'https://somewhere.com/jira/rest/api/2/issue/' +
supportFormData.issueId +
'/attachments';
var options = {
url: url,
headers: {
Authorization: { user: username, password: password },
'X-Atlassian-Token': 'nocheck'
}
};
var r = request.post(options, function(err, res, body) {
if (err) {
console.error(err);
callback(false);
} else {
console.log('Upload successful! Server responded with:', body);
callback(false);
}
});
var form = r.form();
form.append('file', supportFormData.attachments[0].contents, {
filename: supportFormData.attachments[0].fileName,
contentType: supportFormData.attachments[0].contents
});
}
The error I'm receiving is:
org.apache.commons.fileupload.FileUploadException: Header section
has more than 10240 bytes (maybe it is not properly terminated)
The "supportFormData.attachments[0].contents" is ofType Buffer.
Any suggestions as to what could be causing this error?
I ran into this same issue and it turns out JIRA (or Java) requires \r\n as new line character. After I changed \n to \r\n my requests went through without problem.
If its a basic auth change options object to
let auth = new Buffer(`${username}:${password}`).toString('base64');
var options = {
url: url,
headers: {
Authorization: `Basic ${auth}`,
'X-Atlassian-Token': 'nocheck'
}
};

Post request from Scheduled Job is not working

(I am new to JS, node.js and Heroku so I hope the question is clear)
I have an Heroku node.js app, I was able to set up Heroku scheduler to run a task every 1 hour.
The task runs on time and the method fireHook() is called (I can see the log in the console) but the request does not work (I don't get any lo or error).
This is the job:
#!/usr/bin/env node
var request = require('request')
function fireHook(){
console.log("firing")
request({
url: 'https://XXXXXX.herokuapp.com/jobs',
method: "POST",
headers: { "content-type": "application/json" },
json: {"user_id":{ "id":"ddddd"}}
}, function(error, response, body) {
console.log(response)
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
} else {
console.log('success: ', body)
reportToSnitch()
}
})
}
fireHook();
process.exit();
2 questions:
Why sold the request not working ?
I am currently using a webhook to call my main app, is there a better way to call a function on the main app from the script directly?
Thanks
I think your process.exit() is killing your script before the request has received its response. Try to move it into the callback:
#!/usr/bin/env node
var request = require('request')
function fireHook(){
console.log("firing")
request({
url: 'https://XXXXXX.herokuapp.com/jobs',
method: "POST",
headers: { "content-type": "application/json" },
json: {"user_id":{ "id":"ddddd"}}
}, function(error, response, body) {
console.log(response)
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
} else {
console.log('success: ', body)
reportToSnitch()
}
process.exit();
})
}
fireHook();
JavaScript is asynchronous so you have to be careful to consider where and when certain events will occur. If your reportToSnitch() function is async as well, you may need to fire the process.exit() at the end of that function.

Categories