现在有很多Web程序都有上传功能,实现上传功能的组件或框架也很多,如基于java的Commons FileUpload、还有Struts1.x和Struts2中带的上传文件功能(实际上,Struts2在底层也使用了Commons FileUpload)。

  虽然现在有很多上传组件可以利用,但是了解Web上传文件的原理,对于处理突然出现的问题会有很大的帮助,下面就来讲一下通过浏览器上传文件的基本原理。在了解了原理之后,就可以非常容易地自制满足自身需要的上传组件了。

众所周知,在客户端代码中需要使用<input type='file' name='file' />来选择要上传的文件,并上传,代码如上:

<html>
<head>
<title>upload</title>
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=GB18030">
</head> <body>
<form action="servlet/UploadFile" method="post"
enctype="multipart/form-data">
<input type="file" name="file1" id="file1" />
<input type="file" name="file2" id="file2" />
<input type="submit" value="上传" />
</form>
</body>
</html>

从上面的代码可以看出,有两个文件选择框(file1和file2),在上传文件时,<form>标签必须加上enctype="multipart/form-data",否则浏览器无法将文件内容上传到服务端。下面我们来做个实验。在Servlet的doPost方法中编写如下的代码。

 public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
java.io.InputStream is = request.getInputStream();
java.io.FileOutputStream fos = new java.io.FileOutputStream("d:\\out.txt"); byte[] buffer = new byte[8192];
int count = 0;
while((count = is.read(buffer)) >0)
{
fos.write(buffer, 0, count);
}
fos.close();
}

上面的功能非常简单,只是通过request获得一个InputStream对象,并通过这个对象从客户端获得发送过来的字节流(注意,一定要用字节流,因为,上传的文件可能是二进制文件,如图象文件,因此,使用字节流会更通用)。并将这些字节流保存在D盘的out.txt文件中。然后我们打开out.txt,文件的内容如图1所示:

由于out.txt是使用文本形式打开的,并且file1上传的是a.jpg(一个图象文件),因此,显示的是一些乱码。我们可以不用管它们。只需要看看这些内容的头部。我们很快就可以找到规律。每一个文件内容的头部都由“-----------------------------30514443229777”分隔,然后是这个文件的属性,如下:

Content-Disposition: form-data; name="file1"; filename="a.jpg"

Content-Type: image/jpeg

其中包含了文件选择框的name属性,还有上传的文件名(filename字段),要注意的,firefox在上传时,这个filename属性值只是文件名,如果使用IE,就是带路径的文件名,如D:"a.jpg。

接下来的规则就和HTTP的头一样了,以一个空行("r"n)分隔。后面就是文件的具体内容。现在最关键的文件的结尾,从图1可以看出,文件的结尾也是“-----------------------------30514443229777”,因此,可以断定,第一个上传的文件(包括文件头)是夹在两个“-----------------------------30514443229777”之间的。而“-----------------------------30514443229777”就是multipart/form-data协议的分隔符。但这里还有一个最关键的问题。这个分隔符每次上传都不一样,服务端是如何知道每次上传的这个分隔符的呢?

实际上,这个分隔符是通过HTTP请求头的Content-Type字段获得,可通过下面的代码输出这个字段值:

System.out.println(request.getHeader("Content-type"));

输出的内容如下:

multipart/form-data; boundary=---------------------------106712230227687

只要在服务端获得boundary后面的值即可。经过测试,Content-Type中的分隔符号中的“-”比实际上传的“-”少两个,不知是怎么回事。不过这没关系,我们可以认为每一个文件块是以""r"n—“结尾的,或是直接将从boundary获得的分隔符加两个“—”。而最后结尾的分隔符是“---------------------------106712230227687—”,后面多了两个“—”。

综合上述,也就是说,一个文件块是以“---------------------------106712230227687”开头,以“—”结尾,从图2可以看出这一切。

至于剩下的工作,就是按着上面的规则来分析这些字符流了。分析的方法很多。在这里就不详述了。

啦啦啦

