springboot 、html分片上传,断点续传

springboot 、html分片上传,断点续传

  1. 后端代码
	private String UPLOAD_URL = "F:/20230509/";
    private String UPLOAD_SUFFIX_URL = "/gsl/www/";
    public  String getUPLOAD_URL() {
        return UPLOAD_URL + UPLOAD_SUFFIX_URL;
    }

    @PostMapping("/o_upload")
    public Result upload1(@RequestParam("file") MultipartFile multipartFile,
                          @RequestParam("fileName") String fileName,
                          @RequestParam("chunkNumber") int chunkNumber,
                          @RequestParam("totalChunks") int totalChunks) throws IOException {
        UserDetail user = SecurityUser.getUser();
        String originalFileName = StringUtils.cleanPath(multipartFile.getOriginalFilename());
        String fileExtension = getFileExtension(originalFileName);
        String uniqueFileName = fileName + "_" + chunkNumber + "." + fileExtension;
        Path uploadPath = Paths.get(this.getUPLOAD_URL());
        if (!Files.exists(uploadPath)) {
            Files.createDirectories(uploadPath);
        }
        Path filePath = Paths.get(this.getUPLOAD_URL() + uniqueFileName);
        Files.write(filePath, multipartFile.getBytes());
        if (chunkNumber == totalChunks - 1) {
            return mergeChunks(fileName, totalChunks, fileExtension, user.getId());
        } else {
            return new Result().error("上传失败");
        }
    }
    private String getFileExtension(String fileName) {
        int dotIndex = fileName.lastIndexOf(".");
        if (dotIndex > 0) {
            return fileName.substring(dotIndex + 1);
        } else {
            return "";
        }
    }

    /**
     * 合并分片
     * @param fileName
     * @param totalChunks
     * @param fileExtension
     * @param userId
     * @return
     * @throws IOException
     */
    private Result mergeChunks(String fileName, int totalChunks, String fileExtension, Long userId) throws IOException {
        Path mergedFilePath = Paths.get(this.getUPLOAD_URL() + fileName + "." + fileExtension);
        if (!Files.exists(mergedFilePath)) {
            Files.createFile(mergedFilePath);
        }
        for (int i = 0; i < totalChunks; i++) {
            Path chunkFilePath = Paths.get(this.getUPLOAD_URL() + fileName + "_" + i + "." + fileExtension);
            Files.write(mergedFilePath, Files.readAllBytes(chunkFilePath), StandardOpenOption.APPEND);
            Files.delete(chunkFilePath);
        }
        Path filePath = Paths.get(this.getUPLOAD_URL() + fileName);
        File file = new File(filePath.toString());
        // 原文件名称
        String extension = file.getName().substring(0, file.getName().lastIndexOf("."));
        // 后缀
        String suffix = "." + file.getName().substring(file.getName().lastIndexOf(".") + 1);
        // 新的文件名称
        String newFileName = System.currentTimeMillis() + suffix;
        // 文件重命名
        File renamedFile = new File(file.getParent(), newFileName);
        file.renameTo(renamedFile);
        // 文件路径:/gsl/cms/1683616117391.zip
        String resourceUrl = UPLOAD_SUFFIX_URL + newFileName;
        // 可保存到数据库
        Map<String, Object> map = new HashMap<>();
        map.put("url", resourceUrl);
        return new Result().ok(map);
    }

注意:合并分片代码中:
Files.write(mergedFilePath, Files.readAllBytes(chunkFilePath), StandardOpenOption.APPEND);

如果不设置该值StandardOpenOption.APPEND,无法打开合并后的文件
  1. 前端代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>File Upload Test</title>
</head>
<body>
    <input type="file" id="fileInput">
    <button onclick="uploadFile()">Upload</button>

    <script>
        function uploadFile() {
            const file = document.getElementById("fileInput").files[0];
            const url = "/upload/o_upload";
            const chunkSize = 1024 * 1024; // 每个块的大小(字节)
            const fileSize = file.size; // 文件的总大小
            const totalChunks = Math.ceil(fileSize / chunkSize); // 文件被分成的总块数

            let chunkNumber = 0; // 当前上传的块的编号
            let startByte = 0; // 当前上传块的起始字节位置
            let endByte = chunkSize; // 当前上传块的结束字节位置(不包括)

            uploadChunk();

            function uploadChunk() {
                const chunk = file.slice(startByte, endByte); // 获取当前上传块的内容
                const formData = new FormData(); // 创建FormData对象

                // 将参数添加到FormData对象中
                formData.append("file", chunk);
                formData.append("chunkNumber", chunkNumber);
                formData.append("totalChunks", totalChunks);
                formData.append("fileName", file.name);
                formData.append("fileSize", fileSize);

                // 创建XMLHttpRequest对象
                const xhr = new XMLHttpRequest();
                xhr.open("POST", url, true);

                // 监听XMLHttpRequest对象的事件
                xhr.onload = function() {
                    if (xhr.status === 200) {
                        // 上传成功,继续上传下一块
                        chunkNumber++;
                        startByte = endByte;
                        endByte = Math.min(startByte + chunkSize, fileSize);
                        if (startByte < fileSize) {
                            uploadChunk();
                        } else {
                            // 所有块上传成功
                            console.log("Upload completed");
                        }
                    } else {
                        // 上传失败,尝试重传当前块
                        console.error(xhr.statusText);
                        setTimeout(uploadChunk, 1000);
                    }
                };

                xhr.onerror = function() {
                    // 网络错误,尝试重传当前块
                    console.error(xhr.statusText);
                    setTimeout(uploadChunk, 1000);
                };

                // 发送POST请求
                xhr.send(formData);
            }
        }
    </script>
</body>
</html>
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值