简体   繁体   English

使用NodeJ发布多个二进制文件

[英]Post multiple binary files with NodeJs

How can I POST multiple binary files to a server with Content-Type: "form-data" , using only the http module? 我怎样才能POST多个二进制文件与服务器Content-Type: "form-data"使用http模块?

For example, my keys would look like this: 例如,我的键如下所示:

{
    Image1: <binary-data>,
    Image2: <binary-data>
}

TL:DR; TL:DR; See the full example code at the bottom of this answer. 请参阅此答案底部的完整示例代码。

I was trying to figure out how to POST multiple binary image files to a server in NodeJS using only core NodeJs modules (without using anything like npm install request , etc). 我试图找出如何POST多个二进制图像文件中使用的NodeJS唯一的核心模块的NodeJS到服务器(不使用像什么npm install request等)。 After about 3 hours typing the wrong search terms into DuckDuckGo and finding no examples online, I was about to switch careers. 大约3个小时后,在DuckDuckGo中输入了错误的搜索字词,却没有在网上找到任何示例,我打算转行。 But after banging my head against the desk for almost half the day, my sense of urgency was dulled and I managed to hobble together a working solution. 但是当我将头撞到书桌上将近半天后,我的紧迫感变得平淡了,我设法制定了一个可行的解决方案。 This would not have been possible without the good people who wrote the answer to this Stackoverflow post , and this Github Gist . 没有撰写这个Stackoverflow帖子的答案以及Github Gist的好人,这是不可能的。 I used PostMan and Charles Proxy to analyze successful HTTP POSTS, as I dug around in the NodeJS docs. NodeJS文档中 ,我使用PostManCharles Proxy分析成功的HTTP POST

There are a few things to understand to POST two binary images and a text field as multipart/form-data , relying only on core NodeJS modules: 仅依靠核心NodeJS模块将POST两个二进制图像和一个文本字段作为multipart/form-data要了解一些事情:

1) Boundary Identifiers 1)边界标识符

What is the boundary in multipart/form-data? multipart / form-data中的边界是什么?

The first part of the solution is to create a "boundary identifier", which is a string of dashes - , appended with a random number. 解决方案的第一部分是创建一个“边界标识符”,它是一串破折号- ,后跟一个随机数。 You could probably use whatever you wish, foorbar and such. 您可能会使用任何您想要的foorbar ,例如foorbar等。

------------0123456789

Then place that boundary between each blob of data; 然后在每个数据块之间放置该边界; whether that is binary data or just text data. 无论是二进制数据还是文本数据。 When you have listed all your data, you add the boundary identifier at the end, and append two dashes: 列出所有数据后,请在最后添加边界标识符,并添加两个破折号:

------------0123456789--

You also need to add the boundary to the headers so that server receiving the post understands which lines of your post data form the boundary between fields. 您还需要在headers添加边界,以便接收该帖子的服务器了解您的帖子数据的哪些行形成了字段之间的边界。

const headers = {
    // Inform the server what the boundary identifier looks like
    'Content-Type': `multipart/form-data; boundary=${partBoundary}`,
    'Content-Length': binaryPostData.length
}

2) Form Field Meta Descriptors 2)表单字段元描述符

(This probably is not what they are called) (这可能不是他们所说的)

You will also need a way to write the meta data for each form-field you send, whether that form field contains a binary or a text object. 您还需要一种方法为发送的每个表单域写入元数据,无论该表单域包含二进制对象还是文本对象。 Here are the descriptors for an image file, which contain the mime type: 以下是图像文件的描述符,其中包含mime类型:

Content-Disposition: form-data; name="Image1"; filename="image-1.jpg"
Content-Type: image/jpeg

And here is the descriptor for a text field: 这是文本字段的描述符:

Content-Disposition: form-data; name="comment"

The Post Data Output 后期数据输出

So the entire post data that is sent to the server will look like this: 因此,发送到服务器的整个帖子数据将如下所示:

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

Once this post data is generated, it can be converted to binary and written to the HTTP POST request: request.write(binaryPostData) . 生成此发布数据后,可以将其转换为二进制并写入HTTP POST请求: request.write(binaryPostData)

Example Code 范例程式码

Here is the full example code that will allow you to POST binary file and text data without having to include other NodeJS libraries and packages in your code. 这是完整的示例代码,可让您发布二进制文件和文本数据,而不必在代码中包含其他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)
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM