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

Java實(shí)現(xiàn)大文件上傳與斷點(diǎn)續(xù)傳的全過程

 更新時(shí)間:2026年01月21日 09:12:02   作者:Geek攻城貓  
本文介紹了如何使用Java(SpringBoot)實(shí)現(xiàn)高效、可靠的大型文件上傳系統(tǒng),包括分片上傳、斷點(diǎn)續(xù)傳等核心設(shè)計(jì)思想和后端實(shí)現(xiàn)步驟,通過優(yōu)化建議和進(jìn)階方案,進(jìn)一步提升了用戶體驗(yàn)和系統(tǒng)健壯性,需要的朋友可以參考下

在現(xiàn)代 Web 應(yīng)用中,用戶經(jīng)常需要上傳大型文件——如高清視頻、設(shè)計(jì)圖紙或數(shù)據(jù)庫備份。然而,受限于網(wǎng)絡(luò)穩(wěn)定性、服務(wù)器內(nèi)存和瀏覽器限制,直接上傳大文件極易失敗。為此,“分片上傳 + 斷點(diǎn)續(xù)傳”成為業(yè)界標(biāo)準(zhǔn)解決方案。本文將深入講解如何使用 Java(基于 Spring Boot) 實(shí)現(xiàn)高效、可靠的大文件上傳系統(tǒng)。

一、為什么需要斷點(diǎn)續(xù)傳?

  • 網(wǎng)絡(luò)不穩(wěn)定:上傳過程中斷后,無需從頭開始。
  • 節(jié)省帶寬與時(shí)間:僅重傳失敗或未傳的分片。
  • 提升用戶體驗(yàn):支持上傳進(jìn)度顯示、暫停/恢復(fù)。
  • 避免服務(wù)器壓力:避免一次性加載整個(gè)大文件到內(nèi)存。

二、核心設(shè)計(jì)思想

1. 文件分片(Chunking)

將一個(gè)大文件按固定大小(如 2MB)切分為多個(gè)小塊(chunks),每個(gè)塊獨(dú)立上傳。

2. 唯一標(biāo)識

使用文件內(nèi)容的 MD5 值 作為唯一 ID,既可識別文件,又能避免重復(fù)上傳相同內(nèi)容。

3. 分片管理

  • 后端為每個(gè)文件創(chuàng)建臨時(shí)目錄,按序號存儲分片。
  • 提供接口查詢“已上傳分片列表”,實(shí)現(xiàn)斷點(diǎn)檢測。

4. 合并與清理

所有分片收齊后,按順序合并成完整文件,并清理臨時(shí)數(shù)據(jù)。

三、后端實(shí)現(xiàn)(Spring Boot)

1. 項(xiàng)目依賴(Maven)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>

2. 控制器代碼

@RestController
@RequestMapping("/api/upload")
public class ResumableUploadController {

    private static final String TEMP_DIR = "uploads/temp/";
    private static final String FINAL_DIR = "uploads/final/";

    /**
     * 檢查哪些分片已上傳(用于斷點(diǎn)續(xù)傳)
     */
    @GetMapping("/check")
    public Set<Integer> checkUploadedChunks(@RequestParam String fileMd5) {
        Set<Integer> uploaded = new HashSet<>();
        Path tempPath = Paths.get(TEMP_DIR, fileMd5);
        if (Files.exists(tempPath)) {
            try (Stream<Path> stream = Files.list(tempPath)) {
                stream.forEach(p -> {
                    String name = p.getFileName().toString();
                    if (name.matches("\\d+")) {
                        uploaded.add(Integer.parseInt(name));
                    }
                });
            } catch (IOException e) {
                // 日志記錄
            }
        }
        return uploaded;
    }

