Springboot程序员javaWeb学习

HTML5结合springboot带进度条大文件分段上传

2018-09-14  本文已影响138人  极微
一、背景

web应用文件上传是经常会碰到的,一般我们上传的都是几十MB以内的文件,当我们要传输视频等大文件时,首先springboot的默认配置为10MB,大于这个我们是传不上服务器的,如果我们修改了默认配置,那无疑会加重服务器的负担。其次当文件大于一定程度时,不仅浏览器也会占用大量内存,而且http传输极可能会中断。
这里本人参考了大量资料做了一个http文件分段上传,支持GB级别和分段续传。前端主要用了HTML5的File API切割文件,后端主要用了java文件合并功能。不多说了,上代码。

二、前端代码
<script type="text/javascript">
    function uploadFile(){
        var file = $("#file")[0].files[0];  //文件对象
        if(file==undefined){
            alert("请先选中文件");
            return;
        }
        if(file.size>1024*1024*1024*2){
            alert("文件不能大于2GB");
            return;
        }
        $("#upload").attr("disabled","disabled");
        isUpload(file);
    }
    function isUpload (file) {
        var form=new FormData();
        var reader = new FileReader();
        //绑定读取失败事件
        $(reader).error(function(e){
            $("#upload").removeAttr("disabled");
            alert("读取失败");
        })
        
        var fileWithSize=file;
        //根据文件大小计算fileMd5值
        if(file.size>1024*1024*50){
            var fileStart=file.slice(0,1024*1024);
            var fileEnd=file.slice(file.size+1);
            var arr=[fileStart,fileEnd];
            fileWithSize = new Blob(arr, { type: "text/plain" });
        }
        //绑定读取成功事件
        $(reader).load(function(e){
                var fileMd5 = hex_md5(reader.result);

            form.append("fileMd5", fileMd5);  
            form.append("videoSize", file.size);  
            //校验是否上传过该文件,返回上传进度
            $.ajax({ 
                url: "${basePath}/global/isFileExist",
    
                type: "POST",

                data: form,

                async: true,        //异步

                processData: false,  //很重要,告诉jquery不要对form进行处理

                contentType: false,  //很重要,指定为false才能形成正确的Content-Type

                success: function(data){
                    if(data.code==1){
                        $("#process").css("width","100%");
                        $("#process").html("100%");
                        $("#upload").removeAttr("disabled");
                        alert(data.msg)
                    }else{//视频未上传或者部分上传
                        //分片上传
                        $("#process").css("width","0%");
                        $("#process").html("");
                        uploadBySplit(file,fileMd5,0);
                    }
                },
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    $("#upload").removeAttr("disabled");
                    alert("服务器出错!");
                }
            });
        })
        
        reader.readAsBinaryString(fileWithSize);
    }
    //分片上传
    function uploadBySplit(file,fileMd5,i){
        var splitSize=1024*1024*20;//分片大小20M
        var size=file.size;//总大小
        splitCount = Math.ceil(size / splitSize);  //总片数
        
        if(i==splitCount){
            $("#upload").removeAttr("disabled");
            return;
        }
        
        //计算每一片的起始与结束位置
        var start = i * splitSize;
        var end = Math.min(size, start + splitSize);
        
        var fileData=file.slice(start,end);
        var reader = new FileReader();
        $(reader).load(function(e){
                var md5 = hex_md5(reader.result);

            //构造一个表单,FormData是HTML5新增的
            var form = new FormData();
            form.append("fileMd5", fileMd5);
            form.append("size", size);//总大小
            form.append("total", splitCount);  //总片数
            form.append("index", i);        //当前是第几片
            form.append("md5", md5); 
            
            //判断分片是否上传
             $.ajax({
                url: "${basePath}/global/isFileSplitExist",

                type: "POST",

                data: form,

                async: true,        //异步

                processData: false,  //很重要,告诉jquery不要对form进行处理

                contentType: false,  //很重要,指定为false才能形成正确的Content-Type
                
                success: function(data){
                    if(data.code==1){//已上传
                        //处理上传进度
                        var process=Math.round(end/size*100)+"%";
                        $("#process").css("width",process);
                        $("#process").html(process);
                        i++;
                        uploadBySplit(file,fileMd5,i);
                    }else{//未上传
                        form.append("fileData", fileData);
                        //上传分片
                        $.ajax({
                            url: "${basePath}/global/fileUpload",

                            type: "POST",
            
                            data: form,
            
                            async: true,        //异步
            
                            processData: false,  //很重要,告诉jquery不要对form进行处理
            
                            contentType: false,  //很重要,指定为false才能形成正确的Content-Type
                            
                            success: function(data){
                                //处理上传进度
                                var process=Math.round(end/size*100)+"%";
                                $("#process").css("width",process);
                                $("#process").html(process);
                                i++;
                                uploadBySplit(file,fileMd5,i);
                            },
                             error: function(XMLHttpRequest, textStatus, errorThrown) {
                                $("#upload").removeAttr("disabled");
                                alert("服务器出错!");
                            }
                        
                        });
                    }
                },
                
                error: function(XMLHttpRequest, textStatus, errorThrown) {
                    $("#upload").removeAttr("disabled");
                    alert("服务器出错!");
                }
             });
            
            
        });
        reader.readAsBinaryString(fileData);
       
    }
三、后台代码
package com.daotong.controllers.common;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.daotong.controllers.base.DaoTongBaseController;
import com.daotong.core.conf.WebMappingTables;
import com.shove190.core.plugins.data.ActionResult;

