基于Express框架使用POST上传文件
来源:互联网 发布:害羞知乎 编辑:程序博客网 时间:2024/06/11 12:32
"The Hypertext Transfer Protocol (HTTP) is an application-level protocol for distributed, collaborative, hypermedia information systems."[10]
"The HTTP protocol is a request/response protocol. A client sends a request to the server in the form of a request method, URI, and protocol version, followed by a MIME-like message containing request modifiers, client information, and possible body content over a connection with a server. The server responds with a status line, including the message's protocol version and a success or error code, followed by a MIME-like message containing server information, entity metainformation, and possible entity-body content."[10]
Request
"A request message from a client to a server includes, within the first line of that message, the method to be applied to the resource, the identifier of the resource, and the protocol version in use."[10]
Request = Request-Line ; Section 5.1 *(( general-header ; Section 4.5 | request-header ; Section 5.3 | entity-header ) CRLF) ; Section 7.1 CRLF [ message-body ] ; Section 4.3
Request-Line(请求行)[10]
"The Request-Line begins with a method token, followed by the Request-URI and the protocol version, and ending with CRLF. The elements are separated by SP characters. No CR or LF is allowed except in the final CRLF sequence."[10]
Request-Line = Method SP Request-URI SP HTTP-Version CRLF
请求行中的Method[10]
The Method token indicates the method to be performed on the resource identified by the Request-URI. The method is case-sensitive.
Method = "OPTIONS" ; Section 9.2 | "GET" ; Section 9.3 | "HEAD" ; Section 9.4 | "POST" ; Section 9.5 | "PUT" ; Section 9.6 | "DELETE" ; Section 9.7 | "TRACE" ; Section 9.8 | "CONNECT" ; Section 9.9 | extension-method extension-method = token
The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. POST is designed to allow a uniform method to cover the following functions:
- Annotation of existing resources; - Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles; - Providing a block of data, such as the result of submitting a form, to a data-handling process; - Extending a database through an append operation.The actual function performed by the POST method is determined by the server and is usually dependent on the Request-URI. The posted entity is subordinate to that URI in the same way that a file is subordinate to a directory containing it, a news article is subordinate to a newsgroup to which it is posted, or a record is subordinate to a
database."[10]
" The action performed by the POST method might not result in a resource that can be identified by a URI. In this case, either 200 (OK) or 204 (No Content) is the appropriate response status, depending on whether or not the response includes an entity that describes the result."[10]
"If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header (see section 14.30)."[10]
"Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent to retrieve a cacheable resource."[10]
Form的 Html代码[9]
<p> <form method="post" id="upload" action="/fileupload" enctype="multipart/form-data"> <input type="file" name="uploader"/> <input type="submit" value="upload"/> </form> </p>
[3] When a user clicks the submit button on Web form with this tag, the Web browser should collect data from all input fields and submit to the specified URL. The browser should also follow the following rules:
If "get" is specified as the method, data will be submitted as part of the GET header line of the HTTP request.
If "post" is specified as the method, data will be submitted as the body of the HTTP request.
If "application/x-www-form-urlencoded" is specified as the encryption type, names and values of all input fields will be encoded together based on the URL encoding specification.
If no encryption type is specified, "application/x-www-form-urlencoded" will be used as the default encryption type.
If "multipart/form-data" is specified as the encryption type, data will be submitted in multiple parts with one part for one input field. Parts are separated by a boundary identification string, which should be given as the "boundary" attribute of the "Content-type" header line if the HTTP request.
If "application/x-www-form-urlencoded" is specified as the encryption type, only the file name of a FILE type input field will be submitted as the value of the field. The content of the file will not be submitted.
If "multipart/form-data" is specified as the encryption type, only the file name of a FILE type input field will be submitted as the value of the field. The content of the file will not be submitted.
When "multipart/form-data" is specified as the encryption type, the format of the HTTP request body should look like this:
--boundary_identification_stringinput_part_1--boundary_identification_stringinput_part_2--boundary_identification_stringinput_part_3......--boundary_identification_string--
样例[5]:
POST http://www.baidu.com/ HTTP/1.1Host: www.baidu.comContent-Length: 495Content-Type: multipart/form-data; boundary=---------------------------7db2d1bcc50e6e-----------------------------7db2d1bcc50e6eContent-Disposition: form-data; name="myText"hello world-----------------------------7db2d1bcc50e6eContent-Disposition: form-data; name="upload1"; filename="C:\file1.txt"Content-Type: text/plainThis is file1.-----------------------------7db2d1bcc50e6eContent-Disposition: form-data; name="upload2"; filename="C:\file2.txt"Content-Type: text/plainThis is file2, it's longer.-----------------------------7db2d1bcc50e6e--
[5]很显然它们两个选择了不同的数据“模式”作为边界——事实上,浏览器提交两次数据时,使用的边界也可能不会相同,这都没有问题。
选择了边界之后,便会将它放在头部的Content-Type里传递给服务器端,实际需要传递的数据便可以分割为“段”,每段便是“一项”数据。从上面的内容中大家应该都能看出数据传输的规范,因此便不做细谈了。只强调几点:
数据均无需额外编码,直接传递即可,例如您可以看出上面的示例中的“空格”均没有变成加号。至于这里您可以看到清晰地文字内容,是因为我们上传了仅仅包含可视ASCII码的文本文件,如果您上传一个普通的文件,例如图片,捕获到的数据则几乎完全不可读了。
IE和Chrome在filename的选择策略上有所不同,前者是文件的完整路径,而后者则仅仅是文件名。
数据内容以两条横线结尾,并同样以一个换行结束。在网络协议中一般都以连续的CR、LF(即\r、\n,或0x0D、Ox0A)字符作为换行,这与Windows的标准一致。如果您使用其他操作系统,则需要考虑它们的换行符。
处理POST请求的脚本代码
//var busboy = require('connect-busboy');// ... app.use(busboy()); // ... app.post('/fileupload', function(req, res) { var fstream; req.pipe(req.busboy); req.busboy.on('file', function (fieldname, file, filename) { console.log("Uploading: " + filename); var fstream; fstream = fs.createWriteStream(__dirname + '/uploads/' + filename); fstream.on('error', function(err) { console.log(String( err ) ); file.unpipe(); fstream.end(); res.send(filename + ' uploadding failed.'); }); file.pipe(fstream); fstream.on('close', function () { res.send(filename + ' uploadding success.'); // res.redirect('back'); }); });});
浏览器通过form 表单来上传文件时使用的是"RFC 1867 - Form-based File Upload in HTML"协议[4]。使用的数据包格式是:
POSThttp://www.foo.com/ HTTP/1.1Host: www.foo.comContent-Length: 199Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywr3X7sXBYQQ4ZF5G------WebKitFormBoundarywr3X7sXBYQQ4ZF5GContent-Disposition: form-data; name="myfile"; filename="upload.txt"Content-Type: text/plainhello world------WebKitFormBoundarywr3X7sXBYQQ4ZF5G--
使用Node.js生成上述格式的数据包,即可实现文件上传。使用异步API样例代码如下[1]:
const http = require('http');const fs = require('fs');//生成分隔数据var boundaryKey = '----WebKitFormBoundaryjLVkbqXtIi0YGpaB'; var currentDir = __dirname + '/';var sourceFileName = 'abc.txt'var filePath = currentDir + sourceFileName;// checking filetry { stat = fs.statSync(filePath); console.log("File exists."); } catch (e) { if (e.code == 'ENOENT') { console.log("File does not exist."); //return false; } console.log("Exception fs.statSync (" + filePath + "): " + e); // console.log(filePath + " does not exist."); }// var options = { hostname: 'localhost', port: 80, path: '/fileupload', method: 'POST'}//读取需要上传的文件内容fs.readFile( filePath, function (err, data) { //拼装分隔数据段 var payload = '--' + boundaryKey + '\r\n' ; payload += 'Content-Disposition:form-data; name="myfile"; filename="'+ sourceFileName + '"\r\n' ; payload += 'Content-Type:text/plain\r\n\r\n'; payload += data; payload += '\r\n--' + boundaryKey + '--'; //发送请求 var req = http.request(options, function (res) { res.setEncoding('utf8'); res.on('data', function (chunk) { console.log('body:' + chunk); }); }); req.on('error', function(e) { console.error("error:"+e); }); //把boundary、要发送的数据大小以及数据本身写进请求 req.setHeader('Content-Type', 'multipart/form-data; boundary='+boundaryKey+''); req.setHeader('Content-Length', Buffer.byteLength(payload, 'utf8')); req.write(payload); req.end();});
使用同步API样例代码如下:
const http = require('http');const fs = require('fs');//生成分隔数据var boundaryKey = '----WebKitFormBdaendyjLVkbqXtIi0YGpaB'; var currentDir = __dirname + '/';var sourceFileName = 'abc.txt'var filePath = currentDir + sourceFileName;// checking filetry { stat = fs.statSync(filePath); console.log("File exists."); } catch (e) { if (e.code == 'ENOENT') { console.log("File does not exist."); //return false; } console.log("Exception fs.statSync (" + filePath + "): " + e); // console.log(filePath + " does not exist."); }// var options = { hostname: 'localhost', port: 80, path: '/fileupload', method: 'POST'}var fileData = fs.readFileSync( filePath, "utf8"); var payload = '--' + boundaryKey + '\r\n' ; payload += 'Content-Disposition:form-data; name="myfile"; filename="'+ sourceFileName + '"\r\n' ; payload += 'Content-Type:text/plain\r\n\r\n'; payload += fileData; payload += '\r\n--' + boundaryKey + '--'; //发送请求var req = http.request(options, function (res) { res.setEncoding('utf8'); res.on('data', function (chunk) { console.log('body:' + chunk); });});req.on('error', function(e) { console.error("error:"+e);});//把boundary、要发送的数据大小以及数据本身写进请求req.setHeader('Content-Type', 'multipart/form-data; boundary='+boundaryKey+'');req.setHeader('Content-Length', Buffer.byteLength(payload, 'utf8'));req.write(payload);req.end();
nodejs的发送上传文件代码[6]
// modules //const http = require('http');var path = require('path');const fs = require('fs'); var option = { host: "127.0.0.1", port: "80", path: "/fileupload", method: 'post'};// var boundaryKey = '----WebKitFormBoundaryjLVk4bqXtIi0YGpaB'; var formFieldName = "files";var uploadFilePath = "./abc.txt"; var filename = path.basename(uploadFilePath);// checking file try { stat = fs.statSync( uploadFilePath ); console.log("File exists."); } catch (e) { if (e.code == 'ENOENT') { console.log("File does not exist."); //return false; } console.log("Exception fs.statSync (" + uploadFilePath + "): " + e); } var part = []; part.push('--' + boundaryKey); part.push('Content-Type: application/octet-stream'); part.push('Content-Disposition: form-data; name="' + formFieldName + '"; filename="' + filename + '"'); part.push('Content-Transfer-Encoding: binary'); part.push('\r\n');var request = http.request( option, function(res) { // show results console.log('STATUS: ' + res.statusCode); //console.log('HEADERS: ' + JSON.stringify(res.headers)); //console.log('Date: ' + JSON.stringify(res.headers)[2] ); res.setEncoding('utf8'); res.on('data', function(chunk) { console.log('BODY: ' + chunk); }); res.on('end', function(err) { console.log("Uploading " + uploadFilePath + ' complete.'); });}); request.on("error", function(e) { console.log('upload Error: ' + e.message); }) request.setHeader('Content-Type', 'multipart/form-data; boundary="' + boundaryKey + '"'); request.write( part.join('\r\n') );////var readStream = fs.createReadStream(uploadFilePath, { bufferSize: 8 * 1024 }); readStream.on('end', function(err) { if (err) { console.error(err); return; } request.write('\r\n--' + boundaryKey + '--'); request.end(); // Logging console.log("Sending " + uploadFilePath + ' content complete.'); }); readStream.pipe( request, { end: true //readStream });
参考文献
[1] http://www.aichengxu.com/javascript/24609788.htm
[2] http://stackoverflow.com/questions/5744990/how-to-upload-a-file-from-node-js
[3] http://www.herongyang.com/JSP/File-Upload-RFC-1867-Form-based-File-Upload.html
[4] https://www.w3.org/TR/device-upload
[5] http://kb.cnblogs.com/page/95545/
[6] https://hzxiaosheng.bitbucket.io/work/2014/03/09/download-and-upload-file-with-nodejs.html
[9] http://www.faqs.org/rfcs/rfc1867.html
[10] http://www.faqs.org/rfcs/rfc2616.html
http://blog.csdn.net/puncha/article/details/9015317
http://liuxufei.com/blog/jishu/798.html
- 基于Express框架使用POST上传文件
- 基于Express框架使用POST传递Form数据
- 使用node的express框架进行基于cors的get和post跨域
- node.js express框架文件上传路径
- node使用express搭建简单web框架并实现文件上传
- Express使用Multer实现文件上传
- 使用cURL POST上传文件
- 基于 HTTP POST multipart 的文件上传
- NodeJS使用Express框架处理客户端POST请求
- 使用express搭建了框架后,用multer处理post请求传的文件或图片注意的问题
- Nodejs进阶 基于express+multer的文件上传
- nodejs express 上传文件
- Express 文件上传
- node+express文件上传
- 快捷使用Retrofit网络请求框架(get请求,post请求,上传文件)
- android 使用post方式上传文件
- 使用libcurl POST数据和上传文件
- Python中使用POST方式上传文件
- Java内存模型——《深入理解Java虚拟机》笔记
- PCLVisualizer窗口事件注册
- 数据和C
- jquery遍历table的tr获取td的值
- jquery获取各种宽高、js的获取各种宽高及屏幕高度详解
- 基于Express框架使用POST上传文件
- c++如何将文件复制到其他地方
- 【VS开发】C++调用外部程序
- [李景山php]每天laravel-20161020|Request.php-1
- java界面编程---练习1
- 学习ecshop
- HibernateTemplate的使用
- 数据结构实验之排序二:交换排序
- Android trouble-shootingwindow -> Show Views -> device