Node.js HTTP服务器中的文件、图片上传的方法
HTTP协议中,multipart/form-data格式用于向服务器发送二进制数据,通过这一内容类型(Content-Type)可以实现文件、图片的上传。由于这种格式发送的是二进制数据,在服务器端接收和处理数据时会与其它内容类型有所有区别。
HTTP协议中的文件上传
最早的HTTP协议中是不支持文件上传的,在1995年制定的rfc1867规范中,在HTTPPOST请求的内容类型Content-Type中扩展了multipart/form-data类型,该类型用于向服务器发送二进制数据,以便支持文件的上传。
POST上传文件
我们通过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服务器:
consthttp=require('http'); constfs=require('fs'); constutil=require('util'); constquerystring=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( ''+ ' '+ ' '+ '' ); }elseif(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函数解析并保存文件,其解析代码如下:
functionparseFile(req,res){ req.setEncoding('binary'); varbody='';//文件数据 varfileName='';//文件名 //边界字符串 varboundary=req.headers['content-type'].split(';')[1].replace('boundary=',''); req.on('data',function(chunk){ body+=chunk; }); req.on('end',function(){ varfile=querystring.parse(body,'\r\n',':') //只处理图片文件 if(file['Content-Type'].indexOf("image")!==-1) { //获取文件名 varfileInfo=file['Content-Disposition'].split(';'); for(valueinfileInfo){ 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)) varentireData=body.toString(); varcontentTypeRegex=/Content-Type:image\/.*/; contentType=file['Content-Type'].substring(1); //获取文件二进制数据开始位置,即contentType的结尾 varupperBoundary=entireData.indexOf(contentType)+contentType.length; varshorterData=entireData.substring(upperBoundary); //替换开始位置的空格 varbinaryDataAlmost=shorterData.replace(/^\s\s*/,'').replace(/\s\s*$/,''); //去除数据末尾的额外数据,即:"--"+boundary+"--" varbinaryData=binaryDataAlmost.substring(0,binaryDataAlmost.indexOf('--'+boundary+'--')); //保存文件 fs.writeFile(fileName,binaryData,'binary',function(err){ res.end('图片上传完成'); }); }else{ res.end('只能上传图片文件'); } }); }
req是一个IncomingMessage对象,而该对象又实现了ReadableStream,所以我们可以用流的方式来接收数据。数据接收完成了,按rfc1867规范进行了数据处理,并通过fs模块保存了文件。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。