爲什麼要斷點續傳:在傳輸較大文件沒傳輸完成時若出現斷網或者服務器異常等情況則文件會上傳失敗,使用者需要重新開始上傳文件,這樣會使用戶體驗十分不好,所以需要有斷點續傳。斷點續傳好的方法是將文件分爲N個片段進行上傳,這樣即使後面的片段還未上傳完畢之前已上傳的片段也會得以保留。
本文使用jquery fileupload插件進行,它負責將文件分爲N個片段進行傳輸,同時會在http請求頭部添加一個請求頭:Content-Range;此請求頭用於告訴服務器此時傳輸的是哪個區間的片段。
jsp頁面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@include file="WEB-INF/jsp/common/taglibs.jsp"%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>用戶登錄</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<!-- Bootstrap -->
<!--end Bootstrap -->
<!--[if lt IE 9]>
<script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<link href="${ctx}/resources/css/bootstrap.min.css" rel="stylesheet">
<link href="${ctx}/resources/css/bootstrap-theme.min.css"
rel="stylesheet">
<link rel="stylesheet" href="${ctx}/resources/css/jquery.fileupload.css">
<link rel="stylesheet" href="${ctx}/resources/css/jquery.fileupload-ui.css">
</head>
<body>
<div class="row fileupload-buttonbar" style="padding-left:15px;">
<div class="thumbnail col-sm-6">
<!-- <img id="weixin_show" style="height:180px;margin-top:10px;margin-bottom:8px;" src="__PUBLIC__/images/game/game_1.png" data-holder-rendered="true">
--><div class="progress progress-striped active" role="progressbar" aria-valuemin="10" aria-valuemax="100" aria-valuenow="0"><div id="weixin_progress" class="progress-bar progress-bar-success" style="width:0%;"></div></div>
<div class="caption" align="center">
<span id="weixin_upload" class="btn btn-primary fileinput-button">
<span>上傳</span>
<input type="file" id="myFile" name="myFile" multiple>
</span>
<a id="weixin_cancle" href="javascript:void(0)" class="btn btn-warning" role="button" οnclick="cancelUpload()" style="display:none">刪除</a>
<span class="btn btn-warning" id="ase">暫停</span>
</div>
</div>
</div>
<script src="${ctx}/resources/js/jquery.min.js"></script>
<script src="${ctx}/resources/js/bootstrap.min.js"></script>
<script src="${ctx}/resources/js/jquery.ui.widget.js"></script>
<script src="${ctx}/resources/js/jquery.fileupload.js"></script>
<script src="${ctx}/resources/js/jquery.iframe-transport.js"></script>
<script>
$(function() {
var jqXHR;
$("#myFile").fileupload({
url: 'uploadDemo/doUpload', limitConcurrentUploads: 1, sequentialUploads: true, progressInterval: 100, maxChunkSize: 100000, //設置上傳片段大小,不設置則爲整個文件上傳 dataType: "json", add: function (e, data) {jqXHR = data.submit();
} }).bind('fileuploadprogress', function (e, data) { var progress = parseInt(data.loaded / data.total * 100, 10)-1; $("#weixin_progress").css('width',progress + '%'); $("#weixin_progress").html(progress + '%'); }).bind('fileuploaddone', function (e, data) { $("#weixin_progress").css('width',100 + '%'); $("#weixin_progress").html(100 + '%'); /* $("#weixin_show").attr("src","resource/"+data.result); */ $("#weixin_upload").css({display:"none"}); $("#weixin_cancle").css({display:""}); }).bind('fileuploadpaste', function (e, data) {alert("aaa");}); }); //取消上傳 function cancelUpload(){
jqXHR.abort();
}
</script>
</body>
</html>
服務器代碼:
@RequestMapping("/doUpload")
public @ResponseBody
void upload(HttpServletRequest request, PrintWriter writer,
HttpServletResponse response) throws Exception {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
// 獲取傳入文件
multipartRequest.setCharacterEncoding("utf-8");
MultipartFile file = multipartRequest.getFile("myFile");
this.SaveAs("uploadDemo/" + file.getOriginalFilename(), file, request,
response);
// 設置返回值
Map<String, String> map = new HashMap<String, String>();
map.put("name", file.getOriginalFilename());
response.setStatus(200);
writer.write(JSON.toJSONString(map));
}
private void SaveAs(String saveFilePath, MultipartFile file,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
long lStartPos = 0;
int startPosition = 0;
int endPosition = 0;
int fileLength = 100000;
OutputStream fs = null;
String contentRange = request.getHeader("Content-Range");
System.out.println(contentRange);
if (!new File("uploadDemo").exists()) {
new File("uploadDemo").mkdirs();
}
if (contentRange == null) {
FileUtils.writeByteArrayToFile(new File(saveFilePath),
file.getBytes());
} else {
// bytes 10000-19999/1157632 將獲取到的數據進行處理截取出開始跟結束位置
if (contentRange != null && contentRange.length() > 0) {
contentRange = contentRange.replace("bytes", "").trim();
contentRange = contentRange.substring(0,
contentRange.indexOf("/"));
String[] ranges = contentRange.split("-");
startPosition = Integer.parseInt(ranges[0]);
endPosition = Integer.parseInt(ranges[1]);
}
//判斷所上傳文件是否已經存在,若存在則返回存在文件的大小
if (new File(saveFilePath).exists()) {
fs = new FileOutputStream(saveFilePath, true);
FileInputStream fi = new FileInputStream(saveFilePath);
lStartPos = fi.available();
fi.close();
} else {
fs = new FileOutputStream(saveFilePath);
lStartPos = 0;
}
//判斷所上傳文件片段是否存在,若存在則直接返回
if (lStartPos > endPosition) {
fs.close();
return;
} else if (lStartPos < startPosition) {
byte[] nbytes = new byte[fileLength];
int nReadSize = 0;
file.getInputStream().skip(startPosition);
nReadSize = file.getInputStream().read(nbytes, 0, fileLength);
if (nReadSize > 0) {
fs.write(nbytes, 0, nReadSize);
nReadSize = file.getInputStream().read(nbytes, 0,
fileLength);
}
} else if (lStartPos > startPosition && lStartPos < endPosition) {
byte[] nbytes = new byte[fileLength];
int nReadSize = 0;
file.getInputStream().skip(lStartPos);
int end = (int) (endPosition - lStartPos);
nReadSize = file.getInputStream().read(nbytes, 0, end);
if (nReadSize > 0) {
fs.write(nbytes, 0, nReadSize);
nReadSize = file.getInputStream().read(nbytes, 0, end);
}
}
}
if (fs != null) {
fs.flush();
fs.close();
fs = null;
}
}