yudao 文件分片上传

FileController

    @GetMapping("/file-url")
    @Operation(summary = "获取文件地址")
    @ApiResponse(description = "0文件不存在")
    @OperateLog(enable = false)
    public CommonResult<String> getFileUrl(String identifier) {
        String url0 = fileService.getFirstUrlByName(identifier);
        if (url0 != null) {
            return success(url0);
        }
        return success("0");
    }

    @PostMapping("/chunks-upload")
    @Operation(summary = "上传文件", description = "模式二:分片上传文件")
    @ApiResponse(description = "0:上传失败,1:上传成功")
    @OperateLog(enable = false)
    public CommonResult<String> uploadChunks(FileChunkUploadReqVO chunkUploadReqVO) throws Exception {
        MultipartFile file = chunkUploadReqVO.getFile();
        String identifier = chunkUploadReqVO.getIdentifier();
        Long number = chunkUploadReqVO.getNumber();
        Long total = chunkUploadReqVO.getTotal();

        String filename = StrUtil.format("{}_{}", total, number);
        String filepath = getSrcDir(identifier) + "/" + filename;
        if (!FileUtil.exist(filepath)) {
            // 1、创建分片文件
            File chunking = FileUtil.touch(filepath + ".chunking");
            FileUtil.writeBytes(file.getBytes(), chunking);
            // 2、上传成功后删除后缀
            FileUtil.rename(chunking, filename, true);
        }
        return success("1");
    }

    @GetMapping("/chunks-merge")
    @Operation(summary = "合并文件", description = "模式二:分片上传文件")
    @OperateLog
    public CommonResult<String> mergeChunks(String identifier, String path) throws Exception {
        // 1、创建文件
        String url = fileService.createFile(identifier, path,
                new byte[0]);
        File tarFile = fileService.getLocalFile(url);
        if (tarFile == null) {
            throw new ServiceException("文件不存在,暂只支持本地分片文件的合并");
        }
        // 2、合并至文件
        String srcDir = getSrcDir(identifier);
        mergeChunks(srcDir, tarFile);
        // 3、删除临时目录
        FileUtil.del(srcDir);
        return success(url);
    }

    private String getSrcDir(String identifier) {
        return "~/temp/" + identifier;
    }

    private void mergeChunks(String srcDir, File tarFile) {
        if (FileUtil.exist(srcDir)) {
            File[] files = FileUtil.ls(srcDir);
            Arrays.sort(files, (o1, o2) -> {
                Integer n1 = Integer.valueOf(o1.getName().replace("_", ""));
                Integer n2 = Integer.valueOf(o2.getName().replace("_", ""));
                return n1 - n2;
            });
            // 合并
            for (File file : files) {
                if (file.getName().endsWith(".chunking")) {
                    continue;
                }
                byte[] content = FileUtil.readBytes(file);
                FileUtil.writeBytes(content, tarFile, 0, content.length, true);
            }
        }
    }

FileService

    /**
     * 通过url获取文件的实际存储对象
     * @param url 文件url
     * @return 文件绝对路径
     */
    File getLocalFile(String url);

    /**
     * 获取url,该方法的前提是使用name存储md5或其他算法生成的文件唯一标识,从而保证打文件秒传
     * @param name 文件name
     * @return 文件url
     */
    String getFirstUrlByName(String name);

tstup.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chunked File Upload</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
    <style>
        #progressContainer {
            width: 100%;
            background-color: #f3f3f3;
        }

        #progressBar {
            width: 0%;
            height: 30px;
            background-color: #4caf50;
        }
    </style>
</head>
<body>
<h1>Chunked File Upload</h1>
<input type="file" id="fileInput"/>
<button onclick="uploadFile()">Upload</button>
<div id="progressContainer">
    <div id="progressBar"></div>
</div>

<script>
    const chunkSize = 2 * 1024 * 1024; // 2MB per chunk
    let fileInput = document.getElementById('fileInput');

    async function uploadFile() {
        const file = fileInput.files[0];
        if (!file) {
            alert('Please select a file');
            return;
        }

        const totalChunks = Math.ceil(file.size / chunkSize);
        const identifier = generateIdentifier(file);
        const path = file.name; // adjust as needed

        for (let i = 0; i < totalChunks; i++) {
            const start = i * chunkSize;
            const end = Math.min(file.size, start + chunkSize);
            const chunk = file.slice(start, end);
            const formData = new FormData();
            formData.append('file', chunk);
            formData.append('path', path);
            formData.append('identifier', identifier);
            formData.append('number', i + 1);
            formData.append('total', totalChunks);

            try {
                const response = await fetch('http://localhost:20611/admin-api/infra/file/chunks-upload', {
                    method: 'POST',
                    body: formData,
                    headers: {
                        'Authorization': 'Bearer 6ec7f94e414943d893fa5a63d4ad1bad'
                    }
                });
                const result = await response.json();
                if (result.data !== '1' && result.data !== '0') i = totalChunks;

                console.log('Chunk upload response:', result);

                // Update the progress bar
                updateProgressBar((i + 1) / totalChunks * 100);
            } catch (error) {
                console.error('Error uploading chunk:', error);
            }
        }

        // Merge chunks after all are uploaded
        try {
            const response = await fetch(`http://localhost:20611/admin-api/infra/file/chunks-merge?identifier=${identifier}&path=${path}`, {
                method: 'GET',
                headers: {
                    'Authorization': 'Bearer 6ec7f94e414943d893fa5a63d4ad1bad'
                }
            });
            const result = await response.json();
            console.log('Merge response:', result);
            alert('File uploaded and merged successfully');
        } catch (error) {
            console.error('Error merging chunks:', error);
        }
    }

    function updateProgressBar(percentage) {
        const progressBar = document.getElementById('progressBar');
        progressBar.style.width = percentage + '%';
    }

    function generateIdentifier(file) {
        return CryptoJS.MD5(CryptoJS.enc.Latin1.parse(file)).toString();
    }
</script>
</body>
</html>

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值