HTTP上传文件探究
2014-03-10 23:15
417 查看
通常情况下,我们想在网页上上传一个文件的时候,会采用<input type="file">标签,但是你有没有想过,为什么通过这样一个标签,服务器端就能获取到文件数据呢?我们先来写这样一个页面:
可以看到这只是一个普通的form表单,里边有两个字段,一个是file类型,另一个是普通的text类型。同时我们还要注意的是,在form的属性里我们设置了提交方式为post,且 enctype=multipart/form-data。那么我们提交后浏览器发送的请求格式是什么样的呢?下边是chrome的开发者工具贴过来的数据:
从中我们可以看出一些不同,一个就是在Content-Type的值为 multipart/form-data,而一般情况下我们经常看到的为text/html之类的(对照表), 此外就是表单的content内容比较怪异,仔细看下发现content被 ------WebKitFormBoundarykyKevNik4tKTFv0c划分为两部分,而第一部分通过 Content-Disposition可以看出这是一个文件,而且 Content-Type: image/png指定了文件类型,下边跟着文件的数据;而第二部分则对应我们在 表单中提交的otherparm参数。
这样一分析,input:file上传文件的方式就很明白了,我们后台只要按照此格式对数据进行解析即可。其实这样的格式是遵循 Request For Comments( RFC )1867对文件上传的规定。
那么我们后台具体要怎么处理呢?对于java web程序而言,我们通常采用COS、apache common-upload组件等。那么如果我们想自己解析呢,下边是一段copy来的代码,仅供参考:
参考代码
最后附上自己写的一个测试例子: DEMO 。
<html> <head> </head> <body> <form action="http://127.0.0.1:8899/upload.do" method="post" enctype="multipart/form-data" > <input type="file" name="myfile" /> <input name="otherparm" value="aaaaa" /> <input type="submit" value="aa" /> </form> </body> </html>
可以看到这只是一个普通的form表单,里边有两个字段,一个是file类型,另一个是普通的text类型。同时我们还要注意的是,在form的属性里我们设置了提交方式为post,且 enctype=multipart/form-data。那么我们提交后浏览器发送的请求格式是什么样的呢?下边是chrome的开发者工具贴过来的数据:
Request URL:http://127.0.0.1:8899/upload.do Request Headers CAUTION: Provisional headers are shown. Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Content-Type:multipart/form-data; boundary=----WebKitFormBoundarykyKevNik4tKTFv0c Origin:null Referer: User-Agent:Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36 Request Payload ------WebKitFormBoundarykyKevNik4tKTFv0c(\n) Content-Disposition: form-data; name="myfile"; filename="logo.png"(\n) Content-Type: image/png(\n) (\n) (此处为file数据,chrome下显示为空白)(\n\r) ------WebKitFormBoundarykyKevNik4tKTFv0c(\n) Content-Disposition: form-data; name="otherparm"(\n) (\n) aaaaa(\n\r) ------WebKitFormBoundarykyKevNik4tKTFv0c-- |
这样一分析,input:file上传文件的方式就很明白了,我们后台只要按照此格式对数据进行解析即可。其实这样的格式是遵循 Request For Comments( RFC )1867对文件上传的规定。
那么我们后台具体要怎么处理呢?对于java web程序而言,我们通常采用COS、apache common-upload组件等。那么如果我们想自己解析呢,下边是一段copy来的代码,仅供参考:
private byte[] parse(HttpServletRequest request) throws IOException { final int NONE = 0; final int DATAHEADER = 1; final int FILEDATA = 2; final int FIELDDATA = 3; final int MXA_SEGSIZE = 1000 * 1024 * 10;//每批最大的数据量 10M String contentType = request.getContentType();// 请求消息类型 String fieldname = ""; // 表单域的名称 String fieldvalue = ""; // 表单域的值 String filename = ""; // 文件名 String boundary = ""; // 分界符 String lastboundary = ""; // 结束符 String filePath = ""; Hashtable<String, String> formfields = new Hashtable<String, String>(); int filesize = 0; // 文件长度 int pos = contentType.indexOf("boundary="); if (pos != -1) { // 取得分界符和结束符 pos += "boundary=".length(); boundary = "--" + contentType.substring(pos); lastboundary = boundary + "--"; } int state = NONE; // 得到数据输入流reqbuf DataInputStream in = new DataInputStream(request.getInputStream()); // 将请求消息的实体送到b变量中 int totalBytes = request.getContentLength(); String message = ""; if (totalBytes > MXA_SEGSIZE) {//每批大于10m时 message = "Each batch of data can not be larger than " + MXA_SEGSIZE / (1000 * 1024) + "M"; return null; } byte[] b = new byte[totalBytes]; in.readFully(b); in.close(); String reqContent = new String(b, "UTF-8");// BufferedReader reqbuf = new BufferedReader(new StringReader(reqContent)); boolean flag = true; int i = 0; while (flag == true) { String s = reqbuf.readLine(); if ((s == null) || (s.equals(lastboundary))) break; switch (state) { case NONE: if (s.startsWith(boundary)) { state = DATAHEADER; i += 1; } break; case DATAHEADER: pos = s.indexOf("filename="); if (pos == -1) { // 将表单域的名字解析出来 pos = s.indexOf("name="); pos += "name=".length() + 1; s = s.substring(pos); int l = s.length(); s = s.substring(0, l - 1); fieldname = s; state = FIELDDATA; } else { // 将文件名解析出来 String temp = s; pos = s.indexOf("filename="); pos += "filename=".length() + 1; s = s.substring(pos); int l = s.length(); s = s.substring(0, l - 1);// 去掉最后那个引号” filePath = s; pos = s.lastIndexOf("\\"); s = s.substring(pos + 1); filename = s; // 从字节数组中取出文件数组 pos = byteIndexOf(b, temp, 0); b = subBytes(b, pos + temp.getBytes().length + 2, b.length);// 去掉前面的部分 int n = 0; /** * 过滤boundary下形如 Content-Disposition: form-data; name="bin"; * filename="12.pdf" Content-Type: application/octet-stream * Content-Transfer-Encoding: binary 的字符串 */ while ((s = reqbuf.readLine()) != null) { if (n == 1) break; if (s.equals("")) n++; b = subBytes(b, s.getBytes().length + 2, b.length); } pos = byteIndexOf(b, boundary, 0); if (pos != -1) b = subBytes(b, 0, pos - 1); filesize = b.length - 1; formfields.put("filesize", String.valueOf(filesize)); state = FILEDATA; } break; case FIELDDATA: s = reqbuf.readLine(); fieldvalue = s; formfields.put(fieldname, fieldvalue); state = NONE; break; case FILEDATA: while ((!s.startsWith(boundary)) && (!s.startsWith(lastboundary))) { s = reqbuf.readLine(); if (s.startsWith(boundary)) { state = DATAHEADER; break; } } break; } } return b; } // 字节数组中的INDEXOF函数,与STRING类中的INDEXOF类似 public static int byteIndexOf(byte[] b, String s, int start) { return byteIndexOf(b, s.getBytes(), start); } // 字节数组中的INDEXOF函数,与STRING类中的INDEXOF类似 public static int byteIndexOf(byte[] b, byte[] s, int start) { int i; if (s.length == 0) { return 0; } int max = b.length - s.length; if (max < 0) return -1; if (start > max) return -1; if (start < 0) start = 0; search: for (i = start; i <= max; i++) { if (b[i] == s[0]) { int k = 1; while (k < s.length) { if (b[k + i] != s[k]) { continue search; } k++; } return i; } } return -1; } // 用于从一个字节数组中提取一个字节数组 public static byte[] subBytes(byte[] b, int from, int end) { byte[] result = new byte[end - from]; System.arraycopy(b, from, result, 0, end - from); return result; } // 用于从一个字节数组中提取一个字符串 public static String subBytesString(byte[] b, int from, int end) { return new String(subBytes(b, from, end)); }
参考代码
最后附上自己写的一个测试例子: DEMO 。
相关文章推荐
- 解决MVC下使用uploadify上传文件时HttpPostedFileBase为空的问题
- [C#]在WinForm下使用HttpWebRequest上传文件并显示进度[转]
- 免费HTTP上传文件控件WebUploadFile.ocx发布,让实现Word在线编辑器变得容易
- curl的http上传文件代码
- httpClient 使用multipart/form-data 类型上传文件及表单
- Android学习之使用HttpURLConnection同时上传文本和多个文件
- Web文件上传控件发布-Xproer.HttpUploader2
- HttpClient 4 - 文件上传
- 使用HttpURLConnection上传文件
- C# Http以文件的形式上传文件
- HttpClient中转上传文件
- httpclient 文件上传
- springMVC + hadoop + httpclient 文件上传请求直接写入hdfs
- IHttpModule 分块上传大文件
- nodejs-http 对form表单上传文件数据的解析过程
- 解决ajax XMLHttpRequest 跨域上传文件失败问题
- jquery.uploadify 上传文件出现HTTP 404错误
- Http大文件分段上传
- android-async-http byte字节数组上传文件
- (总结3)WinForm中文件上传服务器:HttpWebRequest .