如何使用POST只使用 http模块向服务器发送多个二进制文件?
例如,我的钥匙应该是这样的:
{
Image1: <binary-data>,
Image2: <binary-data>
}发布于 2017-07-01 05:54:10
TL:DR;请参阅答案底部的完整示例代码。
我试图弄清楚如何仅使用核心NodeJS模块(不使用类似于POST等的任何东西)将多个二进制图像文件发送到NodeJs中的服务器。在DuckDuckGo上输入错误的搜索词并在网上找不到任何例子大约3个小时后,我就要换个职业了。但是在我的头撞在桌子上将近半天之后,我的紧迫感变得迟钝,我设法想出了一个可行的解决方案。如果没有给这个堆叠溢出柱和这个吉特布·吉斯特写答案的好人,这是不可能的。我使用PostMan和查尔斯代理来分析成功的HTTP,就像我在NodeJS文档中所做的那样。
对于POST来说,有几件事需要理解--两个二进制图像和一个文本字段( multipart/form-data ),它们仅依赖于核心NodeJS模块:
1)边界标识符
解决方案的第一部分是创建一个“边界标识符”,它是一个带有随机数的破折号-字符串。你可能想用什么就用什么,foorbar之类的。
------------0123456789然后在每个数据块之间放置这个边界;无论是二进制数据还是仅仅文本数据。列出所有数据后,在末尾添加边界标识符,并附加两个破折号:
------------0123456789--还需要将边界添加到headers中,以便接收post的服务器了解post数据的哪些行构成字段之间的边界。
const headers = {
// Inform the server what the boundary identifier looks like
'Content-Type': `multipart/form-data; boundary=${partBoundary}`,
'Content-Length': binaryPostData.length
}2)形式域元描述符
(这可能不是他们所称的)
您还需要一种方法来为您发送的每个表单字段编写元数据,无论该表单字段包含二进制还是文本对象。下面是图像文件的描述符,其中包含mime类型:
Content-Disposition: form-data; name="Image1"; filename="image-1.jpg"
Content-Type: image/jpeg下面是文本字段的描述符:
Content-Disposition: form-data; name="comment"邮政数据输出
所以发送到服务器的整个post数据如下所示:
----------------------------9110957588266537
Content-Disposition: form-data; name="Image1"; filename="image-1.jpg"
Content-Type: image/jpeg
ÿØÿàJFIFHHÿáLExifMMi
ÿí8Photoshop 3.08BIM8BIM%ÔÙ²é ìøB~ÿÀ... <<<truncated for sanity>>>
----------------------------9110957588266537
Content-Disposition: form-data; name="Image2"; filename="image-2.jpg"
Content-Type: image/jpeg
ÿØÿàJFIFHHÿáLExifMMi
ÿí8Photoshop 3.08BIM8BIM%ÔÙ²é ìøB~ÿÀ... <<<truncated for sanity>>>
----------------------------9110957588266537
Content-Disposition: form-data; name="comment"
This is a comment.
----------------------------9110957588266537--一旦生成这些post数据,就可以将其转换为二进制数据,并将其写入HTTP请求:request.write(binaryPostData)。
示例代码
下面是完整的示例代码,它允许您发布二进制文件和文本数据,而不必在代码中包含其他NodeJS库和包。
// This form data lists 2 binary image fields and text field
const form = [
{
name: 'Image1',
type: 'file',
value: 'image-1.jpg'
},
{
name: 'Image2',
type: 'file',
value: 'image-2.jpg'
},
{
name: 'comment',
type: 'text',
value: 'This is a comment.'
}
]
// Types of binary files I may want to upload
const types = {
'.json': 'application/json',
'.jpg': 'image/jpeg'
}
const config = {
host: 'ec2-192.168.0.1.compute-1.amazonaws.com',
port: '80',
path: '/api/route'
}
// Create an identifier to show where the boundary is between each
// part of the form-data in the POST
const makePartBoundary = () => {
const randomNumbers = (Math.random() + '').split('.')[1]
return '--------------------------' + randomNumbers
}
// Create meta for file part
const encodeFilePart = (boundary, type, name, filename) => {
let returnPart = `--${boundary}\r\n`
returnPart += `Content-Disposition: form-data; name="${name}"; filename="${filename}"\r\n`
returnPart += `Content-Type: ${type}\r\n\r\n`
return returnPart
}
// Create meta for field part
const encodeFieldPart = (boundary, name, value) => {
let returnPart = `--${boundary}\r\n`
returnPart += `Content-Disposition: form-data; name="${name}"\r\n\r\n`
returnPart += value + '\r\n'
return returnPart
}
const makePostData = {
// Generate the post data for a file
file: (item, partBoundary) => {
let filePostData = ''
// Generate the meta
const filepath = path.join(__dirname, item.value)
const extention = path.parse(filepath).ext
const mimetype = types[extention]
filePostData += encodeFilePart(partBoundary, mimetype, item.name, item.value)
// Add the binary file data
const fileData = fs.readFileSync(filepath, 'binary')
filePostData += fileData
filePostData += '\r\n'
return filePostData
},
// Generate post data for the text field part of the form
text: (item, partBoundary) => {
let textPostData = ''
textPostData += encodeFieldPart(partBoundary, item.name, item.value)
return textPostData
}
}
const post = () => new Promise((resolve, reject) => {
let allPostData = ''
// Create a boundary identifier (a random string w/ `--...` prefixed)
const partBoundary = makePartBoundary()
// Loop through form object generating post data according to type
form.forEach(item => {
if (Reflect.has(makePostData, item.type)) {
const nextPostData = makePostData[item.type](item, partBoundary)
allPostData += nextPostData
}
})
// Create the `final-boundary` (the normal boundary + the `--`)
allPostData += `--${partBoundary}--`
// Convert the post data to binary (latin1)
const binaryPostData = Buffer.from(allPostData, 'binary')
// Generate the http request options
const options = {
host: config.host,
port: config.port,
path: config.path,
method: 'POST',
headers: {
// Inform the server what the boundary identifier looks like
'Content-Type': `multipart/form-data; boundary=${partBoundary}`,
'Content-Length': binaryPostData.length
}
}
// Initiate the HTTP request
const req = http.request(options, res => {
res.setEncoding('utf8')
let body = ''
// Accumulate the response data
res.on('data', chunk => {
body += chunk
})
// Resolve when done
res.on('end', () => {
resolve(body)
})
res.on('close', () => {
resolve(body)
})
res.on('error', err => {
reject(err)
})
})
// Send the binary post data to the server
req.write(binaryPostData)
// Close the HTTP request object
req.end()
})
// Post and log response
post().then(data => {
console.log(data)
})
.catch(err => {
console.error(err)
})https://stackoverflow.com/questions/44850661
复制相似问题