最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java大文件上傳(分片上傳+斷點(diǎn)續(xù)傳)的解決方案詳解

 更新時(shí)間:2025年12月16日 09:44:24   作者:悟空碼字  
大文件上傳通常指上傳超過(guò)幾百M(fèi)B甚至幾個(gè)GB的文件,與普通文件上傳相比,大文件上傳會(huì)面臨很多調(diào)整,下面小編就和大家詳細(xì)講講具體的解決方案吧

什么是大文件上傳

大文件上傳通常指上傳超過(guò)幾百M(fèi)B甚至幾個(gè)GB的文件。與普通文件上傳相比,大文件上傳面臨以下挑戰(zhàn):

  • 內(nèi)存限制 - 一次性加載整個(gè)文件到內(nèi)存會(huì)導(dǎo)致內(nèi)存溢出
  • 網(wǎng)絡(luò)穩(wěn)定性 - 上傳過(guò)程中網(wǎng)絡(luò)中斷需要能夠斷點(diǎn)續(xù)傳
  • 超時(shí)問(wèn)題 - 長(zhǎng)時(shí)間上傳可能導(dǎo)致連接超時(shí)
  • 進(jìn)度監(jiān)控 - 需要實(shí)時(shí)顯示上傳進(jìn)度
  • 文件校驗(yàn) - 確保文件完整性和安全性

解決方案:分片上傳

大文件上傳的核心思想是將文件分割成多個(gè)小塊,分別上傳,最后在服務(wù)器端合并。

前端代碼示例 (HTML + JavaScript)

<!DOCTYPE html>
<html>
<head>
    <title>大文件上傳</title>
</head>
<body>
    <input type="file" id="fileInput" />
    <button onclick="uploadFile()">開始上傳</button>
    <div id="progress"></div>

    <script>
        const CHUNK_SIZE = 2 * 1024 * 1024; // 2MB

        async function uploadFile() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            
            if (!file) {
                alert('請(qǐng)選擇文件');
                return;
            }

            const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
            const fileMd5 = await calculateFileMD5(file);
            
            // 檢查文件是否已上傳過(guò)
            const checkResult = await checkFileExists(file.name, fileMd5, file.size);
            
            if (checkResult.uploaded) {
                alert('文件已存在');
                return;
            }

            let uploadedChunks = checkResult.uploadedChunks || [];

            for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
                // 跳過(guò)已上傳的分片
                if (uploadedChunks.includes(chunkIndex)) {
                    updateProgress(chunkIndex + 1, totalChunks);
                    continue;
                }

                const chunk = file.slice(chunkIndex * CHUNK_SIZE, (chunkIndex + 1) * CHUNK_SIZE);
                const formData = new FormData();
                formData.append('file', chunk);
                formData.append('chunkIndex', chunkIndex);
                formData.append('totalChunks', totalChunks);
                formData.append('fileName', file.name);
                formData.append('fileMd5', fileMd5);

                try {
                    await uploadChunk(formData);
                    updateProgress(chunkIndex + 1, totalChunks);
                } catch (error) {
                    console.error(`分片 ${chunkIndex} 上傳失敗:`, error);
                    alert('上傳失敗');
                    return;
                }
            }

            // 所有分片上傳完成,請(qǐng)求合并
            await mergeChunks(file.name, fileMd5, totalChunks);
            alert('上傳完成');
        }

        function uploadChunk(formData) {
            return fetch('/upload/chunk', {
                method: 'POST',
                body: formData
            }).then(response => {
                if (!response.ok) {
                    throw new Error('上傳失敗');
                }
                return response.json();
            });
        }

        function checkFileExists(fileName, fileMd5, fileSize) {
            return fetch(`/upload/check?fileName=${fileName}&fileMd5=${fileMd5}&fileSize=${fileSize}`)
                .then(response => response.json());
        }

        function mergeChunks(fileName, fileMd5, totalChunks) {
            return fetch('/upload/merge', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    fileName: fileName,
                    fileMd5: fileMd5,
                    totalChunks: totalChunks
                })
            }).then(response => response.json());
        }

        function updateProgress(current, total) {
            const progress = document.getElementById('progress');
            const percentage = Math.round((current / total) * 100);
            progress.innerHTML = `上傳進(jìn)度: ${percentage}%`;
        }

        // 計(jì)算文件MD5(簡(jiǎn)化版,實(shí)際應(yīng)使用更可靠的庫(kù))
        async function calculateFileMD5(file) {
            // 這里使用簡(jiǎn)單的文件名+大小模擬MD5
            // 實(shí)際項(xiàng)目中應(yīng)使用 spark-md5 等庫(kù)
            return btoa(file.name + file.size).replace(/[^a-zA-Z0-9]/g, '');
        }
    </script>