Web上传文件的原理及实现的更多相关文章

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

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

  2. 第九篇:web之前端之web上传文件的方式

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

  3. Web上传文件

      客户端      相对于FTP文件上传,Web文件上传速度慢一些,但使用方便,不需要客户端,而且权限比FTP容易控制. Web文件上传采用POST方式,上传文件需要设置FORM的entype属性为 ...

  4. php上传文件的原理

    文件上传原理 将客户端的文件上传到服务器,再将服务器的临时文件上传到指定目录 客户端配置 提交表单 表单的发送方式为post 添加enctype="multipart/form-data&q ...

  5. Java+web+上传文件夹

    用JAVA实现大文件上传及显示进度信息 ---解析HTTP MultiPart协议 (本文提供全部源码下载,请访问 https://github.com/1269085759/up6-jsp-mysq ...

  6. Web上传文件的三种解决方案

    第一点:Java代码实现文件上传 FormFile file = manform.getFile(); String newfileName = null; String newpathname =  ...

  7. java+web上传文件夹内的所有文件

    javaweb上传文件 上传文件的jsp中的部分 上传文件同样可以使用form表单向后端发请求,也可以使用 ajax向后端发请求 1.通过form表单向后端发送请求 <form id=" ...

  8. web上传文件夹

    文件夹数据库处理逻辑 publicclass DbFolder { JSONObject root; public DbFolder() { this.root = new JSONObject(); ...

  9. web 表单方式上传文件方法(不用flash插件)

    原理:使用表单的input type="file"标签,通过ajax提交表单请求,后台获取请求中的文件信息,进行文件保存操作 由于我测试用的做了一个上传文件和上传图片方法,所以我有 ...

随机推荐

  1. C语言学习中遇到的小问题(一)

    C语言小白学习C语言的记录1 一.scanf一次性接收连续的数字 1.已知数量,且个数较少:scanf("%d%d%d",&a&b&c); 2.已知数量,但 ...

  2. Oracle的decode、sign、trunc函数

    原文http://knowyouknowme.iteye.com/blog/574974 一.decode 在Oracle/PLSQL中,  decode 具有和 IF-THEN-ELSE 一样的功能 ...

  3. C# 格式化新招

    C# 格式化新招 ) from Attribute_Item where AttributeSysNo=$AttributeSysNo and Name='$Name' and SysNo !=$Sy ...

  4. netbeans连接数据库SQLserver2008

    数据库设置 第一步:配置SQL,打开SQL server 2008文件下的配置工具里的SQL server配置管理器 设置MSSQLSERVER协议中,开启TCP/IP,端口设置为1433 在SQL ...

  5. Chrome RenderText分析(1)

      先从一些基础的类开始 1.Range // A Range contains two integer values that represent a numeric range, like the ...

  6. 咏南中间件JSON序列类

    咏南中间件JSON序列类 1)支持跨平台.跨语言 2)支持主从表数据序列.还原,支持任意数量的表 主从表数据序列为JSON字符串样式: { "rows": [ { "FD ...

  7. 启动Jupyter Notebook

    按照图所示,在命令下输入ipython notebook 即可启动Jupyter. 启动后的效果:

  8. TypeScript学习笔记(九):装饰器(Decorators)

    装饰器简介 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式. 需要注意的是:装饰器是一项实验性特性,在未来的版本中可能会发生改变. 若要启用实验性的装饰器特 ...

  9. Fluent动网格【3】:DEFINE_CG_MOTION宏

    除了利用Profile进行运动指定之外,Fluent中还可以使用UDF宏来指定部件的运动.其中用于运动指定的宏主要有三个: DEFINE_CG_MOTION DEFINE_GEOM DEFINE_GR ...

  10. Cublas矩阵加速运算

    前言 编写 CUDA 程序真心不是个简单的事儿,调试也不方便,很费时.那么有没有一些现成的 CUDA 库来调用呢? 答案是有的,如 CUBLAS 就是 CUDA 专门用来解决线性代数运算的库. 本文将 ...