利用SpringBoot和vue-simple-uploader进行文件的分片上传

利用SpringBoot和vue-simple-uploader进行文件的分片上传

效果【上传Zip文件为例,可以自行扩展】

利用SpringBoot和vue-simple-uploader进行文件的分片上传_第1张图片

引入vue-simple-uploader

  1. 安装上传插件
    npm install vue-simple-uploader --save
    
  2. main.js全局引入上传插件
    import uploader from 'vue-simple-uploader'
    Vue.use(uploader)
    
  3. 安装md5校验插件(保证上传文件的完整性和一致性)
    npm install spark-md5 --save
    

页面






后台

  1. 引入工具

    
        commons-io
        commons-io
        2.6
    
    
    
        commons-lang
        commons-lang
        2.6
    
    
  2. 控制类

    import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.File;
    
    @RestController
    @RequestMapping("/chunk")
    public class ChunkController {
        @RequestMapping("/chunkUpload")
        public StdOut chunkUpload(MultipartFileParam param, HttpServletRequest request, HttpServletResponse response) {
            StdOut out = new StdOut();
    
            File file = new File("C:\\chunk_test");//存储路径
    
            ChunkService chunkService = new ChunkService();
    
            String path = file.getAbsolutePath();
            response.setContentType("text/html;charset=UTF-8");
    
            try {
                //判断前端Form表单格式是否支持文件上传
                boolean isMultipart = ServletFileUpload.isMultipartContent(request);
                if (!isMultipart) {
                    out.setCode(StdOut.PARAMETER_NULL);
                    out.setMessage("表单格式错误");
                    return out;
                } else {
                    param.setTaskId(param.getIdentifier());
                    out.setModel(chunkService.chunkUploadByMappedByteBuffer(param, path));
                    return out;
                }
            } catch (NotSameFileExpection e) {
                out.setCode(StdOut.FAIL);
                out.setMessage("MD5校验失败");
                return out;
            } catch (Exception e) {
                out.setCode(StdOut.FAIL);
                out.setMessage("上传失败");
                return out;
            }
        }
    }
    
  3. StdOut类(只是封装的返回类)

    public class StdOut {
        public static final int SUCCESS = 200;
        public static final int FAIL = 400;
        public static final int PARAMETER_NULL = 500;
        public static final int NO_LOGIN = 600;
        private int code = 200;
        private Object model = null;
        private String message = null;
    
        public StdOut() {
            this.setCode(200);
            this.setModel((Object)null);
        }
    
        public StdOut(int code) {
            this.setCode(code);
            this.setModel((Object)null);
        }
    
        public StdOut(List> model) {
            this.setCode(200);
            this.setModel(model);
        }
    
        public StdOut(int code, List> model) {
            this.setCode(code);
            this.setModel(model);
        }
    
        public int getCode() {
            return this.code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String toString() {
            return JSON.toJSONString(this);
        }
    
        public Object getModel() {
            return this.model;
        }
    
        public void setModel(Object model) {
            this.model = model;
        }
    
        public String getMessage() {
            return this.message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
  4. MultipartFileParam类(文件信息类)

    import org.springframework.web.multipart.MultipartFile;
    
    public class MultipartFileParam {
        private String taskId;
        private int chunkNumber;
        private long chunkSize;
        private int totalChunks;
        private String identifier;
        private MultipartFile file;
    
        public String getTaskId() {
            return taskId;
        }
    
        public void setTaskId(String taskId) {
            this.taskId = taskId;
        }
    
        public int getChunkNumber() {
            return chunkNumber;
        }
    
        public void setChunkNumber(int chunkNumber) {
            this.chunkNumber = chunkNumber;
        }
    
        public long getChunkSize() {
            return chunkSize;
        }
    
        public void setChunkSize(long chunkSize) {
            this.chunkSize = chunkSize;
        }
    
        public int getTotalChunks() {
            return totalChunks;
        }
    
        public void setTotalChunks(int totalChunks) {
            this.totalChunks = totalChunks;
        }
    
        public String getIdentifier() {
            return identifier;
        }
    
        public void setIdentifier(String identifier) {
            this.identifier = identifier;
        }
    
        public MultipartFile getFile() {
            return file;
        }
    
        public void setFile(MultipartFile file) {
            this.file = file;
        }
    }
    
  5. ChunkService类

    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.commons.io.FileUtils;
    import org.apache.commons.lang.StringUtils;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.util.UUID;
    
    public class ChunkService {
        public String chunkUploadByMappedByteBuffer(MultipartFileParam param, String filePath) throws IOException, NotSameFileExpection {
    
            if (param.getTaskId() == null || "".equals(param.getTaskId())) {
                param.setTaskId(UUID.randomUUID().toString());
            }
    
            String fileName = param.getFile().getOriginalFilename();
            String tempFileName = param.getTaskId() + fileName.substring(fileName.lastIndexOf(".")) + "_tmp";
            File fileDir = new File(filePath);
            if (!fileDir.exists()) {
                fileDir.mkdirs();
            }
            File tempFile = new File(filePath, tempFileName);
            //第一步 打开将要写入的文件
            RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
            //第二步 打开通道
            FileChannel fileChannel = raf.getChannel();
            //第三步 计算偏移量
            long position = (param.getChunkNumber() - 1) * param.getChunkSize();
            //第四步 获取分片数据
            byte[] fileData = param.getFile().getBytes();
            //第五步 写入数据
            fileChannel.position(position);
            fileChannel.write(ByteBuffer.wrap(fileData));
            fileChannel.force(true);
            fileChannel.close();
            raf.close();
            //判断是否完成文件的传输并进行校验与重命名
            boolean isComplete = checkUploadStatus(param, fileName, filePath);
            if (isComplete) {
                FileInputStream fileInputStream = new FileInputStream(tempFile.getPath());
                String md5 = DigestUtils.md5Hex(fileInputStream);
                fileInputStream.close();
                if (StringUtils.isNotBlank(md5) && !md5.equals(param.getIdentifier())) {
                    throw new NotSameFileExpection();
                }
                renameFile(tempFile, fileName);
                return fileName;
            }
            return null;
        }
    
        public void renameFile(File toBeRenamed, String toFileNewName) {
            if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {
                System.err.println("文件不存在");
                return;
            }
            String p = toBeRenamed.getParent();
            File newFile = new File(p + File.separatorChar + toFileNewName);
            toBeRenamed.renameTo(newFile);
        }
    
        public boolean checkUploadStatus(MultipartFileParam param, String fileName, String filePath) throws IOException {
            File confFile = new File(filePath, fileName + ".conf");
            RandomAccessFile confAccessFile = new RandomAccessFile(confFile, "rw");
            //设置文件长度
            confAccessFile.setLength(param.getTotalChunks());
            //设置起始偏移量
            confAccessFile.seek(param.getChunkNumber() - 1);
            //将指定的一个字节写入文件中 127,
            confAccessFile.write(Byte.MAX_VALUE);
            byte[] completeStatusList = FileUtils.readFileToByteArray(confFile);
            confAccessFile.close();//不关闭会造成无法占用
            //创建conf文件文件长度为总分片数,每上传一个分块即向conf文件中写入一个127,那么没上传的位置就是默认的0,已上传的就是127
            for (int i = 0; i < completeStatusList.length; i++) {
                if (completeStatusList[i] != Byte.MAX_VALUE) {
                    return false;
                }
            }
            confFile.delete();
            return true;
        }
    }
    

  6.NotSameFileExpection类

public class NotSameFileExpection extends Exception {
    public NotSameFileExpection() {
        super("File MD5 Different");
    }
}

遇到问题

  • 根据自己的实际情况进行取舍,灵活处理

你可能感兴趣的:(前后台,上传文件,分片上传)