HTTP 文件上传的基本原理:
method 属性必须设为post的原因是:值不是放在URL之后传递到服务器的;
encType属性:这个属性管理的是表单的MIME编码
服务器端:
“multipart/form-data”类型的
HTTP请求消息后,其核心和基本的编程工作就是读取请求消息中的实体内容,然后解析出每个分区的数据,接着再从每个分区中解析出描述头和主体内容部分。
具体方法如下:
1、 根据request获得文件输入流;
2、 依次读取行,此时进行两部分内容的处理,
a:获取文件名
以 filename=”xxxxx”来标识一个文件头,
b:获取其他表单值(因为其流是按照multipart/form-data方式来编码的,所以在服务器端,不能直接用request.getParameter()来获得);
都以流头的字符标识为值的结束;
实体内容内部的字段分隔界线是在content-type头中指定的字段分隔界线前面增加了两个减号(-)字符而形成的(由浏览器随机生成,由浏览器保证不会与用户上传的文件内容重复)
当找到一个分区的开始位置后,程序还需要分辨出分区中的描述头和主体内容,并对这两部分内容分开存储。如何分辨出一个分区的描述头和主体部分呢?每个分区中的描述头和主体内容之间有一空行,再加上描述头后面的换行,这就说明描述头和主体部分之间是使用“\n”、“\r”、“\n”、“\r”这四个连续的字节内容进行分隔。因此,程序需要把“\n”、“\r”、“\n”、“\r”这四个连续的字节内容作为描述头和主体部分之间的分隔界线,并在字节数组缓冲区buffer中寻找这个特殊的分隔界线来识别描述头和主体部分。
3、根据读到的文件信息(文件名,文件大小等),判断是否合法(文件类型、文件大小判断)。如果合适则返回,如果不合适则创建同名文件并将其删除;
演示Form 显示提交的流的格式:
当前的主流文件上传组件性能对比
实例演示性能对比:
jspSmartUpload
fileUpLoad
cos
20M 、75M 、300M
所以选择Cos作为内部读取解析文件流的组件;
使用组件而不是自己来解析文件流的原因:
1、 读取文件大小的限制,如同smartUpload组件一样,上传的文件大小会受到JVM内存容量的限制,因为读取的文件流都是存储在内存之中的,而fileUpLoad和cos采用的是当文件读取到了一定的大小就先写到硬盘上的临时目录中;
2、 读取效率的限制,组件可以根据文件的大小设定一定的缓存,来提升读取的效率,分配好内存的缓存大小,及硬盘目录缓存大小;
(文件缓存循环机制作的简要介绍,缓冲区如何正好读取到分区界限)
此类组件的使用及封装方式:
MultipartRequest mu = new MultipartRequest(HttpServletRequestrequest,Directory,MaxSize,”gbk”);
我们只需要传递一个申请的request流,目录名,它会自动完成相应的解析文件流的过程,然后直接在对应设置的目录中生成对应的同名文件;
代理模式:
整个的代理流程如下:
首先类HttpServerRequestProxy继承于HttpServletRequestWrappe
运行流程如下:
服务器端获取文件传输速度的计算方式:
1.
2.
3.
判断上传文件(单个)大小是否符合服务器设置:
(如何快速判断,而不是等二进制流读取完再返回错误信息。)
- javaScript客户端脚本判断,但是针对不同的浏览器,不同的权限设置,不一定能成功;(且于服务器端实际读取的文件大小精度有差异,难以靠以上传的流的大小来保证文件是否已经上传完毕)
function ShowFileSize()
{
}
- 服务器即时判断:服务器读取流,直到读取的流的数量大于所设定大的限制,则返回错误信息。(占太多的时间,且浪费服务器资源)
- 服务器端判断:在发送POST请求时,信息头里面都包含 Content-Length属性,其代表着整个请求的数据长度。我们可以通过简单的判断这个Header数据,发现超过了我们允许的长度则直接断开连接,这样就不会对服务器造成资源浪费。
演示:simpleFileUpload
如何判断批量上传的每一个文件的大小:
(使用方法二)
1.在客户端判断文件大小,只提交服务器一个文件流,但是文件大小的精确程度会有一定程度的不同,导致一种假死现象;还会有客户端js代码的权限问题。
2. 在服务器端判断文件大小,但是服务器在真实读取文件流之前,使用上面的方法二,只能得到整个流的大小,不能得到,单个文件的大小;这样只能实现整个上传进度的统计。而不可能实现多个文件分开计算其精度的方法;
演示:simpleFileUpload
3.每个文件提交一个文件流,服务器对每个流处理时返回文件大小及文件进度。方法是,每个 <inputtype=”file”> 标签都置于一个独立的form表单中,然后一次提交;
服务器如何在真正文件上传(读取流)之前获得文件名?
如果文件不合法,服务器、客户端如何而实现:
(此时服务器已经必定在读http传递过来的文件流,除非判断文件名是在客户端判断,或者是使用Ajax在服务器端判断的方法)
1、 客户端使用Ajax接受服务器返回出错信息,并在服务器内部读取流文件时,如果发现文件不合法则抛出特定的异常,并在服务器内部自行捕获处理;
2、 流覆盖;
流覆盖演示:streamCover
原理:将两个请求同时提交到一个地方,导致后面的流截断前面正在上传的文件流,从而达到删除正在上传的文件的效果;(删除正在上传的文件)
客户端从服务器读取文件上传进度信息:
(Ajax)
几个重要的步骤:
1、
2、
3、
4、
5、
需要注意的问题:
1、
解决方法:
清除由于IE缓存造成的Ajax不能多次发出请求:
- 服务器端:
response.setHeader(“Cache-Control”,”no-cache”);
- 客户端
2、
Ajax的优势就是可以异步发送请求到服务器,而不需要服务器实时的去相应,所以设为异步;
服务器端相应Ajax请求的几个问题:
1、
- request.getHeader("x-requested-with");
b.在URL后添加一个与服务器端约定好的参数;
(在服务器端都必须显示的去判断)
2、
Ajax请求默认的参数默认均为 ”ISO8859-1”,在服务器端必须要进行解码;
与进度条的集成:
根据Ajax从服务器获取的文件信息(文件大小,上传速度,上传百分比等信息),信息每更新一次就设定进度条的值;动态隐藏和显示显示进度条的div;
UI上传组件应该具备以下基本功能:
1. 能让用户提供一个上传路径
2. 能够限制上载文件的大小(防止上传文件过大)
6. 可以设定屏蔽(或指定)固定类型的文件(如以 .exe结尾的文件等)
7.能够向用户提供一个友好的交互界面,实时显示该文件上传的进度、当前用户的网络状态、上传此文件已花费时间、传输速率,剩余时间等信息
8. 支持对已上传或正在上传文件的删除
9. 用户使用及操作及可扩展 ,并能够灵活的集成到其它项目中
一个页面的,多表单提交的问题解决方案:
1.
2.
Javascript问题:
- 引号中传字符串值; 可以单双引号嵌套使用
- Setinterval()函数传值,采用
function(){ realFunction(args)} 的匿名传值方法;
3.
一个页面多次提交导致流被截断;