继上次大文件分块上传原理见:http://blog.csdn.net/haohao123nana/article/details/51279098,博主终于有时间来真正的代码实现它。

关键部分

  • 前端用file.slice()分块
  • 前端用FileReader获取每一分块的md5值
  • 前端上传每一分块前,都会去服务器校验分片是否已上传,以此来支持断点续传
  • 后端用MultipartFile接受分块文件
  • 后端用FileOutputStream拼装分块文件

一、话不多说,直接上代码,我想这是你们最喜欢的

html

<!DOCTYPE HTML>
<html>
<head><meta charset="utf-8"><title>HTML5大文件分片上传示例</title><script src="http://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script><script src="md5.js"></script><script>var i = -1;var succeed = 0;var databgein;  //开始时间var dataend;    //结束时间var action=false;    //false检验分片是否上传过(默认); true上传文件var page = {init: function(){$("#upload").click(function(){databgein=new Date();var file = $("#file")[0].files[0];  //文件对象isUpload(file);});} };$(function(){page.init();});function isUpload (file) {//构造一个表单,FormData是HTML5新增的var form = new FormData();var r = new FileReader();r.readAsBinaryString(file);$(r).load(function(e){var bolb = e.target.result;var md5 = hex_md5(bolb);form.append("md5", md5);  //Ajax提交$.ajax({url: "http://localhost:8080//bookQr/test/isUpload",type: "POST",data: form,async: true,        //异步processData: false,  //很重要,告诉jquery不要对form进行处理contentType: false,  //很重要,指定为false才能形成正确的Content-Typesuccess: function(data){var dataObj = eval("("+data+")");var uuid = dataObj.fileId;var date = dataObj.date;if (dataObj.flag == "1") {//没有上传过文件upload(file,uuid,md5,date);} else if(dataObj.flag == "2") {//已经上传部分upload(file,uuid,md5,date);} else if(dataObj.flag == "3") {//文件已经上传过alert("文件已经上传过,秒传了!!");    $("#uuid").append(uuid);                      }},error: function(XMLHttpRequest, textStatus, errorThrown) {alert("服务器出错!");}});})               }/** file 文件对象* uuid 后端生成的uuid* filemd5 整个文件的md5* date  文件第一个分片上传的日期(如:20170122)*/function upload (file,uuid,filemd5,date) {name = file.name;        //文件名size = file.size;        //总大小var shardSize = 5 * 1024 * 1024,    //以5MB为一个分片shardCount = Math.ceil(size / shardSize);  //总片数if (i > shardCount) {i = -1;i = shardCount;} else {if(!action){i += 1;  //只有在检测分片时,i才去加1; 上传文件时无需加1}}//计算每一片的起始与结束位置var start = i * shardSize,end = Math.min(size, start + shardSize);//构造一个表单,FormData是HTML5新增的var form = new FormData();if(!action){form.append("action", "check");  //检测分片是否上传$("#param").append("action==check ");}else{form.append("action", "upload");  //直接上传分片form.append("data", file.slice(start,end));  //slice方法用于切出文件的一部分$("#param").append("action==upload ");}form.append("uuid", uuid);form.append("filemd5", filemd5);form.append("date", date);form.append("name", name);form.append("size", size);form.append("total", shardCount);  //总片数form.append("index", i+1);        //当前是第几片var ssindex = i+1;$("#param").append("index=="+ssindex+"<br/>");//按大小切割文件段  var data = file.slice(start, end);var r = new FileReader();r.readAsBinaryString(data);$(r).load(function(e){var bolb = e.target.result;var md5 = hex_md5(bolb);form.append("md5", md5);  //Ajax提交$.ajax({url: "http://localhost:8080//bookQr/test/upload",type: "POST",data: form,async: true,        //异步processData: false,  //很重要,告诉jquery不要对form进行处理contentType: false,  //很重要,指定为false才能形成正确的Content-Typesuccess: function(data){var dataObj = eval("("+data+")");var fileuuid = dataObj.fileId;var flag = dataObj.flag;//服务器返回该分片是否上传过if(!action){if (flag == "1") {//未上传action = true;} else if(dataObj.flag == "2") {//已上传action = false;++succeed;                            }//递归调用                        upload(file,uuid,filemd5,date);}else{//服务器返回分片是否上传成功//改变界面++succeed;$("#output").text(succeed + " / " + shardCount);if (i+1 == shardCount) {dataend=new Date();$("#uuid").append(fileuuid);$("#usetime").append(dataend.getTime()-databgein.getTime());} else {                          //已上传成功,然后检测下一个分片action = false;//递归调用                        upload(file,uuid,filemd5,date);}} },error: function(XMLHttpRequest, textStatus, errorThrown) {alert("服务器出错!");}});})               }</script></head><body><input type="file" id="file" /><button id="upload">上传</button><span id="output" style="font-size:12px">等待</span><span id="usetime" style="font-size:12px;margin-left:20px;">用时</span><span id="uuid" style="font-size:12px;margin-left:20px;">uuid==</span><br/><br/><br/><br/><span id="param" style="font-size:12px;margin-left:20px;">param==</span></body></html>

