java+web+下载断点续传
需求:
文件批量上传,支持断点续传。
文件批量下载,支持断点续传。
使用JS能够实现批量下载,能够提供接口从指定url中下载文件并保存在本地指定路径中。
服务器不需要打包。
支持大文件断点下载。比如下载10G的文件。
PC端全平台支持。Windows,macOS,Linux
全浏览器支持。ie6,ie7,ie8,ie9,ie10,ie11,edge,firefox,chrome,safari
1、先将 down2 这个示例和控件下载下来,根据个人的需求放置自己需要的东西就行,全部放到项目里也可以,下面是我自己需要的东西:
2、代码部分:分为jsp和servlet部分
1、jsp部分代码:
<scripttype=“text/javascript”>var fileMd5;
//监听分块上传过程中的三个时间点
WebUploader.Uploader.register({
“before-send-file”: “beforeSendFile”,
“before-send”: “beforeSend”,
“after-send-file”: “afterSendFile”,
},
{
//时间点1:所有分块进行上传之前调用此函数
beforeSendFile: function(file) {
var deferred = WebUploader.Deferred();
//1、计算文件的唯一标记,用于断点续传
(new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024).progress(function(percentage) {
$(‘#item1’).find(“p.state”).text(“正在读取文件信息…”);
}).then(function(val) {
fileMd5 = val;
$(‘#item1’).find(“p.state”).text(“成功获取文件信息…”);
//获取文件信息后进入下一步
deferred.resolve();
});
return deferred.promise();
},
//时间点2:如果有分块上传,则每个分块上传之前调用此函数
beforeSend: function(block) {
var deferred = WebUploader.Deferred();
$.ajax({
type: “POST”,
url: “<%=basePath%>Video?action=checkChunk”,
data: {
//文件唯一标记
fileMd5: fileMd5,
//当前分块下标
chunk: block.chunk,
//当前分块大小
chunkSize: block.end – block.start
},
dataType: “json”,
success: function(response) {
if (response.ifExist) {
//分块存在,跳过
deferred.reject();
} else {
//分块不存在或不完整,重新发送该分块内容
deferred.resolve();
}
}
});
this.owner.options.formData.fileMd5 = fileMd5;
deferred.resolve();
return deferred.promise();
},
//时间点3:所有分块上传成功后调用此函数
afterSendFile: function() {
//如果分块上传成功,则通知后台合并分块
$.ajax({
type: “POST”,
url: “<%=basePath%>Video?action=mergeChunks”,
data: {
fileMd5: fileMd5,
},
success: function(response) {
alert(“上传成功”);
var path = “uploads/” + fileMd5 + “.mp4”;
$(“#item1”).attr(“src”, path);
}
});
}
});
var uploader = WebUploader.create({
// swf文件路径
swf: ‘<%=basePath%>scripts/webuploader-0.1.5/Uploader.swf’,
// 文件接收服务端。
server: ‘<%=basePath%>UploadVideo’,
// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: {
id: ‘#add_video’,
//这个id是你要点击上传文件的id,自己设置就好
multiple: false
},
// 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
resize: true,
auto: true,
//开启分片上传
chunked: true,
chunkSize: 10 * 1024 * 1024,
accept: {
//限制上传文件为MP4
extensions: ‘mp4’,
mimeTypes: ‘video/mp4’,
}
});
// 当有文件被添加进队列的时候
uploader.on(‘fileQueued’,
function(file) {
$(‘#item1’).empty();
$(‘#item1’).html(‘<div id=”‘ + file.id + ‘” class=”item”>’ + ‘<a class=”upbtn” id=”btn” onclick=”stop()”>[取消上传]</a>’ + ‘<p class=”info”>’ + file.name + ‘</p>’ + ‘<p class=”state”>等待上传…</p></div>’);
});
// 文件上传过程中创建进度条实时显示。
uploader.on(‘uploadProgress’,
function(file, percentage) {
$(‘#item1’).find(‘p.state’).text(‘上传中 ‘ + Math.round(percentage * 100) + ‘%’);
});
uploader.on(‘uploadSuccess’,
function(file) {
$(‘#’ + file.id).find(‘p.state’).text(‘已上传’);
});
uploader.on(‘uploadError’,
function(file) {
$(‘#’ + file.id).find(‘p.state’).text(‘上传出错’);
});
uploader.on(‘uploadComplete’,
function(file) {
$(‘#’ + file.id).find(‘.progress’).fadeOut();
});
function start() {
uploader.upload();
$(‘#btn’).attr(“onclick”, “stop()”);
$(‘#btn’).text(“取消上传”);
}
function stop() {
uploader.stop(true);
$(‘#btn’).attr(“onclick”, “start()”);
$(‘#btn’).text(“继续上传”);
}</script>//这个id是你要点击上传文件的id,自己设置就好 multiple:false}, // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传! resize: true, auto:true, //开启分片上传 chunked: true, chunkSize:10*1024*1024, accept: { //限制上传文件为MP4 extensions: ‘mp4’, mimeTypes: ‘video/mp4’, } }); // 当有文件被添加进队列的时候 uploader.on( ‘fileQueued’, function( file ) { $(‘#item1’).empty(); $(‘#item1’).html(‘
<divid=“‘ + file.id + ‘”class=“item”>‘+ ‘
<aclass=“upbtn”id=“btn”onclick=“stop()”>[取消上传]</a>‘+ ‘
<pclass=“info”>‘ + file.name + ‘</p>‘ + ‘
<pclass=“state”>等待上传…</p></div>‘ ); }); // 文件上传过程中创建进度条实时显示。 uploader.on( ‘uploadProgress’, function( file, percentage ) { $(‘#item1’).find(‘p.state’).text(‘上传中 ‘+Math.round(percentage * 100) + ‘%’); }); uploader.on( ‘uploadSuccess’, function( file ) { $( ‘#’+file.id ).find(‘p.state’).text(‘已上传’); }); uploader.on( ‘uploadError’, function( file ) { $( ‘#’+file.id ).find(‘p.state’).text(‘上传出错’); }); uploader.on( ‘uploadComplete’, function( file ) { $( ‘#’+file.id ).find(‘.progress’).fadeOut(); }); function start(){ uploader.upload(); $(‘#btn’).attr(“onclick”,”stop()”); $(‘#btn’).text(“取消上传”); } function stop(){ uploader.stop(true); $(‘#btn’).attr(“onclick”,”start()”); $(‘#btn’).text(“继续上传”); }</script>
2、servlet部分代码:
servlet部分需要两个servlet,一个用于接收分块文件,一个用于合并分块成一个文件:
1、接收分块servlet代码:
@SuppressWarnings(“serial”)publicclass UploadVideo extends HttpServlet {@Override protectedvoid doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException,
IOException {
// TODO Auto-generated method stub
super.doGet(req, resp);
doPost(req, resp);
}@SuppressWarnings(“unchecked”)publicvoid doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException,
IOException {
DiskFileItemFactory factory =new DiskFileItemFactory();
ServletFileUpload sfu =new ServletFileUpload(factory);
sfu.setHeaderEncoding(“utf-8”);
String savePath =this.getServletConfig().getServletContext().getRealPath(“”);
String folad =“uploads”;
savePath = savePath +“\\”+ folad +“\\”;
String fileMd5 =null;
String chunk =null;
try{
List < FileItem > items = sfu.parseRequest(request);
for(FileItem item: items){
if(item.isFormField()){
String fieldName = item.getFieldName();
if(fieldName.equals(“fileMd5”)){
fileMd5 = item.getString(“utf-8”);
}
if(fieldName.equals(“chunk”)){
chunk = item.getString(“utf-8”);
}
}else{
File file =new File(savePath +“/”+ fileMd5);
if(!file.exists()){
file.mkdir();
}
File chunkFile =new File(savePath +“/”+ fileMd5 +“/”+ chunk);
FileUtils.copyInputStreamToFile(item.getInputStream(), chunkFile);
}
}
}catch(FileUploadException e){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2、合并分块servlet代码:
@SuppressWarnings(“serial”)publicclass Video extends HttpServlet {
publicvoid doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException,
IOException {
super.doGet(request, response);
doPost(request, response);
}
publicvoid doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException,
IOException {
String savePath =this.getServletConfig().getServletContext().getRealPath(“”);
String folad =“uploads”;
savePath = savePath +“\\”+ folad +“\\”;
String action = request.getParameter(“action”);
if(action.equals(“mergeChunks”)){
//合并文件
//需要合并的文件的目录标记
String fileMd5 = request.getParameter(“fileMd5”);
//读取目录里的所有文件
File f =new File(savePath +“/”+ fileMd5);
File[] fileArray = f.listFiles(new FileFilter(){
//排除目录只要文件
@Override publicboolean accept(File pathname){
// TODO Auto-generated method stub
if(pathname.isDirectory()){
returnfalse;
}
returntrue;
}
});
//转成集合,便于排序
List < File > fileList =new ArrayList < File >(Arrays.asList(fileArray));
Collections.sort(fileList,new Comparator < File >(){@Override publicint compare(File o1, File o2){
// TODO Auto-generated method stub
if(Integer.parseInt(o1.getName())< Integer.parseInt(o2.getName())){
return– 1;
}
return 1;
}
});
//UUID.randomUUID().toString()–>随机名
File outputFile =new File(savePath +“/”+ fileMd5 +“.mp4”);
//创建文件
outputFile.createNewFile();
//输出流
FileChannel outChnnel =new FileOutputStream(outputFile).getChannel();
//合并
FileChannel inChannel;
for(File file: fileList){
inChannel =new FileInputStream(file).getChannel();
inChannel.transferTo(0, inChannel.size(), outChnnel);
inChannel.close();
//删除分片
file.delete();
}
outChnnel.close();
//清除文件夹
File tempFile =new File(savePath +“/”+ fileMd5);
if(tempFile.isDirectory()&& tempFile.exists()){
tempFile.delete();
}
System.out.println(“合并成功”);
}elseif(action.equals(“checkChunk”)){
//检查当前分块是否上传成功
String fileMd5 = request.getParameter(“fileMd5”);
String chunk = request.getParameter(“chunk”);
String chunkSize = request.getParameter(“chunkSize”);
File checkFile =new File(savePath +“/”+ fileMd5 +“/”+ chunk);
response.setContentType(“text/html;charset=utf-8”);
//检查文件是否存在,且大小是否一致
if(checkFile.exists()&& checkFile.length()== Integer.parseInt(chunkSize)){
//上传过
response.getWriter().write(“{\”ifExist\”:1}”);
}else{
//没有上传过
response.getWriter().write(“{\”ifExist\”:0}”);
}
}
}
}
至此,大文件上传的分块和断点就ok了,这也只是我自己的项目需求编写的,这个框架还涵盖很多的内容和功能,需要你自己去研究了,不过都不是很难,你也可以去修改它的css和js文件根据自己的需求。
详细的配置信息可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/06/14/java%e6%89%b9%e9%87%8f%e4%b8%8b%e8%bd%bd%e6%96%87%e4%bb%b6%e5%88%b0%e6%8c%87%e5%ae%9a%e6%96%87%e4%bb%b6%e5%a4%b9/