    /**
     * 上傳單個(gè)分片
     */
    @PostMapping("/chunk")
    public ResponseEntity<?> uploadChunk(
            @RequestParam String fileMd5,
            @RequestParam int chunkIndex,
            @RequestParam MultipartFile chunk) {

        try {
            Path tempDir = Paths.get(TEMP_DIR, fileMd5);
            Files.createDirectories(tempDir);
            Path chunkFile = tempDir.resolve(String.valueOf(chunkIndex));
            chunk.transferTo(chunkFile.toFile());
            return ResponseEntity.ok().build();
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * 合并所有分片為完整文件
     */
    @PostMapping("/merge")
    public ResponseEntity<?> mergeChunks(
            @RequestParam String fileMd5,
            @RequestParam String fileName,
            @RequestParam int totalChunks) {

        try {
            Path finalPath = Paths.get(FINAL_DIR, fileName);
            Files.createDirectories(finalPath.getParent());

            try (OutputStream out = Files.newOutputStream(finalPath)) {
                for (int i = 0; i < totalChunks; i++) {
                    Path chunk = Paths.get(TEMP_DIR, fileMd5, String.valueOf(i));
                    if (!Files.exists(chunk)) {
                        return ResponseEntity.badRequest()
                                .body("Missing chunk: " + i);
                    }
                    byte[] data = Files.readAllBytes(chunk);
                    out.write(data);
                }
            }

            // 清理臨時(shí)分片
            FileUtils.deleteDirectory(new File(TEMP_DIR + fileMd5));

            return ResponseEntity.ok(Map.of("success", true, "path", finalPath.toString()));
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

注意:實(shí)際項(xiàng)目中應(yīng)加入文件名校驗(yàn)、權(quán)限控制、異常日志等。

四、前端配合邏輯(簡要)

前端需完成以下步驟:

  1. 計(jì)算文件 MD5(推薦使用 spark-md5 庫)。
  2. 調(diào)用 /check 接口 獲取已上傳分片索引。
  3. 循環(huán)上傳未完成的分片,每個(gè)請求攜帶:
    • fileMd5
    • chunkIndex
    • 分片 Blob 數(shù)據(jù)
  4. 全部上傳完成后,調(diào)用 /merge 觸發(fā)合并。
// 示例:上傳一個(gè)分片
const uploadChunk = async (fileMd5, index, blob) => {
  const formData = new FormData();
  formData.append('fileMd5', fileMd5);
  formData.append('chunkIndex', index);
  formData.append('chunk', blob);
  await fetch('/api/upload/chunk', { method: 'POST', body: formData });
};

五、關(guān)鍵優(yōu)化建議

優(yōu)化點(diǎn)說明
MD5 計(jì)算前端計(jì)算可避免重復(fù)上傳;若安全要求高,后端也可二次校驗(yàn)。
并發(fā)上傳允許同時(shí)上傳多個(gè)分片(如 3~5 個(gè)),提升速度。
超時(shí)清理定時(shí)任務(wù)刪除 24 小時(shí)未合并的臨時(shí)分片,防止磁盤占滿。
限流與鑒權(quán)防止惡意上傳,限制單用戶上傳頻率和總大小。
進(jìn)度反饋前端根據(jù)已傳分片數(shù)實(shí)時(shí)更新進(jìn)度條。

六、進(jìn)階方案

1. 使用 TUS 協(xié)議

TUS 是一個(gè)開源的 Resumable Upload 標(biāo)準(zhǔn)。Java 社區(qū)有 tus-java-server 實(shí)現(xiàn),開箱即用,支持跨平臺、跨語言。

2. 對接云存儲

阿里云 OSS、騰訊云 COS、AWS S3 等均提供 分片上傳 API??蓪⒎制苯由蟼髦猎拼鎯?,后端僅負(fù)責(zé)協(xié)調(diào),大幅降低服務(wù)器負(fù)載。

七、總結(jié)

通過“分片上傳 + 斷點(diǎn)續(xù)傳”,我們不僅能可靠地處理 GB 級文件,還能顯著提升用戶體驗(yàn)和系統(tǒng)健壯性。雖然實(shí)現(xiàn)細(xì)節(jié)較多,但核心邏輯清晰:切分 → 上傳 → 校驗(yàn) → 合并。

在實(shí)際項(xiàng)目中,建議結(jié)合業(yè)務(wù)需求選擇自研或集成成熟方案。對于高并發(fā)、高可靠場景,優(yōu)先考慮云廠商的分片上傳能力;對于內(nèi)部系統(tǒng)或定制化需求,本文提供的 Java 實(shí)現(xiàn)可作為堅(jiān)實(shí)基礎(chǔ)。

以上就是Java實(shí)現(xiàn)大文件上傳與斷點(diǎn)續(xù)傳的全過程的詳細(xì)內(nèi)容,更多關(guān)于Java大文件上傳與斷點(diǎn)續(xù)傳的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java編程中應(yīng)用的GUI設(shè)計(jì)基礎(chǔ)

    Java編程中應(yīng)用的GUI設(shè)計(jì)基礎(chǔ)

    這篇文章主要介紹了Java編程中應(yīng)用的GUI設(shè)計(jì)基礎(chǔ),為一些Java開發(fā)CS類型應(yīng)用的基礎(chǔ)概念知識,需要的朋友可以參考下
    2015-10-10
  • IDEA .iml文件及.idea文件夾超詳細(xì)講解

    IDEA .iml文件及.idea文件夾超詳細(xì)講解

    iml文件是IntelliJ IDEA自己創(chuàng)建的模塊文件,用于java應(yīng)用開發(fā),存儲一些模塊相關(guān)的信息,比如一個(gè)Java組建,插件組建,Maven組建等,這篇文章主要介紹了IDEA .iml文件及.idea文件夾,需要的朋友可以參考下
    2023-09-09
  • SpringBoot如何讀取mock數(shù)據(jù)(高效調(diào)試接口)

    SpringBoot如何讀取mock數(shù)據(jù)(高效調(diào)試接口)

    本文介紹如何在SpringBoot項(xiàng)目中讀取resources目錄下的mock數(shù)據(jù)文件,以便高效調(diào)試接口,在 Spring Boot 項(xiàng)目中,通常會將靜態(tài)資源或配置文件放在 src/main/resources 目錄下,下面通過實(shí)例給大家詳細(xì)介紹,感興趣的朋友一起看看吧
    2024-12-12
  • 通過JWT來解決登錄認(rèn)證問題的方案

    通過JWT來解決登錄認(rèn)證問題的方案

    Json web token (JWT),是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn)((RFC7519),該token被設(shè)計(jì)為緊湊且安全的,特別適用于分布式站點(diǎn)的單點(diǎn)登錄(SSO)場景,本文給大家介紹了如何通過 JWT 來解決登錄認(rèn)證問題,需要的朋友可以參考下
    2024-12-12
  • java創(chuàng)建xls文件放到指定文件夾中實(shí)現(xiàn)方式

    java創(chuàng)建xls文件放到指定文件夾中實(shí)現(xiàn)方式

    本文介紹了如何在Java中使用ApachePOI庫創(chuàng)建和操作Excel文件,重點(diǎn)是如何創(chuàng)建一個(gè)XLS文件并將其放置到指定文件夾中
    2025-11-11
  • springboot集成RocketMQ過程及使用示例詳解

    springboot集成RocketMQ過程及使用示例詳解

    這篇文章主要為大家介紹了springboot集成RocketMQ過程及使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • java中CopyOnWriteArrayList源碼解析

    java中CopyOnWriteArrayList源碼解析

    為了將讀取的性能發(fā)揮到極致,jdk中提供了CopyOnWriteArrayList類,下面這篇文章主要給大家介紹了關(guān)于java中CopyOnWriteArrayList源碼解析的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • java_時(shí)間戳與Date_相互轉(zhuǎn)化的實(shí)現(xiàn)代碼

    java_時(shí)間戳與Date_相互轉(zhuǎn)化的實(shí)現(xiàn)代碼

    本篇文章是對java_時(shí)間戳與Date_相互轉(zhuǎn)化的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下如下。
    2016-11-11
  • java 中InputStream,String,File之間的相互轉(zhuǎn)化對比

    java 中InputStream,String,File之間的相互轉(zhuǎn)化對比

    這篇文章主要介紹了java 中InputStream,String,File之間的相互轉(zhuǎn)化對比的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • java解析背包問題實(shí)例代碼(簡單易懂)

    java解析背包問題實(shí)例代碼(簡單易懂)

    背包問題是計(jì)算機(jī)科學(xué)中的經(jīng)典問題,涉及到在給定的一組物品中選擇最佳的物品組合,以使其總重量不超過背包所能承載的限制,同時(shí)獲得最大的總價(jià)值,這篇文章主要介紹了java解析背包問題的相關(guān)資料,需要的朋友可以參考下
    2025-10-10

最新評論

榕江县| 六枝特区| 兖州市| 南阳市| 卢龙县| 姜堰市| 株洲县| 丹江口市| 台州市| 乐平市| 班戈县| 马尔康县| 专栏| 库伦旗| 赤城县| 东平县| 淳安县| 旅游| 靖江市| 崇明县| 定襄县| 贵定县| 麻江县| 永胜县| 赣州市| 海淀区| 孟连| 德惠市| 临洮县| 凤城市| 东阳市| 米林县| 海盐县| 湘潭市| 清水县| 龙口市| 河西区| 宁陵县| 安义县| 石景山区| 渑池县|