为什么要断点续传:在传输较大文件没传输完成时若出现断网或者服务器异常等情况则文件会上传失败,使用者需要重新开始上传文件,这样会使用户体验十分不好,所以需要有断点续传。断点续传好的方法是将文件分为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;
url: 'uploadDemo/doUpload', limitConcurrentUploads: 1, sequentialUploads: true, progressInterval: 100, maxChunkSize: 100000, //设置上传片段大小,不设置则为整个文件上传 dataType: "json", add: function (e, data) {$("#myFile").fileupload({
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; } }