断点上传能够防止意外情况导致上传一半的文件下次上传时还要从头下载,网上有很多关于断点的实现,这篇文章只是从前到后完整的记录下一个可用的实例,由于生产环境要求不高,而且就是提供给一两个人用,所以我简化了诸多过程,不用Flash,也不用applet,只是通过html5的新特性进行浏览器端的处理。简单说下关键点如果上次传到n字节,那么浏览器下次续传直接就是从文件的n字节开始向服务器传送数据,而不是都传过去,服务器从n字节开始接收。html5能给文件分片,所以每次上传完一块文件后,应该返回当前已经上传的文件大小,好让h5能从此断点继续读取。前端的js是网上别人的底子,我进行了可用性修改。代码完全可用,而且都是用的最简单的东西实现可以看到我用了本地文件的最后修改时间这个属性,因为这样可以脱离数据库只通过文件名+文件最后修改时间来确定文件的唯一性,如果生产中有数据库的接入,建议先生成续传文件并返回对应的唯一id。服务器端方法获取当前已经上传文件的大小/*** 获取已上传的文件大小*@paramrequest*@paramresponse*/publicvoidgetChunkedFileSize(HttpServletRequest request,HttpServletResponse response){//存储文件的路径,根据自己实际确定String currentFilePath = "c:\\uploadFile\\Image\\";PRintWriter print =null;try{request.setCharacterEncoding("utf-8");print =response.getWriter();String fileName =newString(request.getParameter("fileName").getBytes("ISO-8859-1"),"UTF-8");String lastModifyTime = request.getParameter("lastModifyTime");File file =newFile(currentFilePath+fileName+"."+lastModifyTime);if(file.exists()){print.print(file.length());}else{print.print(-1);}}catch(Exception e) {//TODO Auto-generated catch blocke.printStackTrace();}}
2.文件上传
/***
* 断点文件上传 1.先判断断点文件是否存在 2.存在直接流上传 3.不存在直接新创建一个文件 4.上传完成以后设置文件名称
**/publicstaticvoidappendUpload2Server(HttpServletRequest request,HttpServletResponse response) {
PrintWriter print=null;try{
request.setCharacterEncoding("utf-8");
print=response.getWriter();
String fileSize= request.getParameter("fileSize");longtotalSize =StringUtil.toLong(fileSize);
RandomaccessFile randomAccessfile=null;longcurrentFileLength = 0;//记录当前文件大小,用于判断文件是否上传完成String currentFilePath = "c:\\uploadFile\\Image\\";//记录当前文件的绝对路径String fileName =newString(request.getParameter("fileName").getBytes("ISO-8859-1"),"UTF-8");
String lastModifyTime= request.getParameter("lastModifyTime");
File file=newFile(currentFilePath+fileName+"."+lastModifyTime);//存在文件if(file.exists()){
randomAccessfile=newRandomAccessFile(file, "rw");
}else{//不存在文件,根据文件标识创建文件randomAccessfile =newRandomAccessFile(currentFilePath+fileName+"."+lastModifyTime, "rw");
}//开始文件传输InputStream in =request.getInputStream();
randomAccessfile.seek(randomAccessfile.length());byteb[] =newbyte[1024];intn;while((n = in.read(b)) != -1) {
randomAccessfile.write(b,0, n);
}
currentFileLength=randomAccessfile.length();//关闭文件closeRandomAccessFile(randomAccessfile);
randomAccessfile=null;//整个文件上传完成,修改文件后缀if(currentFileLength ==totalSize) {
File oldFile=newFile(currentFilePath+fileName+"."+lastModifyTime);
File newFile=newFile(currentFilePath+fileName);if(!oldFile.exists()){return;//重命名文件不存在}if(newFile.exists()){//如果存在形如test.txt的文件,则新的文件存储为test+当前时间戳.txt, 没处理不带扩展名的文件String newName = fileName.substring(0,fileName.lastIndexOf("."))+System.currentTimeMillis()+"."
+fileName.substring(fileName.lastIndexOf(".")+1);
newFile=newFile(currentFilePath+newName);
}if(!oldFile.renameTo(newFile)){
oldFile.delete();
}
}
print.print(currentFileLength);
}catch(Exception e) {
e.printStackTrace();
}
}/*** 关闭随机访问文件
*
*@paramrandomAccessfile*/publicstaticvoidcloseRandomAccessFile(RandomAccessFile rfile) {if(null!=rfile) {try{
rfile.close();
}catch(Exception e) {
e.printStackTrace();
}
}
}
jsp页面<html><head><title>断点续传文件</title><metacharset="utf-8"></head><bodyonload="init();"><divclass="row"><labelfor="fileToUpload">请选择需要上传的文件</label><inputtype="file"name="fileToUpload"id="fileToUpload"onchange="fileSelected();"multiple/></div></div><divclass="row"><buttononclick="uploadFiles()">上传</button><buttononclick="pauseUpload()">暂停</button> <labelid="progressNumber"></label></div><divid="msg"style="max-height: 400px; overflow:auto;min-height: 100px;"></div><
br><div><h6>支持批量,支持断点续传</h6></div></body></html>
js代码varmsg =null;varparagraph = 1024*1024*2;//每次分片传输文件的大小 2Mvarblob =null;//分片数据的载体Blob对象varfileList =null;//传输的文件varuploadState = 0;//0: 无上传/取消, 1: 上传中, 2: 暂停//初始化消息框functioninit(){
msg= document.getElementById("msg");
}functionuploadFiles(){//将上传状态设置成1uploadState = 1;if(fileList.files.length>0){for(vari = 0; i< fileList.files.length; i++){varfile =fileList.files[i];
uploadFileInit(file,i);
}
}else{
msg.innerHTML= "请选择上传文件!";
}
}/**
* 获取服务器文件大小,开始续传
* @param file
* @param i*/functionuploadFileInit(file,i){if(file){varstartSize = 0;varendSize = 0;vardate =file.lastModifiedDate;varlastModifyTime = date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate()+"-"
+date.getHours()+"-"+date.getMinutes()+"-"+date.getSeconds()//获取当前文件已经上传大小jQuery.post("xxx/getChunkedFileSize.do",
{"fileName":encodeURIComponent(file.name),"fileSize":file.size,"lastModifyTime":lastModifyTime,"chunkedFileSize":"chunkedFileSize"},function(data){if(data != -1){
endSize=Number(data);
}
uploadFile(file,startSize,endSize,i);
});
}
}/**
* 分片上传文件*/functionuploadFile(file,startSize,endSize,i) {vardate =file.lastModifiedDate;varlastModifyTime = date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate()+"-"
+date.getHours()+"-"+date.getMinutes()+"-"+date.getSeconds()varreader =newFileReader();
reader.onload=functionloaded(evt) {//构造xmlHttpRequest 对象,发送文件 Binary 数据varxhr =newxmlhttpRequest();
xhr.sendAsBinary=function(text){vardata =newArrayBuffer(text.length);varui8a =newUint8Array(data, 0);for(vari = 0; i < text.length; i++) ui8a[i] = (text.charCodeAt(i) & 0xff);this.send(ui8a);
}
xhr.onreadystatechange=function(){if(xhr.readyState==4){//表示服务器的相应代码是200;正确返回了数据if(xhr.status==200){//纯文本数据的接受方法varmessage=xhr.responseText;
message=Number(message);
uploadProgress(file,startSize,message,i);
}else{
msg.innerHTML= "上传出错,服务器相应错误!";
}
}
};//创建回调方法xhr.open("POST","xxx/appendUpload2Server.do?fileName=" + encodeURIComponent(file.name)+"&fileSize="+file.size+"&lastModifyTime="+lastModifyTime,false);
xhr.overrideMimeType("application/octet-stream;charset=utf-8");
xhr.sendAsBinary(evt.target.result);
};if(endSize <file.size){//处理文件发送(字节)startSize =endSize;if(paragraph > (file.size -endSize)){
endSize=file.size;
}else{
endSize+=paragraph ;
}if(file.webkitSlice) {//webkit浏览器blob =file.webkitSlice(startSize, endSize);
}elseblob=file.slice(startSize, endSize);
reader.readAsBinaryString(blob);
}else{
document.getElementById('progressNumber'+i).innerHTML = '100%';
}
}//显示处理进程functionuploadProgress(file,startSize,uploadLen,i) {varpercentComplete = Math.round(uploadLen * 100 /file.size);
document.getElementById('progressNumber'+i).innerHTML = percentComplete.toString() + '%';//续传if(uploadState == 1){
uploadFile(file,startSize,uploadLen,i);
}
}/*暂停上传*/functionpauseUpload(){
uploadState= 2;
}/**
* 选择文件之后触发事件*/functionfileSelected() {
fileList= document.getElementById('fileToUpload');varlength =fileList.files.length;varframe = document.getElementById('fileFrame');for(vari=0; i<length; i++){
file=fileList.files[i];if(file){varfileSize = 0;if(file.size > 1024 * 1024)
fileSize= (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';elsefileSize= (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';varnameDiv = document.createElement("div");
nameDiv.setAttribute("id","fileName"+i);
nameDiv.innerHTML='Name: ' +file.name;varsizeDiv = document.createElement("div");
sizeDiv.setAttribute("id","fileSize"+i);
sizeDiv.innerHTML='fileSize: ' +fileSize;vartypeDiv = document.createElement("div");
typeDiv.setAttribute("id","progressNumber"+i);
typeDiv.innerHTML='';
}
frame.appendChild(nameDiv);
frame.appendChild(sizeDiv);
frame.appendChild(typeDiv);
}
}