[英]NodeJS Request how to send multipart/form-data POST request
I'm trying to send a POST request to an API with an image in the request.我正在尝试将 POST 请求发送到请求中包含图像的 API。 I'm doing this with the request module but everything I try it isn't working.
我正在使用请求模块执行此操作,但我尝试的所有操作均无效。 My current code:
我当前的代码:
const options = {
method: "POST",
url: "https://api.LINK.com/file",
port: 443,
headers: {
"Authorization": "Basic " + auth,
"Content-Type": "multipart/form-data"
},
form : {
"image" : fs.readFileSync("./images/scr1.png")
}
};
request(options, function (err, res, body) {
if(err) console.log(err);
console.log(body);
});
But request uses Content-Type: application/x-www-form-urlencoded
for some reason... How can I fix this?但是请求使用
Content-Type: application/x-www-form-urlencoded
出于某种原因......我该如何解决这个问题?
As explained in documentation form multipart/form-data
request is using form-data
library.正如文档中所解释的,表单
multipart/form-data
请求正在使用form-data
数据库。 So you need to supply formData
option instead of form
option.所以你需要提供
formData
选项而不是form
选项。
const options = {
method: "POST",
url: "https://api.LINK.com/file",
port: 443,
headers: {
"Authorization": "Basic " + auth,
"Content-Type": "multipart/form-data"
},
formData : {
"image" : fs.createReadStream("./images/scr1.png")
}
};
request(options, function (err, res, body) {
if(err) console.log(err);
console.log(body);
});
Annoyed that almost all example solutions to this problem include third-party modules.恼火的是,几乎所有解决此问题的示例解决方案都包含第三方模块。 I know it's often easier to simply include a module and copy/paste a code example but really shouldn't need one for basic HTTP concepts like this.
我知道简单地包含一个模块并复制/粘贴一个代码示例通常更容易,但对于像这样的基本 HTTP 概念来说真的不需要一个。 Adding modules can quickly increase your footprint in cloud environments like AWS lambda where total solution file-size can drastically impact run-time performance.
添加模块可以快速增加您在 AWS lambda 等云环境中的占用空间,其中总解决方案文件大小会极大地影响运行时性能。 I found this helpful example here,
我在这里找到了这个有用的例子,
https://tanaikech.github.io/2017/07/27/multipart-post-request-using-node.js/ https://tanaikech.github.io/2017/07/27/multipart-post-request-using-node.js/
var fs = require('fs');
var request = require('request');
var upfile = 'sample.zip';
fs.readFile(upfile, function(err, content){
if(err){
console.error(err);
}
var metadata = {
token: "### access token ###",
channels: "sample",
filename: "samplefilename",
title: "sampletitle",
};
var url = "https://slack.com/api/files.upload";
var boundary = "xxxxxxxxxx";
var data = "";
for(var i in metadata) {
if ({}.hasOwnProperty.call(metadata, i)) {
data += "--" + boundary + "\r\n";
data += "Content-Disposition: form-data; name=\"" + i + "\"; \r\n\r\n" + metadata[i] + "\r\n";
}
};
data += "--" + boundary + "\r\n";
data += "Content-Disposition: form-data; name=\"file\"; filename=\"" + upfile + "\"\r\n";
data += "Content-Type:application/octet-stream\r\n\r\n";
var payload = Buffer.concat([
Buffer.from(data, "utf8"),
new Buffer(content, 'binary'),
Buffer.from("\r\n--" + boundary + "--\r\n", "utf8"),
]);
var options = {
method: 'post',
url: url,
headers: {"Content-Type": "multipart/form-data; boundary=" + boundary},
body: payload,
};
request(options, function(error, response, body) {
console.log(body);
});
});
Hope it's helpful to someone else!希望对其他人有帮助!
With request
module deprecated , consider using form-data
and core http(s)
module instead.不推荐使用
request
模块,请考虑使用form-data
和核心http(s)
模块。
It's a little bit more verbose but you get smaller production builds, which is always good, even more so in serverless environments where it may reduce cold start time.它有点冗长,但你会得到更小的生产构建,这总是好的,在无服务器环境中更是如此,因为它可以减少冷启动时间。
// abstract and promisify actual network request
async function makeRequest(formData, options) {
return new Promise((resolve, reject) => {
const req = formData.submit(options, (err, res) => {
if (err) {
return reject(new Error(err.message))
}
if (res.statusCode < 200 || res.statusCode > 299) {
return reject(new Error(`HTTP status code ${res.statusCode}`))
}
const body = []
res.on('data', (chunk) => body.push(chunk))
res.on('end', () => {
const resString = Buffer.concat(body).toString()
resolve(resString)
})
})
})
}
const formData = new FormData()
formData.append('comment', 'Some note attached to the submitted file')
formData.append('image', fs.createReadStream('./images/logo.png'))
const options = {
host: 'postman-echo.com',
path: '/post',
method: 'POST',
protocol: 'https:', // note : in the end
headers: {
Authorization: `Basic some-token-here`,
},
}
const res = await makeRequest(formData, options)
I wanted to build a working example in typescript with no external dependencies.我想在没有外部依赖的打字稿中构建一个工作示例。 I would still recommend the solution above but figured this might be helpful for others.
我仍然会推荐上面的解决方案,但认为这可能对其他人有帮助。 The only external dependency here is mime-types to set the content-type header but if you know the content type being uploaded in advance this could be removed, making the solution completely dependency free.
这里唯一的外部依赖是用于设置内容类型标头的mime-types ,但如果您知道提前上传的内容类型,则可以将其删除,从而使解决方案完全没有依赖性。
implementation:执行:
/* eslint-disable prefer-template */
import http from 'http';
import fs from 'fs';
import mime from 'mime-types';
interface FileObject extends Record<string, string> {
fileName: string,
}
const buildFormInputFields = (fileObj: FileObject) => Object.entries(fileObj).map(
([key, value], index) => {
const pairStr = `name="${key}"; ${key}="${value}"`;
return index === Object.keys(fileObj).length - 1 ? pairStr : `${pairStr};`;
},
).join(' ');
const createFileRequest = (
fileObj: FileObject,
data: Buffer,
boundary: string,
) => {
const inputFields = buildFormInputFields(fileObj);
const { fileName } = fileObj;
const mimeType = mime.lookup(fileName);
if (!mimeType) throw new Error(`invalid mime type for file ${fileName}`);
const header = (
`--${boundary}\r\n`
+ `Content-Disposition: form-data; ${inputFields}`
+ '\r\n'
+ `Content-Type: ${mime.lookup(fileObj.fileName)}`
+ '\r\n\r\n'
);
return Buffer.concat([
Buffer.from(header, 'utf-8'),
data,
Buffer.from('\r\n', 'utf-8'),
]);
};
const buildRequestOptions = (boundary: string, length: number) => (
{
// your host here
host: 'localhost',
// your port here
port: 8088,
// your endpoint here
path: '/api/v1/files',
method: 'POST',
headers: {
'Content-Type': `multipart/form-data; boundary=${boundary}`,
'Content-Length': length,
Accept: '*/*',
},
}
);
const sendFileUploadRequest = (files: FileObject[], boundary: string) => new Promise(
(resolve, reject) => {
const fileBodies = files.map((fileObj) => {
const { fileName } = fileObj;
const data = fs.readFileSync(`${__dirname}/${fileName}`);
return createFileRequest(fileObj, data, boundary);
});
const footer = `--${boundary}--`;
const body = Buffer.concat([
...fileBodies,
Buffer.from(footer, 'utf-8'),
]);
const options = buildRequestOptions(boundary, body.length);
const req = http.request(options, (res) => {
console.log(`status: ${res.statusCode}`);
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
res.on('end', () => {
console.log('No more data in response.');
resolve(null);
});
});
req.on('error', (e) => {
reject(e);
console.error(`problem with request: ${e.message}`);
});
req.write(body);
req.end();
},
);
example script using this:使用这个的示例脚本:
(async () => {
const files: FileObject[] = [
{
fileName: 'test1.txt',
foo: 'bar',
},
{
fileName: 'test2.txt',
foo: 'bar',
},
];
const boundary = '----FOOBAR';
await sendFileUploadRequest(files, boundary);
})();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.