node 对于 formdata 数据解析处理

文件上传数据格式:

------WebKitFormBoundaryjlaXz2OrLImcaQJb // 分界标识
Content-Disposition: form-data; name="file"; filename="hostFile.txt" // 头字段信息
Content-Type: text/plain // 内容类型

文件内容
------WebKitFormBoundaryjlaXz2OrLImcaQJb
Content-Disposition: form-data; name="fileSize"

1024
------WebKitFormBoundaryjlaXz2OrLImcaQJb
Content-Disposition: form-data; name="fileName"

text.txt
------WebKitFormBoundaryjlaXz2OrLImcaQJb-- // 后面两个 ‘--’ 代表结束

第一步:根据 request 的 data 事件监听取得请求信息及上传数据

第二步:取得请求头的 content-type 字段中的 boundary 后面的分界标识值

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryjlaXz2OrLImcaQJb
var m = this.headers['content-type'].match(/boundary=(?:"([^"]+)"|([^;]+))/i);
// m[1] || m[2]

第三步:在每次写入数据的时候,会根据规则获取头字段信息与文件内容或者字段值

var self = this;
req
  .on('error', function(err) {
    self._error(err);
  })
  .on('aborted', function() {
    self.emit('aborted');
    self._error(new Error('Request aborted'));
  })
  .on('data', function(buffer) {
    self.write(buffer); // 写入操作
  })
  .on('end', function() {
    if (self.error) {
      return;
    }
IncomingForm.prototype.write = function(buffer) {
  if (this.error) {
    return;
  }
  if (!this._parser) {
    this._error(new Error('uninitialized parser'));
    return;
  }

  this.bytesReceived += buffer.length;
  this.emit('progress', this.bytesReceived, this.bytesExpected);

  var bytesParsed = this._parser.write(buffer); // multipart_parse
  if (bytesParsed !== buffer.length) {
    this._error(new Error('parser error, '+bytesParsed+' of '+buffer.length+' bytes parsed'));
  }

  return bytesParsed;
};

// multipart_parse
MultipartParser.prototype.write = function(buffer) {
  var self = this,
      i = 0,
      len = buffer.length,
      prevIndex = this.index,
      index = this.index,
      state = this.state,
      flags = this.flags,
      lookbehind = this.lookbehind,
      boundary = this.boundary,
      boundaryChars = this.boundaryChars,
      boundaryLength = this.boundary.length,
      boundaryEnd = boundaryLength - 1,
      bufferLength = buffer.length,
      c,
      cl,
......
  for (i = 0; i < len; i++) {
......
  }
......
}

同时执行下面操作

// Content-Disposition: form-data; name="fileName"

headerField += b.toString(self.encoding, start, end);

headerValue += b.toString(self.encoding, start, end);

var m = headerValue.match(/name=("([^"]*)"|([^()<>@,;:\"/[]?={}s	/]+))/i);
if (headerField == 'content-disposition') {
  if (m) {
    part.name = m[2] || m[3] || '';
  }
  // headerValue.match(/filename=("(.*?)"|([^()<>@,;:\"/[]?={}s	/]+))($|;s)/i);
  part.filename = self._fileName(headerValue);
} else if (headerField == 'content-type') {
  part.mime = headerValue;
} else if (headerField == 'content-transfer-encoding') {
  part.transferEncoding = headerValue.toLowerCase();
}

根据 filename 字段的值来判断是该分界内容是字段值还是文件内容

if (part.filename === undefined) {
  var value = ''
    , decoder = new StringDecoder(this.encoding);

  part.on('data', function(buffer) {
    self._fieldsSize += buffer.length;
    if (self._fieldsSize > self.maxFieldsSize) {
      self._error(new Error('maxFieldsSize exceeded, received '+self._fieldsSize+' bytes of field data'));
      return;
    }
    value += decoder.write(buffer);
  });

  part.on('end', function() {
    self.emit('field', part.name, value);
  });
  return;
}
part.on(
'data', function(buffer) { self._fileSize += buffer.length; if (self._fileSize > self.maxFileSize) { self._error(new Error('maxFileSize exceeded, received '+self._fileSize+' bytes of file data')); return; } if (buffer.length == 0) { return; } self.pause(); file.write(buffer, function() { self.resume(); }); }); part.on('end', function() { file.end(function() { self._flushing--; self.emit('file', part.name, file); self._maybeEnd(); }); });

根据 buffer 数据获取头字段信息及内容的判断有点复杂,逻辑如此,具体操作还没理清,未完待续......

原文地址:https://www.cnblogs.com/kdcg/p/13536553.html