分片上传和断点续传

我们在工作中可能会遇到大文件的上传,如果一次传输,很有可能会遇到 网络、超时各种各样的问题。这里将分片上传和断点续传两种方式介绍一下方案。

一、分片上传

分片上传的原理:前端首先请求接口获取这次上传的uuid,上传的时间等信息返回给前端页面。前端将所要上传文件进行分片,在上传的时候包含 服务器返回给前端的 该次上传的uuid,该篇文件的md5,上传的第几片的的标识,和文件名称等标识。等到上传的片数等于总片数的时候对所有分片进行合并,删除临时分片。

这里由springboot+freemarker 来演示这个原理
(服务端用到工具类和前端用到的 js由于断点续传 同样也用到,在介绍完 断点续传 之后统一贴出来)

1、首先添加配置文件 application.properties(由于断点续传 需要依赖mysql故也将mysql引入)

server.port=8080

spring.freemarker.expose-spring-macro-helpers=true
# 是否优先从文件系统加载template,以支持热加载,默认为true
spring.freemarker.prefer-file-system-access=true
# 设定模板的后缀.
spring.freemarker.suffix=.ftl
# 设定模板的加载路径,多个以逗号分隔,默认:
spring.freemarker.template-loader-path=classpath:/templates/
spring.mvc.static-path-pattern=/static/**


spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/pingyougou
spring.datasource.username=root
spring.datasource.password=root

#配置.xml文件路径
mybatis.config-locations=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:conf/mapper/*Mapper.xml
#配置模型路径
mybatis.type-aliases-package=com.yin.freemakeradd.pojo

2、引入controller类

localhost:8080/upload2/index 第断点上传入口

@Controller
@RequestMapping(value = "/test")
public class IndexController {
    @GetMapping(value = "/upload/index")
    public String  index(Model model){
        return "breakpointBurst";
    }

    @GetMapping(value = "/upload2/index")
    public String  index2(Model model){
        return "burst";
    }

}

分片上传的具体上传逻辑

@RestController
@RequestMapping(value = "/burst/test")
public class BurstController {

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

    private static final String temp_dir = "C:\\Users\\Administrator\\Desktop\\upload";
    private static final String final_dir = "C:\\Users\\Administrator\\Desktop\\test";
    /**
     * 上传文件
     *
     * @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 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"));
        //整个文件的md5
        String fileMd5 = request.getParameter("filemd5"); 
        //文件第一个分片上传的日期(如:20200118)
        String date = request.getParameter("date"); 
        //分片的md5
        String md5 = request.getParameter("md5"); 

        //生成上传文件的路径信息,按天生成
        String savePath = "\\upload" + File.separator + date;
        String saveDirectory = temp_dir + savePath +File.separator+uuid;
        //验证路径是否存在,不存在则创建目录
        File path = new File(saveDirectory);
        if (!path.exists()) {
            path.mkdirs();
        }
        //文件分片位置
        //File file = new File(saveDirectory, uuid + "_" + index);


        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);
                    String dir = final_dir + savePath;
                    File newFile = new File(dir, uuid + "." + suffix);
                    File copyDir = new File(dir);
                    if(!copyDir.mkdir()){
                        copyDir.mkdirs();
                    }
                    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;
                        FileInputStream 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();
                    //删除临时文件
                    FileUtil.deleteFolder( temp_dir+ savePath +File.separator+uuid);
                }
            }
        }

        HashMap map = new HashMap<>();
        map.put("fileId", uuid);
        return map;
    }

    /**
     * 上传文件前获取id和日期(如果是分片上传这一步可以交给前端处理)
     *
     * @param request
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "/isUpload")
    public Map<String, Object> getUpload(HttpServletRequest request) throws Exception {

        HashMap<String,Object> map = new HashMap<>();
        String uuid = UUID.randomUUID().toString();
        map.put("fileId", uuid);
        map.put("date", formatter.format(LocalDateTime.now()));
        return map;


    }


}

分片前端展示:

<!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 type="text/javascript" src="/static/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 () {
                    $("#upload").attr('disabled', true)
                    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//burst/test/isUpload",

                    type: "POST",

                    data: form,
					 //异步
                    async: true,       
					//很重要,告诉jquery不要对form进行处理
                    processData: false,  
					//很重要,指定为false才能形成正确的Content-Type
                    contentType: false,  

                    success: function (dataObj) {

                        // var dataObj = eval("("+data+")");

                        var uuid = dataObj.fileId;
                        var date = dataObj.date;

                        //没有上传过文件
                        upload(file, uuid, md5, date);


                    }, 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 = 512 * 1024,    //以0.5m为一个分片

                    shardCount = Math.ceil(size / shardSize);  //总片数


            if (i > shardCount) {

                i = -1;

                i = shardCount;

            } else {

                i += 1;  //只有在检测分片时,i才去加1; 上传文件时无需加1


            }

            //计算每一片的起始与结束位置

            var start = i * shardSize,

            end = Math.min(size, start + shardSize);


            //构造一个表单,FormData是HTML5新增的

            var form = new FormData();


            form.append("action", "upload");  //直接上传分片

            form.append("data", file.slice(start, end));  //slice方法用于切出文件的一部分
   
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值