通常情况下,我们想在网页上上传一个文件的时候,会采用<input type="file">标签,但是你有没有想过,为什么通过这样一个标签,服务器端就能获取到文件数据呢?我们先来写这样一个页面:

 <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--

从中我们可以看出一些不同,一个就是在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来的代码,仅供参考:

 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

HTTP上传文件探究的更多相关文章

  1. WebService支持多平台上传文件的实现

    WebService支持多平台上传文件的实现   要使用网站上传文件,在ASP.NET的范畴,我基本上能想到的有两类,一类是通过HTTP POST请求获得文件信息,另外一类是通过WebService或 ...

  2. IE8/9 JQuery.Ajax 上传文件无效

    IE8/9 JQuery.Ajax 上传文件有两个限制: 使用 JQuery.Ajax 无法上传文件(因为无法使用 FormData,FormData 是 HTML5 的一个特性,IE8/9 不支持) ...

  3. 三种上传文件不刷新页面的方法讨论:iframe/FormData/FileReader

    发请求有两种方式,一种是用ajax,另一种是用form提交,默认的form提交如果不做处理的话,会使页面重定向.以一个简单的demo做说明: html如下所示,请求的路径action为"up ...

  4. asp.net mvc 上传文件

    转至:http://www.cnblogs.com/fonour/p/ajaxFileUpload.html 0.下载 http://files.cnblogs.com/files/fonour/aj ...

  5. app端上传文件至服务器后台,web端上传文件存储到服务器

    1.android前端发送服务器请求 在spring-mvc.xml 将过滤屏蔽(如果不屏蔽 ,文件流为空) <!-- <bean id="multipartResolver&q ...

  6. .net FTP上传文件

    FTP上传文件代码实现: private void UploadFileByWebClient() { WebClient webClient = new WebClient(); webClient ...

  7. 通过cmd完成FTP上传文件操作

    一直使用 FileZilla 这个工具进行相关的 FTP 操作,而在某一次版本升级之后,发现不太好用了,连接老是掉,再后来完全连接不上去. 改用了一段时间的 Web 版的 FTP 工具,后来那个页面也 ...

  8. 前端之web上传文件的方式

    前端之web上传文件的方式 本节内容 web上传文件方式介绍 form上传文件 原生js实现ajax上传文件 jquery实现ajax上传文件 form+iframe构造请求上传文件 1. web上传 ...

  9. Django session cookie 上传文件、详解

    session 在这里先说session 配置URL from django.conf.urls import patterns, include, url from django.contrib i ...

随机推荐

  1. SQL增加,删除,更改表中字段

    1. 向表中添加新的字段 alter  table  table_name  add  column_name  varchar2(20) not null 2. 删除表中的一个字段 delete t ...

  2. 洛谷P1475 控制公司 Controlling Companies

    P1475 控制公司 Controlling Companies 66通过 158提交 题目提供者该用户不存在 标签USACO 难度普及/提高- 提交  讨论  题解 最新讨论 暂时没有讨论 题目描述 ...

  3. 复习下 AJAX

    什么是AJAx:Asynchronous Javascript and XML中文意思:异步JavaScript 和XML批一种创建交互式网页应用的网页开发技术.AJAX优点1.Ajax 在本质上是一 ...

  4. 关键字 this 的作用

    1.关键字 this ①是指当前对象自己 当一个类中要明确指出使用对象自己的变量或函数时,就应该加上this关键字,小栗子a如下: public class A { string Name = &qu ...

  5. SQL Server 数据库基础编程

    Ø Go批处理语句 用于同时执行多个语句 Ø 使用.切换数据库 use master go     Ø 创建.删除数据库   方法1.   --判断是否存在该数据库,存在就删除 if (exists ...

  6. about control %CPU

    #include <stdio.h> #include <unistd.h> #include <stdlib.h> #define timesize 200000 ...

  7. Android IOS WebRTC 音视频开发总结(三十)-- ice协议异同

    本文主要介绍ICE与SDP交互过程中的一些知识,支持原创,文章来博客园RTC.Blacker,转载请说明出处. 用过libjingle模块的同学都知道连接过程中candidate的获取与连接流程,一边 ...

  8. Java学生管理系统项目案例

    这是一个不错的Java学生管理系统项目案例,希望能够帮到大家的学习吧. 分代码如下 package com.student.util; import java.sql.Connection; impo ...

  9. MVC开发Markdown编辑器(1)

    MVC markdown MVC开发Markdown编辑器(1) 前言 安装 解析 结束语 前言 想在近段时间通过mvc开发个人博客,编辑器希望是markdown风格的,这样写文字会很方便.首先先解决 ...

  10. 鼠标HOVER时区块动画旋转变色的CSS3样式掩码

    鼠标hover时区块动画旋转变色的css3样式掩码<!DOCTYPE html> <html> <head> <meta charset="utf- ...