如何处理multipart/form-data请求而不需要使用express

3

所以我有一个表单:

  <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或其他类似的第三方库的情况下,获取表单字段的对象。是否可以使用边界数字来获取属性,或者在哪里可以看到发送了哪些数据?


为什么表单的编码类型是 _multipart/form-data_?你计划发送文件数据(例如用户上传的文件)吗? - Sᴀᴍ Onᴇᴌᴀ
3个回答

6
是的,"可以使用边界编号获取属性",不过您需要手动匹配字段并解析名称...
此外,为了使表单中的值被发送,需要在输入标签上设置属性名称。例如:
<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--

###解析表单字段

在下面的起始代码中,它检查请求头Content-Typeboundary(您可能还想确保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();
});

0
使用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 不使用任何库是否有可能?以便我只使用边界号或内容分发来获取表单数据? - user7292717
当然,从我给你的库中,查看文件https://github.com/felixge/node-formidable/blob/master/lib/incoming_form.js#L273,例如从第273行开始。 - Sergey Yarotskiy
嗯,我仍然需要用户库,例如multipart_parser。 - user7292717

0
我一直在使用 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 获取数据。 - user7292717
当然可以。但是你必须解析整个HTTP请求,包括头部。我可以问一下为什么你不想使用模块吗?如果它们是纯JS模块,那么使用它们和自己编写没有什么区别。 附言:抱歉,我不喜欢成为那个人,当可能有一个好的理由时,却说“你为什么要这样做”。 - Jay Shepherd
我知道,但是在我参与的学校项目中,我们不允许使用任何库。所以基本上我们需要重新构建一个模块。我不知道为什么... - user7292717

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接