controller

package com.yxtech.sys.controller;import com.yxtech.common.Constant;
import com.yxtech.sys.domain.FileRes;
import com.yxtech.sys.service.FileResService;
import com.yxtech.sys.service.ResService;
import com.yxtech.utils.file.FileMd5Util;
import com.yxtech.utils.file.NameUtil;
import com.yxtech.utils.file.PathUtil;
import jodd.datetime.JDateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;/*** Created by Administrator on 2015/10/9.*/
@RestController
@Scope("prototype")
@RequestMapping(value = "/test")
public class testController {@Autowiredprivate FileResService fileResService;@Autowiredprivate ResService resService;/*** 上传文件** @param request* @return* @throws IllegalStateException* @throws IOException*/@RequestMapping(value = "/upload")public Map<String, Object> upload(HttpServletRequest request, @RequestParam(value = "data",required = false) MultipartFile multipartFile) throws IllegalStateException, IOException, Exception {String action = request.getParameter("action");String uuid = request.getParameter("uuid");String fileName = request.getParameter("name");String size = request.getParameter("size");//总大小int total = Integer.valueOf(request.getParameter("total"));//总片数int index = Integer.valueOf(request.getParameter("index"));//当前是第几片String fileMd5 = request.getParameter("filemd5"); //整个文件的md5String date = request.getParameter("date"); //文件第一个分片上传的日期(如:20170122)String md5 = request.getParameter("md5"); //分片的md5//生成上传文件的路径信息,按天生成String savePath = Constant.FILE_PATH + File.separator + date;String saveDirectory = PathUtil.getAppRootPath(request) + savePath + File.separator + uuid;//验证路径是否存在,不存在则创建目录File path = new File(saveDirectory);if (!path.exists()) {path.mkdirs();}//文件分片位置File file = new File(saveDirectory, uuid + "_" + index);//根据action不同执行不同操作. check:校验分片是否上传过; upload:直接上传分片Map<String, Object> map = null;if("check".equals(action)){String md5Str = FileMd5Util.getFileMD5(file);if (md5Str != null && md5Str.length() == 31) {System.out.println("check length =" + md5.length() + " md5Str length" + md5Str.length() + "   " + md5 + " " + md5Str);md5Str = "0" + md5Str;}if (md5Str != null && md5Str.equals(md5)) {//分片已上传过map = new HashMap<>();map.put("flag", "2");map.put("fileId", uuid);map.put("status", true);return map;} else {//分片未上传map = new HashMap<>();map.put("flag", "1");map.put("fileId", uuid);map.put("status", true);return map;}}else if("upload".equals(action)){//分片上传过程中出错,有残余时需删除分块后,重新上传if (file.exists()) {file.delete();}multipartFile.transferTo(new File(saveDirectory, uuid + "_" + index));}if (path.isDirectory()) {File[] fileArray = path.listFiles();if (fileArray != null) {if (fileArray.length == total) {//分块全部上传完毕,合并String suffix = NameUtil.getExtensionName(fileName);File newFile = new File(PathUtil.getAppRootPath(request) + savePath, uuid + "." + suffix);FileOutputStream outputStream = new FileOutputStream(newFile, true);//文件追加写入byte[] byt = new byte[10 * 1024 * 1024];int len;FileInputStream temp = null;//分片文件for (int i = 0; i < total; i++) {int j = i + 1;temp = new FileInputStream(new File(saveDirectory, uuid + "_" + j));while ((len = temp.read(byt)) != -1) {System.out.println("-----" + len);outputStream.write(byt, 0, len);}}//关闭流temp.close();outputStream.close();//修改FileRes记录为上传成功Example example = new Example(FileRes.class);Example.Criteria criteria = example.createCriteria();criteria.andEqualTo("md5",fileMd5);FileRes fileRes = new FileRes();fileRes.setStatus(Constant.ONE);fileResService.updateByExampleSelective(fileRes,example);}else if(index == 1){//文件第一个分片上传时记录到数据库FileRes fileRes = new FileRes();String name = NameUtil.getFileNameNoEx(fileName);if (name.length() > 50) {name = name.substring(0, 50);}fileRes.setName(name);fileRes.setSuffix(NameUtil.getExtensionName(fileName));fileRes.setUuid(uuid);fileRes.setPath(savePath + File.separator + uuid + "." + fileRes.getSuffix());fileRes.setSize(Integer.parseInt(size));fileRes.setMd5(fileMd5);fileRes.setStatus(Constant.ZERO);fileRes.setCreateTime(new Date());this.fileResService.insert(fileRes);}}}map = new HashMap<>();map.put("flag", "3");map.put("fileId", uuid);map.put("status", true);return map;}/*** 上传文件前校验** @param request* @return* @throws IOException*/@RequestMapping(value = "/isUpload")public Map<String, Object> isUpload(HttpServletRequest request) throws Exception {String md5 = request.getParameter("md5");Example example = new Example(FileRes.class);Example.Criteria criteria = example.createCriteria();criteria.andEqualTo("md5", md5);List<FileRes> list = fileResService.selectByExample(example);Map<String, Object> map = null;return map;}}

FileMd5Util

package com.yxtech.utils.file;import java.io.File;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.MessageDigest;/*** @author cuihao* @create 2017-01-20-15:13*/public class FileMd5Util {public static final String KEY_MD5 = "MD5";public static final String CHARSET_ISO88591 = "ISO-8859-1";/*** Get MD5 of one file:hex string,test OK!** @param file* @return*/public static String getFileMD5(File file) {if (!file.exists() || !file.isFile()) {return null;}MessageDigest digest = null;FileInputStream in = null;byte buffer[] = new byte[1024];int len;try {digest = MessageDigest.getInstance("MD5");in = new FileInputStream(file);while ((len = in.read(buffer, 0, 1024)) != -1) {digest.update(buffer, 0, len);}in.close();} catch (Exception e) {e.printStackTrace();return null;}BigInteger bigInt = new BigInteger(1, digest.digest());return bigInt.toString(16);}/**** Get MD5 of one file!test ok!** @param filepath* @return*/public static String getFileMD5(String filepath) {File file = new File(filepath);return getFileMD5(file);}/*** MD5 encrypt,test ok** @param data* @return byte[]* @throws Exception*/public static byte[] encryptMD5(byte[] data) throws Exception {MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);md5.update(data);return md5.digest();}public static byte[] encryptMD5(String data) throws Exception {return encryptMD5(data.getBytes(CHARSET_ISO88591));}/**** compare two file by Md5** @param file1* @param file2* @return*/public static boolean isSameMd5(File file1,File file2){String md5_1= FileMd5Util.getFileMD5(file1);String md5_2= FileMd5Util.getFileMD5(file2);return md5_1.equals(md5_2);}/**** compare two file by Md5** @param filepath1* @param filepath2* @return*/public static boolean isSameMd5(String filepath1,String filepath2){File file1=new File(filepath1);File file2=new File(filepath2);return isSameMd5(file1, file2);}public static void main(String[] args) {//        for(int i=1;i<79;i++){//            File file = new File("F:\\bookQr_SYS\\target\\bookQr\\files\\20170122\\2124b3f9-a4c8-4297-a182-6249010dcd72\\2124b3f9-a4c8-4297-a182-6249010dcd72_"+i);File file = new File("F:\\bookQr_SYS\\target\\bookQr\\files\\20170122\\2124b3f9-a4c8-4297-a182-6249010dcd72.mp4");String md5Str = getFileMD5(file);System.out.println(""+md5Str.length()+" "+md5Str);
//        }}
}

NameUtil


/*** Created by Administrator on 2015/10/12.*/
public class NameUtil {/*** Java文件操作 获取文件扩展名*/public static String getExtensionName(String filename) {if ((filename != null) && (filename.length() > 0)) {int dot = filename.lastIndexOf('.');if ((dot > -1) && (dot < (filename.length() - 1))) {return filename.substring(dot + 1);}}return filename.toLowerCase();}/*** Java文件操作 获取不带扩展名的文件名*/public static String getFileNameNoEx(String filename) {if ((filename != null) && (filename.length() > 0)) {int dot = filename.lastIndexOf('.');if ((dot > -1) && (dot < (filename.length()))) {return filename.substring(0, dot);}}return filename.toLowerCase();}public static void main(String[] args) {String str = "AAAbb.jpg";System.out.println(getExtensionName(str).toLowerCase());System.out.println(getFileNameNoEx(str).toUpperCase());}
}

PathUtil


import com.yxtech.sys.controller.MailController;import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;/*** Created by Administrator on 2015/10/12.*/
public class PathUtil {/**** @param request* @return 返回结果类似于 “F:\workSpace\bookQr\src\main\webapp\”*/public static String  getAppRootPath(HttpServletRequest request){//ServletActionContext.getServletContext().getRealPath("/")+"upload";return request.getSession().getServletContext().getRealPath("/");}/***自定义文件保存路径* @param request*/public static String  getCustomRootPath(HttpServletRequest request){String path = "";Properties prop = new Properties();InputStream in = MailController.class.getResourceAsStream("/config/jdbc.properties");try {prop.load(in);path = prop.getProperty("FILE_PATH").trim();} catch (IOException e) {e.printStackTrace();}return path;}/**** @param request* @return http://www.qh.com:8080/projectName*/public static String  getHttpURL(HttpServletRequest request) {StringBuffer buff = new StringBuffer();buff.append("http://");buff.append(request.getServerName());buff.append(":");buff.append(request.getServerPort());buff.append(request.getContextPath());return buff.toString();}}

二、效果图

下面以一个56.1M的视频演示。5M一片,总共分了12片。

1、上传完成界面:

2、重复上传界面:

3、服务端界面,左边是分片文件夹,右边是合成的视频


4、数据库表存储记录

下载地址: http://download.csdn.net/download/haohao123nana/10015607(已失效)

新的下载地址: https://download.csdn.net/download/haohao123nana/13107245

有问题加群qq:221070971

大文件分片、并发上传,断点续传,秒传 第二弹相关推荐

