欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Node.js HTTP服务器中的文件、图片上传的方法

程序员文章站 2022-03-18 17:01:41
http协议中,multipart/form-data格式用于向服务器发送二进制数据,通过这一内容类型(content-type)可以实现文件、图片的上传。由于这种格式发送的是二进制数...

http协议中,multipart/form-data格式用于向服务器发送二进制数据,通过这一内容类型(content-type)可以实现文件、图片的上传。由于这种格式发送的是二进制数据,在服务器端接收和处理数据时会与其它内容类型有所有区别。

http协议中的文件上传

最早的http协议中是不支持文件上传的,在1995年制定的rfc1867规范中,在http post请求的内容类型content-type中扩展了multipart/form-data类型,该类型用于向服务器发送二进制数据,以便支持文件的上传。

post上传文件

我们通过form表单提交文件时,会构造类似像下面这样一个表单:

<form enctype="multipart/form-data" action="_url_" method="post">
 <input name="userfile1" type="file">
 <input type="submit" value="发送文件">
</form>

在使用form提交表单数据时,默认的编码格式为application/x-www-form-urlencoded,上传文件时需要通过enctype属性将编码方式设置为multipart/form-data。

http数据提交与服务器数据解析

在包含请求体的请求中,提交的数据会按指定编码类型进行编码,而客户端会按编码方式设置请求头中的content-type字段。
在一个application/x-www-form-urlencoded编码的请求中,会设置一个如下的请求头:

content-type:application/x-www-form-urlencoded

而用于文件上传的编码方式multipart/form-data,会设置一个如下的请求头:

content-type: multipart/form-data, boundary=aab03x

服务器数据接收与解析

对于一个编码方式为application/x-www-form-urlencoded的请求来说,会对提交内容进行url编码。服务器会收到类似如下内容:

post / http/1.1
content-type: application/x-www-form-urlencoded
accept-encoding: gzip, deflate
host: itbilu.com
content-length: 23
connection: keep-alive
cache-control: max-age=0

key1=value1&key2=value2

请求头与请求体之间会有一个空行,服务器会对请求体以querystring的方式进行解码。

而对一个multipart/form-data的文件上传请求来说,收到的内容类似如下:

post / http/1.1
content-type: multipart/form-data; boundary=----webkitformboundaryyn9yywo9esipybix
accept-encoding: gzip, deflate
host: itbilu.com
content-length: 22646
connection: keep-alive
cache-control: max-age=0

------webkitformboundaryoqbx9oybhx4sf1yq
content-disposition: form-data; name="myname"

itbilu.com
------webkitformboundaryyn9yywo9esipybix
content-disposition: form-data; name="upload"; filename="41gilecho3l.jpg"
content-type: image/jpeg

����jfif��c // 文件的二进制数据
……
--------webkitformboundaryyn9yywo9esipybix--

在请求头的content-type字段中,除了编码类型为multipart/form-data描述外,还有一个boundary属性,这是客户端随机生成的一个数据边界描述。

如上所示,文件上传时内容是分段传输的,每一boundary表示一个fild(form表单控值)边界。

如上面示例所示,上传文件时除内容描述外还包含一个的content-type文件mime的描述,其后是一个空行和文件的二进制数据。所有的表单数据结束后,会有一个”–”+boundary+”–”结束符。而服务器接收到数据后,同样会根据boundary来进行数据的接收和解析。

node.js中处理图片/文件上传

node.js中处理文件上传的第三方模块,本站曾经介绍过使用formidable模块处理文件上传,下面简单介绍使用node.js原生环境处理图片上传,上传文件时也可以参考处理。

首先,使用node.js的http模块创建一个http服务器:

const http = require('http');
const fs = require('fs');
const util = require('util');
const querystring =require('querystring');

//用http模块创建一个http服务端
http.createserver(function(req, res) {
 if (req.url == '/upload' && req.method.tolowercase() === 'get') {
  //显示一个用于文件上传的form
  res.writehead(200, {'content-type': 'text/html'});
  res.end(
   '<form action="/upload" enctype="multipart/form-data" method="post">'+
    '<input type="file" name="upload" multiple="multiple" />'+
    '<input type="submit" value="upload" />'+
   '</form>'
  );
 } else if (req.url == '/upload' && req.method.tolowercase() === 'post') {
  if(req.headers['content-type'].indexof('multipart/form-data')!==-1)
   parsefile(req, res)
  } else {
   res.end('其它提交方式');
  }
}).listen(3000);

在这一步中,我们创建http 服务器,当get请求时,会加载一上用于文件上传的form表单。上传文件会通过post方式提交到服务器,这时服务端会通过parsefile函数解析并保存文件,其解析代码如下:

function parsefile (req, res) {
 req.setencoding('binary');
 var body = '';  // 文件数据
 var filename = ''; // 文件名
 // 边界字符串
 var boundary = req.headers['content-type'].split('; ')[1].replace('boundary=','');
 req.on('data', function(chunk){
  body += chunk;
 });

 req.on('end', function() {
  var file = querystring.parse(body, '\r\n', ':')

  // 只处理图片文件
  if (file['content-type'].indexof("image") !== -1)
  {
   //获取文件名
   var fileinfo = file['content-disposition'].split('; ');
   for (value in fileinfo){
    if (fileinfo[value].indexof("filename=") != -1){
     filename = fileinfo[value].substring(10, fileinfo[value].length-1);

     if (filename.indexof('\\') != -1){
      filename = filename.substring(filename.lastindexof('\\')+1);
     }
     console.log("文件名: " + filename);
    }
   }

   // 获取图片类型(如:image/gif 或 image/png))
   var entiredata = body.tostring();
   var contenttyperegex = /content-type: image\/.*/;

   contenttype = file['content-type'].substring(1);

   //获取文件二进制数据开始位置,即contenttype的结尾
   var upperboundary = entiredata.indexof(contenttype) + contenttype.length;
   var shorterdata = entiredata.substring(upperboundary);

   // 替换开始位置的空格
   var binarydataalmost = shorterdata.replace(/^\s\s*/, '').replace(/\s\s*$/, '');

   // 去除数据末尾的额外数据,即: "--"+ boundary + "--"
   var binarydata = binarydataalmost.substring(0, binarydataalmost.indexof('--'+boundary+'--'));

   // 保存文件
   fs.writefile(filename, binarydata, 'binary', function(err) {
    res.end('图片上传完成');
   });
  } else {
   res.end('只能上传图片文件');
  }
 });
}

req是一个incomingmessage对象,而该对象又实现了readablestream,所以我们可以用流的方式来接收数据。数据接收完成了,按rfc1867规范进行了数据处理,并通过fs模块保存了文件。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。