如何在没有快递的情况下处理多部分/表单数据请求

Posted

技术标签:

【中文标题】如何在没有快递的情况下处理多部分/表单数据请求【英文标题】:How to Handle multipart/form-data request without express 【发布时间】:2017-10-26 17:42:27 【问题描述】:

所以我有这个表格:

  <form id="myForm" enctype="multipart/form-data">
      <input id="name" title="name" type="text" placeholder="name" />
      <input id="quantity" title="quantity" type="text" placeholder="quantity" />
      <input id="price" title="price" type="text" placeholder="price" />
      <input id="imageLocation" title="imageLocation" type="text" placeholder="imagelocation" />
      <input id="description" title="description" type="text" placeholder="description" />
  </form>

这里是我发送数据的地方:

 function postMultiPartHttpCall() 
    var XHR = new XMLHttpRequest();

    var form = document.getElementById("myForm");

    var FD = new FormData(form);

    XHR.addEventListener("load", function (event) 
        var callingObj = ;
        callingObj.responseText = event.target.responseText;
        console.log(callingObj);
    );

    XHR.open("POST", '/articlePost');
    XHR.send(FD);

这是我收到它的地方:

    function _processFormData (request, res, onSuccess,onError) 
    var requestBody = '';
    request.on('data', function (data) 
        requestBody += data;
        if (requestBody.length > 1e7) 
            res.writeHead(413, 'Request length too long',  'Content-Type': 'text/html' );
            res.end('413 : Request Entity Too Large');
        
    );

    request.on('end', function () 
        var oFormData = qsLib.parse(requestBody);
        console.log(request.headers);

    );

所以当我发送一些数据时,我会收到这个(console.log):

Debugger listening on 127.0.0.1:5858
Server was started
 host: 'localhost:1337',
connection: 'keep-alive',
'content-length': '44',
origin: 'http://localhost:1337',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36     (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryyVFw7KZwIaAIQeQ1',
accept: '*/*',
referer: 'http://localhost:1337/CartSPA.html',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.8,hr;q=0.6,de-AT;q=0.4,de;q=0.2,de-DE;q=0.2' 

所以我想要的是在不使用 expressJS 或其他类似的 3rd 方库的情况下获取具有表单字段的对象。是否可以使用边界编号获取属性或者我在哪里可以看到发送了哪些数据?

【问题讨论】:

为什么是multipart/form-data形式的编码类型?您是否打算发送文件数据(例如用户上传的)? 【参考方案1】:

是的,“可以使用边界编号获取属性”,但您必须匹配字段并手动解析名称...

此外,为了发送表单中的值,需要在input tags 上设置属性name。例如:

<input id="quantity" title="quantity" type="text" placeholder="quantity" />

应更新为包含 name 属性,如下所示:

<input name="quantity" id="quantity" title="quantity" type="text" placeholder="quantity" />

在表单字段上设置名称属性后,请求的正文应该包含表单数据(即,当数据加载完成时,requestBody 应该具有编码表单的内容)。例如,它可能类似于下面的输出:

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

z4520
-----------------------------150182554619156
Content-Disposition: form-data; name="quantity"

2
-----------------------------150182554619156
Content-Disposition: form-data; name="price"

32.90
-----------------------------150182554619156
Content-Disposition: form-data; name="imagelocation"

/img/z4520.jpg
-----------------------------150182554619156
Content-Disposition: form-data; name="description"

water filter
-----------------------------150182554619156--

###解析表单字段

在下面的起始代码中,它会检查 boundary 的请求标头 Content-Type(您可能还想确保 Content-Type 实际上是“multipart/form-data") 使用String.indexOf(),然后使用String.split() 设置边界并从结果数组中获取第二个元素。该边界值可用于将正文数据分隔到一个数组中(也可以使用String.split())。

我将把它作为练习留给读者解析出值并将它们存储在一个数组中(参见@TODO;)。

提示: Array.reduce() 可能会派上用场...

request.on('end', function() 
    if (request.headers.hasOwnProperty('content-type') && request.headers['content-type'].indexOf('boundary=') > -1) 
        var parts = request.headers['content-type'].split('boundary=');
        var boundary = parts[1];
        var splitBody = requestBody.split(boundary);
        /**
         @TODO: iterate over elements in splitBody, matching name and value and add to array of fields
        */

        res.writeHead(200, "Content-Type": "application/json");
        res.write(JSON.stringify(formFields: splitBody));
    
    else 
        //bad request
        res.writeHead(400, "Content-Type": "application/json");
        res.write("missing boundary in content-type"));
    
    res.end();
);

【讨论】:

【参考方案2】:

使用https://github.com/felixge/node-formidable:

var formidable = require('formidable'),
    http = require('http'),
    util = require('util');

http.createServer(function(req, res) 
  if (req.url == '/upload' && req.method.toLowerCase() == 'post') 
    // parse a file upload
    var form = new formidable.IncomingForm();

    form.parse(req, function(err, fields, files) 
      res.writeHead(200, 'content-type': 'text/plain');
      res.write('received upload:\n\n');
      res.end(util.inspect(fields: fields, files: files));
    );

    return;
  

  // show a file upload form
  res.writeHead(200, 'content-type': 'text/html');
  res.end(
    '<form action="/upload" enctype="multipart/form-data" method="post">'+
    '<input type="text" name="title"><br>'+
    '<input type="file" name="upload" multiple="multiple"><br>'+
    '<input type="submit" value="Upload">'+
    '</form>'
  );
).listen(8080);

【讨论】:

@SergeyYaratskiy 是否可以不使用任何库?这样我就可以通过使用边界号或内容配置来获取表单数据? 当然,从我给你的库中,查看文件github.com/felixge/node-formidable/blob/master/lib/…,例如从第273行开始。 hmm 我还需要multipart_parser等用户库【参考方案3】:

我一直在使用 npm 模块multiparty。这很简单。

在您的请求处理程序中,执行类似的操作。

var form = new multiparty.Form();
form.parse(req, function(err, fields, files) 
  res.writeHead(200, 'content-type': 'text/plain');
  res.write('received upload:\n\n');
  res.end(util.inspect(fields: fields, files: files));
);

【讨论】:

是否可以在没有任何库的情况下使用它。所以只需分割边界或使用 content-disposition 获取数据 当然可以。但是您必须解析包括标头在内的整个 HTTP 请求。请问您为什么不想使用模块?如果它们是纯 JS 模块,那么使用它们或自己编写它们真的没有区别。 p.s 抱歉,我讨厌成为那种在有充分理由的情况下说“你为什么要那样做”的人。 我知道,但是在我参与的学校项目中,我们不允许使用任何图书馆。所以基本上我们应该重建一个模块。我不知道为什么...

以上是关于如何在没有快递的情况下处理多部分/表单数据请求的主要内容,如果未能解决你的问题,请参考以下文章

如何从反应提交多部分/表单数据到表达?

PHP多部分表单数据PUT请求?

如何在邮递员的同一请求中发送多部分/表单数据和嵌套 json?

如何在没有 JavaScript 的情况下设计可扩展的 HTML 表单?

如何创建包含多部分表单数据的可重复 POST 请求?

如何在多部分/表单数据请求中发送对象而不在 Angular 中转换为字符串