  1. .NetCore+WebUploader实现大文件分片上传

    项目要求通过网站上传大文件,比如视频文件,通过摸索实现了文件分片来上传,然后后台进行合并. 使用了开源的前台上传插件WebUploader(http://fex.baidu.com/webupload ...

  2. python 大文件分片上传_Python实现大文件分片上传

    转载请注明出处:http://blog.csdn.net/jinixin/article/details/77545140 引言想借着这篇文章简要谈谈WebUploader大文件上传与Python结合 ...

  3. 大文件分片上传,断点续传,秒传 实现

    前段时间做视频上传业务,通过网页上传视频到服务器. 视频大小 小则几十M,大则 1G+,以一般的HTTP请求发送数据的方式的话,会遇到的问题:1,文件过大,超出服务端的请求大小限制:2,请求时间过长, ...

  4. vue前端上传文件夹的插件_基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件...

    1. 前言 之前公司要在管理系统中做一个全局上传插件,即切换各个页面的时候,上传界面还在并且上传不会受到影响,这在vue这种spa框架面前并不是什么难题.然而后端大佬说我们要实现分片上传.秒传以及断点 ...

  5. formdata上传文件_封装一个多文件断点续传、分片上传、秒传、重试机制的组件...

    本文为:多文件断点续传.分片上传.秒传.重试机制 的更新版,若想看初始版本的实现,请查看该文章. 凡是要知其然知其所以然 文件上传相信很多朋友都有遇到过,那或许你也遇到过当上传大文件时,上传时间较长, ...

  6. 使用webuploader组件实现大文件分片上传,断点续传

    无组件断点续传.gif 1. 组件简介 webuploader:是一个以HTML5为主, Flash为辅的文件上传组件,采用大文件分片/并发上传的方式,极大地提高了文件上传的效率,同时兼容多种浏览器版 ...

  7. html上传文件_.NET基于WebUploader大文件分片上传、断网续传、秒传

    (给DotNet加星标,提升.Net技能) 转自:学习中的苦与乐 cnblogs.com/xiongze520/p/10412693.html 现在的项目开发基本上都用到了上传文件功能,或图片,或文档 ...

  8. 大文件分片上传前端框架_无插件实现大文件分片上传,断点续传

    文件上传.gif 1. 简介: 本篇文章基于实际项目的开发,将介绍项目中关于大文件分片上传.文件验证.断点续传.手动重试上传等需求的使用场景及实现: 2. 项目需求 在一个音视频的添加中,既要有音视频 ...

  9. 无插件实现大文件分片上传,断点续传

    代码地址如下: http://www.demodashi.com/demo/11888.html 1. 简介: 本篇文章基于实际项目的开发,将介绍项目中关于大文件分片上传.文件验证.断点续传.手动重试 ...

最新文章

  1. java 重载与覆盖_Java重载与覆盖
  2. 硅谷产品实战-总结:14、如何用数据做出产品决定?
  3. 20165101刘天野 2018-2019-2《网络对抗技术》第1周 Kali的安装
  4. 关于GestureDetector的onFling方法e1返回null问题
  5. HikariCP连接池配置
  6. 工作380-js判断是否为空
  7. leetcode--数组(Easy)
  8. 恐怖地狱火恶魔叉404模板下载
  9. [建议]添加模板功能
  10. html日期自动更新,原生javascript实现自动更新的时间日期_javascript技巧
  11. 三维点云配准方法(两帧)
  12. 深度学习之卷积神经网络CNN及tensorflow代码实现示例详细介绍(转载)
  13. 连续型随机变量单点概率为0以及不可能事件
  14. 老调重谈:C语言中的指针和数组
  15. 用STM32CubeIDE速攻FreeRTOS
  16. gcc与cuda的关系
  17. Android win10 平板 省电,小编解答win10系统Mobile设置更省电的图文办法
  18. 网站服务器如何选择?
  19. VSCode调试C/C++项目
  20. freeMaker 三目(三元)表达式

热门文章

  1. 被市场模糊化的零信任,现在又开始浮出水面,进一步结合了市场零信任的需求
  2. Bribe the prison思路理解
  3. c++ getline
  4. 2.龙芯2k1000 linux3.10内核编译过程
  5. python 环境迁移,新电脑安装python把旧电脑的包导入新电脑
  6. python 仪表数字识别_利用Python进行数字识别
  7. 常见网络安全设备默认口令
  8. 塞尔达传说顺序_电子游戏《塞尔达传说》中的怪物如何结成历史小说
  9. windows文件夹同步
  10. 迅雷快鸟覆盖全国30个省市,北京联通最高提速至500M