java+断点续传
我们平时经常做的是上传⽂件,上传⽂件夹与上传⽂件类似,但也有⼀些不同之处,这次做了上传⽂件夹就记录下以备后⽤。
这次项⽬的需求:
⽀持⼤⽂件的上传和续传,要求续传⽀持所有浏览器,包括ie6,ie7,ie8,ie9,Chrome,Firefox,360安全浏览器,并且刷新浏览器后仍然能够续传,重启浏览器(关闭浏览器后再打开)仍然能够继续上传,重启电脑后仍然能够上传
⽀持⽂件夹的上传,要求服务端能够保留层级结构,并且能够续传。需要⽀持10万个以上的⽂件夹上传。
⽀持低版本的系统和浏览器,因为这个项⽬的最终运⾏环境在政府,政府的配置都⼀般,职员都是办公⽤,内存都不⼤,基本上以Windows XP的系统为主。
1、介绍enctype
enctype 属性规定发送到服务器之前应该如何对表单数据进⾏编码。
enctype作⽤是告知服务器请求正⽂的MIME类型(请求消息头content-type的作⽤⼀样)
1、1 enctype的取值有三种
值描述
application/x-www-form-urlencoded在发送前编码所有字符(默认)
multipart/form-data不对字符编码。每⼀个表单项分割为⼀个部件
text/plain空格转换为 “+” 加号,但不对特殊字符编码。
1. 当enctype=’application/x-www-form-urlencoded’
2.当enctype=’multipart/form-data’
通过观察发现这个的请求体就发⽣了变化。这种请求体被称之为多部件请求体。
什么是多部件请求体:就是把每⼀个表单项分割为⼀个部件。
以请求头的content-type的boundary后⾯的⼀串随机字符串作为分割标识
普通表单项:
//name的意思是⽂本框⾥⾯name的属性值,⽽admin是我们输⼊的⽂本值
Content-Disposition: form-data; name="urname"
admin
⽂件表单项
//filename的意思是:我们上传的⽂件名称,content-Type的意思是:MIME类型,asdasdas的意思是:⽂件⾥⾯的内容
Content-Disposition: form-data; name="upload"; filename="a.txt"
Content-Type: text/plain
asdasdas
3. 当enctype=’text/plain’
w3c称:空格会变成”+”加号,但是我这⾥没有发现,只有当get请求的时候,空格会变成”+”号
进⼊正题
完成上传需要满⾜3个必要的条件
提供form表单,method必须是post,因为get请求的传输数据⼀般为2kb,不同浏览器不⼀样。
form表单属性enctype的必须是multipart/form-data
提供input type=”file”类的上传输⼊域
⼤致实现原理:当enctype的值是multipart/form-data时,浏览器会把每个表单项进⾏分割,分割成不同的部件,以boundary的值为分割标识,这个标识的字符串是随机⽣成的,最后⼀个表单项的分割标识字符串末尾会多两个”- -“,代表结束。服务端⽤
代码实现
⼀、开发环境搭建
准备两个第三⽅jar包
所有依赖包
代码实现
<%@ page language="java" import="up6.DBFile" pageEncoding="UTF-8"%> <%@ page contentType="text/html;chart=UTF-8"%>
<%@ page import="up6.FileBlockWriter" %>
<%@ page import="up6.XDebug" %>
<%@ page import="up6.*" %>
<%@ page import="up6.biz.*" %>
<%@ page import="s.fileupload.FileItem" %>
<%@ page import="s.fileupload.FileItemFactory" %>
<%@ page import="s.fileupload.FileUploadException" %> <%@ page import="s.fileupload.disk.DiskFileItemFactory" %> <%@ page import="s.fileupload.rvlet.ServletFileUpload" %> <%@ page import="s.lang.*" %>
<%@ page import="URLDecoder"%>
<%@ page import="java.util.Iterator"%>
<%@ page import="net.sf.json.JSONObject"%>
<%@ page import="java.util.List"%>
<%
out.clear();
String uid = Header("uid");//
String id = Header("id");
String lenSvr = Header("lenSvr");
String lenLoc = Header("lenLoc");
String blockOfft = Header("blockOfft");
String blockSize = Header("blockSize");
String blockIndex = Header("blockIndex");
String blockMd5 = Header("blockMd5");
String complete = Header("complete");
String pathSvr = "";
//参数为空
if( StringUtils.isBlank( uid )
|| StringUtils.isBlank( id )
|| StringUtils.isBlank( blockOfft ))
{
XDebug.Output("param is null");return;
}
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List files = null;
try {files = upload.parRequest(request);}
catch (FileUploadException e)
{out.println("read file data error:" + e.toString());return;}
FileItem rangeFile = null;
Iterator fileItr = files.iterator();
while (fileItr.hasNext())
{
rangeFile = (FileItem) ();
if(StringUtils.equals( FieldName(),"pathSvr"))
{
pathSvr = String();
pathSvr = PathTool.url_decode(pathSvr);
}
}
boolean verify = fal;
String msg = "";
String md5Svr = "";
long blockSizeSvr = Size();
if(!StringUtils.isBlank(blockMd5)){md5Svr = Md5Tool.InputStream());} verify = Integer.parInt(blockSize) == blockSizeSvr;
if(!verify){ msg = "block size error sizeSvr:" + blockSizeSvr + "sizeLoc:" + blockSize;}
if(verify && !StringUtils.isBlank(blockMd5))
{
verify = md5Svr.equals(blockMd5); if(!verify) msg = "block md5 error";
}
if(verify)
{
FileBlockWriter res = new FileBlockWriter();
if( Integer.parInt(blockIndex)==1) res.CreateFile(pathSvr,Long.parLong(lenLoc));
res.write( Long.parLong(blockOfft),pathSvr,rangeFile);
up6_biz_event.file_post_block(id,Integer.parInt(blockIndex));
JSONObject o = new JSONObject();
o.put("msg", "ok");
o.put("md5", md5Svr);
o.put("offt", blockOfft);
msg = o.toString();
}
rangeFile.delete();
out.write(msg);
%>
下载的必须条件
两个头⼀个流
content-type
Content-Type是返回消息中⾮常重要的内容,表⽰⽂档内容属于什么MIME类型。
浏览器会根据Content-Type来决定如何显⽰返回的消息体内容。
默认值是text/html
可以使⽤ServletContext().getMimeType(“⽂件名”)获取MIME类型。
Content-Disposition
Content-disposition 是 MIME 协议的扩展,MIME 协议指⽰ MIME ⽤户代理如何显⽰附加的⽂件。
默认值是inline,表⽰在浏览器窗⼝中打开。
服务端向客户端游览器发送⽂件时,如果是浏览器⽀持的⽂件类型,⼀般会默认使⽤浏览器打开,⽐如txt、jpg等,会直接在浏览器中显⽰。
如果需要提⽰⽤户保存,利⽤Content-Disposition进⾏⼀下处理,关键在于⼀定要加上attachment。
例如:Content-Disposition:attachment;filename=xxx,浏览器就会激活下载框对话框, attachment 表⽰附件, filname 后⾯跟随的是显⽰在下载框中的⽂件名称。
流
下载就是向客户端响应字节数据! 将⼀个⽂件变成字节数组, 使⽤ OutputStream()
来响应给浏览器。
代码如下,此代码已经实现了断点续传功能,⽤户在下载过程可以暂停,和继续下载,对服务器造成的压⼒也⽐较⼩。
String fid = Header("id");