【问题标题】:Post multiple binary files with NodeJs使用 NodeJs 发布多个二进制文件
【发布时间】:2017-06-30 16:01:46
【问题描述】:

我如何通过Content-Type: "form-data" 将多个二进制文件POST 发送到服务器,使用http 模块?

例如,我的密钥如下所示:

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

【问题讨论】:

    标签: node.js forms http post binary


    【解决方案1】:

    TL:DR;请参阅此答案底部的完整示例代码。

    我试图弄清楚如何在 NodeJS 中仅使用核心 NodeJs 模块(不使用 npm install request 等任何东西)将多个二进制图像文件 POST 发送到服务器。在 DuckDuckGo 中输入错误的搜索词大约 3 小时后,在网上找不到任何示例,我正要转行。但在我的头撞在桌子上将近半天之后,我的紧迫感变得迟钝,我设法拼凑出一个可行的解决方案。如果没有为this Stackoverflow postthis Github Gist 写答案的好人,这是不可能的。我使用PostManCharles Proxy 来分析成功的HTTP POST,正如我在NodeJS 文档中挖掘的那样。

    对于POST 两个二进制图像和一个文本字段为multipart/form-data,有几点需要理解,仅依赖于核心NodeJS 模块:

    1) 边界标识符

    What is the boundary in multipart/form-data?

    解决方案的第一部分是创建一个“边界标识符”,它是一串破折号-,附加一个随机数。你可以使用任何你想要的东西,foorbar 等等。

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

    然后在每个数据块之间放置该边界;无论是二进制数据还是文本数据。列出所有数据后,在末尾添加边界标识符,并附加两个破折号:

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

    您还需要将边界添加到headers,以便接收帖子的服务器了解您的帖子数据的哪些行形成了字段之间的边界。

    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"
    

    发布数据输出

    所以发送到服务器的整个帖子数据将如下所示:

    ----------------------------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 POST请求: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)
    })
    

    【讨论】:

      猜你喜欢
      • 2018-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-15
      相关资源
      最近更新 更多