</body>
</html>

后端Java代碼示例 (Spring Boot)

配置文件上傳設(shè)置

@Configuration
public class UploadConfig {
    
    @Bean
    public MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        factory.setMaxFileSize("10GB");
        factory.setMaxRequestSize("10GB");
        return factory.createMultipartConfig();
    }
}

文件上傳控制器

@RestController
@RequestMapping("/upload")
public class FileUploadController {
    
    @Value("${file.upload-dir:/tmp/uploads}")
    private String uploadDir;
    
    /**
     * 檢查文件是否存在
     */
    @GetMapping("/check")
    public ResponseEntity<CheckResult> checkFile(
            @RequestParam String fileName,
            @RequestParam String fileMd5,
            @RequestParam Long fileSize) {
        
        String filePath = Paths.get(uploadDir, fileMd5, fileName).toString();
        File file = new File(filePath);
        
        CheckResult result = new CheckResult();
        
        // 如果文件已存在
        if (file.exists() && file.length() == fileSize) {
            result.setUploaded(true);
            return ResponseEntity.ok(result);
        }
        
        // 檢查已上傳的分片
        String chunkDir = getChunkDir(fileMd5);
        File chunkFolder = new File(chunkDir);
        if (!chunkFolder.exists()) {
            result.setUploaded(false);
            result.setUploadedChunks(new ArrayList<>());
            return ResponseEntity.ok(result);
        }
        
        List<Integer> uploadedChunks = Arrays.stream(chunkFolder.listFiles())
                .map(f -> Integer.parseInt(f.getName()))
                .collect(Collectors.toList());
        
        result.setUploaded(false);
        result.setUploadedChunks(uploadedChunks);
        return ResponseEntity.ok(result);
    }
    
