Java大文件分片上传

分片上传需要跟前端配合

1,前端代码

在这里插入图片描述

<dody>
	<input type="file" name="upload" id="file"/>
	<button id="slice">上传</button>
</dody>
<script>
$("#slice").change(function(event) {
    var file = $("#slice")[0].files[0];
    PostFile(file,0);
});
$("#slice").on('click',function() {
    console.log('点击');
    var file = $("#file")[0].files[0];
    PostFile(file, 0);
});
//执行分片上传
function PostFile(file,i, uuid){
    var name = file.name, //文件名
        size = file.size, //总大小shardSize = 2 * 1024 * 1024,
        shardSize = 10 * 1024 * 1024, //以2MB为一个分片,每个分片的大小
        shardCount = Math.ceil(size / shardSize); //总片数
    if(i >= shardCount){
        return;
    }

    //判断uuid是否存在
    if (uuid == undefined || uuid == null) {
        uuid = guid();
    }

    //console.log(size,i+1,shardSize); //文件总大小,第一次,分片大小//
    var start = i * shardSize;
    var end = start + shardSize;
    var packet = file.slice(start, end); //将文件进行切片
    /* 构建form表单进行提交 */
    var form = new FormData();
    form.append("uuid", uuid);// 前端生成uuid作为标识符传个后台每个文件都是一个uuid防止文件串了
    form.append("data", packet); //slice方法用于切出文件的一部分
    form.append("name", name);
    form.append("totalSize", size);
    form.append("total", shardCount); //总片数
    form.append("index", i + 1); //当前是第几片
    form.append("moduleName", 'test'); //模块名
    form.append("recordId", '111'); //记录id
    form.append("fileType", 'aaa'); //文件类型
    $.ajax({
        url: "http://localhost:9888/inspactData/file/doPost",
        type: "POST",
        data: form,
        //timeout:"10000", //超时10秒
        async: true, //异步
        dataType:"json",
        processData: false, //很重要,告诉jquery不要对form进行处理
        contentType: false, //很重要,指定为false才能形成正确的Content-Type
        success: function (msg) {
            console.log(msg);
            /* 表示上一块文件上传成功,继续下一次 */
            if (msg.data.status == 201) {
                form = '';
                i++;
                PostFile(file, i, uuid);
            } else if (msg.data.status == 502) {
                form = '';
                /* 失败后,每2秒继续传一次分片文件 */
                setInterval(function () { PostFile(file, i, uuid) }, 2000);
            } else if (msg.data.status == 200) {
                merge(uuid, name)
                console.log("上传成功");
            } else if (msg.data.status == 500) {
                console.log('第'+msg.i+'次,上传文件有误!');
            } else {
                console.log('未知错误');
            }
        }
    })
}

function merge(uuid, fileName) {
    $.ajax({
        url: "http://localhost:9888/inspactData/file/merge",
        type: "GET",
        data: {uuid: uuid, newFileName: fileName,moduleName:'test',recordId:'111',fileType:'aaa'},
        //timeout:"10000", //超时10秒
        async: true, //异步
        dataType:"json",
        success: function (msg) {
            console.log(msg);
        }
    })
}