/**
 * 视频上传类
 */
@RestController
public class FileUploadController extends DaoTongBaseController {

    @Value("${savePath}")
    private String savePath;

    /**
     * 视频分片上传公共接口,考虑到用户效率,采用MD5算法确认文件唯一性,理论上文件不同而摘要相同的概率几近于0,对于发生hash碰撞的两个文件,
     * 其得到的视频将会是与其发生hash碰撞的视频。
     *
     * @param request
     * @return
     * @throws IllegalStateException
     * @throws IOException
     */
    @SuppressWarnings("rawtypes")
    @RequestMapping(value = WebMappingTables.MAPPING_WEB_COMMON_FIELUPLOADCONTROLLER)
    public ActionResult upload(MultipartFile fileData) throws IllegalStateException, IOException, Exception {
        // 搜素目录下是否包含有相同的MD5文件?

        int total = Integer.valueOf(request.getParameter("total"));// 总片数
        int index = Integer.valueOf(request.getParameter("index"));// 当前是第几片,第一片index必须为0
        String fileMd5 = request.getParameter("fileMd5"); // 整个文件的md5
        // 文件夹位置
        File parent = new File(savePath + File.separator + fileMd5);
        if (!parent.exists()) {
            parent.mkdirs();
        }
        // 文件分片位置
        File file = new File(parent, fileMd5 + "_" + index);
        // 保存分片到本地
        if (file.exists()) {
            file.delete();
        }
        // 由前端控制上传进度
        fileData.transferTo(file);

        // 判断所有分片是否全部上传完成,完成则合并
        File dir = new File(savePath + File.separator + fileMd5);
        File[] files = dir.listFiles();
        if (files.length == total) {// 上传完成
            FileOutputStream fileOutputStream = null;
            FileInputStream temp = null;// 文件分片
            File newFile = new File(dir, fileMd5);

            fileOutputStream = new FileOutputStream(newFile, true);
            byte[] byt = new byte[10 * 1024 * 1024];
            int len;

            for (int i = 0; i < total; i++) {
                temp = new FileInputStream(new File(dir, fileMd5 + "_" + i));
                while ((len = temp.read(byt)) != -1) {
                    fileOutputStream.write(byt, 0, len);
                }
            }
            fileOutputStream.close();
            temp.close();
            // 全部完成之后,删除分片
            for (int i = 0; i < total; i++) {
                File splitFile = new File(dir, fileMd5 + "_" + i);
                if (splitFile.exists()) {
                    splitFile.delete();
                }
            }
        }
        return buildActionResult("操作成功");
    }

    /**
     * 获取文件的MD5值
     */
    private 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());
        // 16进制
        return bigInt.toString(16);
    }

    /**
     * 检测视频是否已上传
     */
    @RequestMapping(value = WebMappingTables.MAPPING_WEB_COMMON_FIELUPLOADCONTROLLER_ISFILEEXIST)
    public ActionResult<Object> isFileExist() {
        File pdir = new File(savePath);
        if (!pdir.exists()) {
            pdir.mkdirs();
        }

        ActionResult<Object> actionResult = new ActionResult<Object>();

        String fileMd5 = request.getParameter("fileMd5");

        File dir = new File(savePath + File.separator + fileMd5);

        if (dir.exists()) {// 目录存在
            File file = new File(dir, fileMd5);
            if (file.exists()) {// 文件存在
                actionResult.setCode(1);
                actionResult.setMsg("文件已上传,秒传");
                return actionResult;
            } else {// 文件不存在,计算分片大小
                actionResult.setCode(-1);
                actionResult.setMsg("文件已部分上传");
                return actionResult;
            }
        } else {
            actionResult.setCode(-1);
            actionResult.setMsg("文件未上传");
            return actionResult;
        }
    }

    /**
     * 检测视频分片是否已上传
     */
    @RequestMapping(value = WebMappingTables.MAPPING_WEB_COMMON_FIELUPLOADCONTROLLER_ISFILESPLITEXIST)
    public ActionResult<Object> isFileSplitExist() {
        ActionResult<Object> actionResult = new ActionResult<>();

        String splitMd5 = getReqStringParams("md5");// 分片Md5
        String fileMd5 = getReqStringParams("fileMd5");// 文件md5
        int index = getReqIntegerParams("index");// 第几片

        File file = new File(savePath + File.separator + fileMd5, fileMd5 + "_" + index);
        if (file.exists()) {
            if (splitMd5.equals(getFileMD5(file))) {
                actionResult.setCode(1);
                actionResult.setMsg("视频分片已上传");
                return actionResult;
            } else {// 视频分片损坏,删除重传
                file.delete();

                actionResult.setCode(-1);
                actionResult.setMsg("视频分片已损坏");
                return actionResult;
            }
        } else {
            actionResult.setCode(-1);
            actionResult.setMsg("视频分片未上传");
            return actionResult;
        }

    }
    public static void main(String[] args) {
        File file=new File("E:\\saveFileDir\\f18215e2014b70b78e30ce2af989643a\\f18215e2014b70b78e30ce2af989643a");
        System.out.println(new FileUploadController().getFileMD5(file));
        //f18215e2014b70b78e30ce2af989643a
        //f18215e2014b70b78e30ce2af989643a
        String path = ClassUtils.getDefaultClassLoader().getResource("").getPath();
        System.out.println(path);
    }
}
上一篇下一篇

猜你喜欢

热点阅读