    /**
     * 上傳文件分片
     */
    @PostMapping("/chunk")
    public ResponseEntity<UploadResult> uploadChunk(
            @RequestParam("file") MultipartFile file,
            @RequestParam Integer chunkIndex,
            @RequestParam Integer totalChunks,
            @RequestParam String fileName,
            @RequestParam String fileMd5) {
        
        try {
            // 創(chuàng)建分片目錄
            String chunkDir = getChunkDir(fileMd5);
            File chunkFolder = new File(chunkDir);
            if (!chunkFolder.exists()) {
                chunkFolder.mkdirs();
            }
            
            // 保存分片文件
            File chunkFile = new File(chunkDir + File.separator + chunkIndex);
            file.transferTo(chunkFile);
            
            UploadResult result = new UploadResult();
            result.setSuccess(true);
            result.setMessage("分片上傳成功");
            return ResponseEntity.ok(result);
            
        } catch (Exception e) {
            UploadResult result = new UploadResult();
            result.setSuccess(false);
            result.setMessage("分片上傳失敗: " + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }
    
    /**
     * 合并文件分片
     */
    @PostMapping("/merge")
    public ResponseEntity<MergeResult> mergeChunks(@RequestBody MergeRequest request) {
        try {
            String chunkDir = getChunkDir(request.getFileMd5());
            String fileName = request.getFileName();
            String filePath = Paths.get(uploadDir, request.getFileMd5(), fileName).toString();
            
            // 創(chuàng)建目標(biāo)文件
            File targetFile = new File(filePath);
            File parentDir = targetFile.getParentFile();
            if (!parentDir.exists()) {
                parentDir.mkdirs();
            }
            
            // 合并分片
            try (FileOutputStream fos = new FileOutputStream(targetFile)) {
                for (int i = 0; i < request.getTotalChunks(); i++) {
                    File chunkFile = new File(chunkDir + File.separator + i);
                    try (FileInputStream fis = new FileInputStream(chunkFile)) {
                        byte[] buffer = new byte[1024];
                        int len;
                        while ((len = fis.read(buffer)) > 0) {
                            fos.write(buffer, 0, len);
                        }
                    }
                    // 刪除分片文件
                    chunkFile.delete();
                }
            }
            
            // 刪除分片目錄
            new File(chunkDir).delete();
            
            MergeResult result = new MergeResult();
            result.setSuccess(true);
            result.setMessage("文件合并成功");
            result.setFilePath(filePath);
            return ResponseEntity.ok(result);
            
        } catch (Exception e) {
            MergeResult result = new MergeResult();
            result.setSuccess(false);
            result.setMessage("文件合并失敗: " + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
        }
    }
    
    private String getChunkDir(String fileMd5) {
        return Paths.get(uploadDir, "chunks", fileMd5).toString();
    }
}

數(shù)據(jù)傳輸對(duì)象

@Data
public class CheckResult {
    private boolean uploaded;
    private List<Integer> uploadedChunks;
}

@Data
public class UploadResult {
    private boolean success;
    private String message;
}

@Data
public class MergeRequest {
    private String fileName;
    private String fileMd5;
    private Integer totalChunks;
}

@Data
public class MergeResult {
    private boolean success;
    private String message;
    private String filePath;
}

應(yīng)用配置

# application.properties
spring.servlet.multipart.max-file-size=10GB
spring.servlet.multipart.max-request-size=10GB
file.upload-dir=/data/uploads

關(guān)鍵技術(shù)點(diǎn)

  • 分片上傳:將大文件分割成小塊,分別上傳
  • 斷點(diǎn)續(xù)傳:記錄已上傳的分片,網(wǎng)絡(luò)中斷后可以從中斷處繼續(xù)
  • 文件校驗(yàn):通過(guò)MD5驗(yàn)證文件完整性
  • 進(jìn)度監(jiān)控:實(shí)時(shí)顯示上傳進(jìn)度
  • 內(nèi)存優(yōu)化:流式處理,避免內(nèi)存溢出

優(yōu)化建議

  • 增加重試機(jī)制:網(wǎng)絡(luò)異常時(shí)自動(dòng)重試
  • 并行上傳:同時(shí)上傳多個(gè)分片提高速度
  • 壓縮傳輸:對(duì)分片進(jìn)行壓縮減少網(wǎng)絡(luò)傳輸量
  • 安全驗(yàn)證:添加身份驗(yàn)證和文件類型檢查
  • 分布式存儲(chǔ):支持分布式文件系統(tǒng)存儲(chǔ)

這種方案可以有效解決大文件上傳的各種問(wèn)題,提供穩(wěn)定可靠的上傳體驗(yàn)。

到此這篇關(guān)于Java大文件上傳(分片上傳+斷點(diǎn)續(xù)傳)的解決方案詳解的文章就介紹到這了,更多相關(guān)Java大文件上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

汉源县| 米林县| 肥东县| 鞍山市| 阳高县| 曲靖市| 新安县| 普兰店市| 闸北区| 红桥区| 西宁市| 六枝特区| 东明县| 昔阳县| 江达县| 杭州市| 临潭县| 诸城市| 长治县| 嘉义市| 始兴县| 安国市| 天台县| 贡嘎县| 常德市| 盈江县| 依兰县| 安阳县| 杭锦后旗| 福贡县| 和田市| 瑞金市| 班戈县| 沛县| 文昌市| 大化| 佛教| 新巴尔虎右旗| 邵东县| 余庆县| 和林格尔县|