function guid() {
    return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0,
            v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

2,controller

/**
 * 分片
 * @param: req
 * @return: resp
 */
@RequestMapping("/doPost")
@ResponseBody
public Map fragmentation(HttpServletRequest req, HttpServletResponse resp,@RequestParam(required = false) String moduleName,@RequestParam(required = false) String recordId) {
    resp.addHeader("Access-Control-Allow-Origin", "*");
    return fileUploadBiz.fragmentation(req,resp,moduleName,recordId);
}
/**
 * 合并
 * @param: uuid
 * @param: newFileName
 * @param: moduleName
 * @param: recordId
 * @param: fileType
 * @param: resp
 * @return Map
 */
@RequestMapping(value = "/merge", method = RequestMethod.GET)
@ResponseBody
public Map merge(String uuid, String newFileName,@RequestParam(required = false) String moduleName,@RequestParam(required = false) String recordId ,HttpServletResponse resp) {
    resp.addHeader("Access-Control-Allow-Origin", "*");
    return fileUploadBiz.merge(uuid,newFileName,moduleName,recordId);
}

3,service

service两行代码此处省略

4,impl

private final String[] arrSuffix = {"jpg", "jpeg", "jpe", "bmp", "gif", "png", "svg", "rar", "zip", "7z", "pdm", "doc", "xls", "ppt", "tif", "txt", "chm", "docx", "xlsx", "pptx", "pdf", "vsd", "mpp", "jpg", "jpeg", "jpe", "bmp", "gif", "png", "avi", "asx", "asf", "mpg", "wmv", "3gp", "mp3", "mp4", "mov", "flv", "wmv9", "rm", "rmvb", "et", "wps", "dps", "pmp", "bdc", "ceb", "wmf", "ofd", "ttf", "woff", "woff2", "ttc", "fon", "font", "otf", "eot"};
@Value("${file.path}")
private String filePath;
/**
 * @param req
 * @param resp
 * @return
 */
@Override
public Map fragmentation(HttpServletRequest req, HttpServletResponse resp,String moduleName,String recordId) {
    Map map = new HashMap<>();
    MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
    // 获得文件分片数据
    MultipartFile file = multipartRequest.getFile("data");
    // 分片第几片
    int index = Integer.parseInt(multipartRequest.getParameter("index"));
    // 总片数
    int total = Integer.parseInt(multipartRequest.getParameter("total"));
    // 获取文件名
    String fileName = multipartRequest.getParameter("name");
    String name = fileName.substring(0, fileName.lastIndexOf("."));
    String fileEnd = fileName.substring(fileName.lastIndexOf(".")+1);//文件后缀
    //判断上传文件类型是否是指定类型
    boolean isContains = Arrays.asList(arrSuffix).contains(fileEnd);
    if (isContains == false) {
        throw new InspactDataException(InspactDataResultEnum.FILE_TYPE_EXCEEDED);
    }

    //临时文件存放地址,D:/home/file/attachment/tempDir/uuid/,uuid/文件名/当前分片数/.tem
    File uploadFile = new File(filePath +"/tempDir/"+(moduleName!=null?moduleName+ "/":"")  + (recordId!=null?recordId+ "/":"")  + (fileEnd!=null?fileEnd+ "/":"")+ "/" , name + "("+index + ").tem");
    //判断文件父目录是否存在
    if (!uploadFile.getParentFile().exists()) {
        uploadFile.getParentFile().mkdirs();//这里因为创建的是多级目录,所以需要使用mkdirs()方法。使用mkdir()方法则文件夹创建不成功,会报找不到路径错误。
    }
    if (index < total) {//当前分片数小于总分片数
        try {
            file.transferTo(uploadFile);
            // 上传的文件分片名称
            map.put("status", 201);
            return map;
        } catch (IOException e) {
            e.printStackTrace();
            map.put("status", 502);
            return map;
        }
    } else {
        try {
            file.transferTo(uploadFile);
            // 上传的文件分片名称
            map.put("status", 200);
            return map;
        } catch (IOException e) {
            e.printStackTrace();
            map.put("status", 502);
            return map;
        }
    }
}

/**
 * @param uuid
 * @param fileName 文件名
 * @param moduleName 模块名
 * @param recordId 记录id
 * @param fileType 文件类型
 * @return
 */
@Override
public Map merge(String uuid, String fileName,String moduleName,String recordId) {

    Map retMap = new HashMap();
    try {
        //获取后缀
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        //临时目录地址 D:/home/file/attachment/tempDir/test/111/aaa/
        String tempPath =filePath +"tempDir/"+(moduleName!=null?moduleName+ "/":"")  + (recordId!=null?recordId+ "/":"")  + (suffix!=null?suffix+ "/":"");
        //主目录地址  D:/home/file/attachment/test/111/aaa/
        String mainPath =filePath +(moduleName!=null?moduleName+ "/":"")  + (recordId!=null?recordId+ "/":"")  + (suffix!=null?suffix+ "/":"");
        File dirFile = new File(tempPath);
        //判断临时目录是否存在
        if (!dirFile.exists()) {
            throw new RuntimeException("文件不存在!");
        }
        //分片上传的文件已经位于同一个文件夹下 ,使用map排序 确保合并时文件的完整性
        String[] arrFileName = dirFile.list();
        Map map = new HashMap();
        //存储排序后的文件名
        List list = new ArrayList();
        for (int i = 0; i <= arrFileName.length -1 ; i++) {
            Integer index = Integer.valueOf(arrFileName[i].substring(arrFileName[i].indexOf("(") + 1, arrFileName[i].indexOf(")")));
            map.put(index,arrFileName[i]);
        }
        Set<Integer> keySet = map.keySet();
        Iterator<Integer> iter = keySet.iterator();
        while (iter.hasNext()) {
            Integer key = iter.next();
            list.add(map.get(key));
        }
        //判断主目录是否存在 不存在则创建  RandomAccessFile无法创建多级目录  D:/home/file/attachment/test/111/aaa/
        File file = new File(mainPath);
        if (!file.exists()) {
            file.mkdirs();//这里因为创建的是多级目录,所以需要使用mkdirs()方法。使用mkdir()方法则文件夹创建不成功,会报找不到路径错误。
        }
        //文件上传名字重复后缀+1 获得+1后的文件名
        String  newFileName = checkFileName(filePath + (moduleName!=null?moduleName+ "/":"")  + (recordId!=null?recordId+ "/":"")  + (suffix!=null?suffix+ "/":"") , fileName);
        // 拼接后的文件上传地址
        fileName = newFileName;
        // 创建空的合并文件   D:/home/file/attachment/tempDir/test/111/aaa/simhei_itmop.com.zip
        File targetFile = new File(mainPath, fileName);
        //将合并的文件写到主目录D:/home/file/attachment/tempDir/test/111/aaa/simhei_itmop.com.zip
        RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw");
        int position = 0;
        //遍历同一个文件夹下的所有文件
        for (Object fileNames : list) {
            //源临时文件目录 D:/home/file/attachment/tempDir/test/111/aaa/
            File sourceFile = new File(tempPath , String.valueOf(fileNames));
            //循环读取临时文件
            RandomAccessFile readFile = new RandomAccessFile(sourceFile, "rw");
            int chunksize = 1024 * 3;
            byte[] buf = new byte[chunksize];
            writeFile.seek(position);
            int byteCount = 0;
            while ((byteCount = readFile.read(buf)) != -1) {
                if (byteCount != chunksize) {
                    byte[] tempBytes = new byte[byteCount];
                    System.arraycopy(buf, 0, tempBytes, 0, byteCount);
                    buf = tempBytes;
                }
                writeFile.write(buf);
                position = position + byteCount;
            }
            readFile.close();
            String pathTemp = sourceFile.getPath();
            File deleteFile=new File(pathTemp);
            deleteFile.delete();//删除缓存的临时文件
        }
        writeFile.close();
        //返回前端的url地址 需要拼接IP端口
        String fileUrl="/"+(moduleName!=null?moduleName+ "/":"")  + (recordId!=null?recordId+ "/":"")  + (suffix!=null?suffix+ "/":"")+fileName;
        retMap.put("url", fileUrl);
    } catch (IOException e) {
        e.printStackTrace();
        retMap.put("code", "500");
    }
    return retMap;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值