国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

SpringBoot 實戰:文件上傳之秒傳、斷點續傳、分片上傳

開發 前端
在日常開發中,文件上傳的場景多種多樣。比如,在線教育平臺上的視頻資源上傳,社交平臺上的圖片分享,以及企業內部的知識文檔管理等。

文件上傳功能幾乎是每個 Web 應用不可或缺的一部分。無論是個人博客中的圖片上傳,還是企業級應用中的文檔管理,文件上傳都扮演著至關重要的角色。今天,松哥和大家來聊聊文件上傳中的幾個高級玩法——秒傳、斷點續傳和分片上傳。

一 文件上傳的常見場景

在日常開發中,文件上傳的場景多種多樣。比如,在線教育平臺上的視頻資源上傳,社交平臺上的圖片分享,以及企業內部的知識文檔管理等。這些場景對文件上傳的要求也各不相同,有的追求速度,有的注重穩定性,還有的需要考慮文件大小和安全性。因此,針對不同需求,我們有了秒傳、斷點續傳和分片上傳等解決方案。

二 秒傳、斷點上傳與分片上傳

秒傳

秒傳,顧名思義,就是幾乎瞬間完成文件上傳的過程。其實現原理是通過計算文件的哈希值(如 MD5 或 SHA-1),然后將這個唯一的標識符發送給服務器。如果服務器上已經存在相同的文件,則直接返回成功信息,避免了重復上傳。這種方式不僅節省了帶寬,也大大提高了用戶體驗。

斷點續傳

斷點續傳是指在網絡不穩定或者用戶主動中斷上傳后,能夠從上次中斷的地方繼續上傳,而不需要重新開始整個過程。這對于大文件上傳尤為重要,因為它可以有效防止因網絡問題導致的上傳失敗,同時也能節約用戶的流量和時間。

分片上傳

分片上傳則是將一個大文件分割成多個小塊分別上傳,最后再由服務器合并成完整的文件。這種做法的好處是可以并行處理多個小文件,提高上傳效率;同時,如果某一部分上傳失敗,只需要重傳這一部分,不影響其他部分。

三 秒傳實戰

后端實現

在 SpringBoot 項目中,我們可以使用 MessageDigest 類來計算文件的 MD5 值,然后檢查數據庫中是否存在該文件。

@RestController
@RequestMapping("/file")
public class FileController {
    @Autowired
    FileService fileService;

    @PostMapping("/upload1")
    public ResponseEntity<String> secondUpload(@RequestParam(value = "file",required = false) MultipartFile file,@RequestParam(required = false,value = "md5") String md5) {
        try {
            // 檢查數據庫中是否已存在該文件
            if (fileService.existsByMd5(md5)) {
                return ResponseEntity.ok("文件已存在");
            }
            // 保存文件到服務器
            file.transferTo(new File("/path/to/save/" + file.getOriginalFilename()));
            // 保存文件信息到數據庫
            fileService.save(new FileInfo(file.getOriginalFilename(), DigestUtils.md5DigestAsHex(file.getInputStream())));
            return ResponseEntity.ok("上傳成功");
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上傳失敗");
        }
    }
}

前端調用

前端可以通過 JavaScript 的 FileReader API 讀取文件內容,通過 spark-md5 計算 MD5 值,然后發送給后端進行校驗。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>秒傳</title>
    <script src="spark-md5.js"></script>
</head>
<body>
<input type="file" id="fileInput" />
<button onclick="startUpload()">開始上傳</button>
<hr>
<script>
    async function startUpload() {
        const fileInput = document.getElementById('fileInput');
        const file = fileInput.files[0];
        if (!file) {
            alert("請選擇文件");
            return;
        }

        const md5 = await calculateMd5(file);
        const formData = new FormData();
        formData.append('md5', md5);

        const response = await fetch('/file/upload1', {
            method: 'POST',
            body: formData
        });

        const result = await response.text();
        if (response.ok) {
            if (result != "文件已存在") {
                // 開始上傳文件
            }
        } else {
            console.error("上傳失敗: " + result);
        }
    }

    function calculateMd5(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onloadend = () => {
                const spark = new SparkMD5.ArrayBuffer();
                spark.append(reader.result);
                resolve(spark.end());
            };
            reader.onerror = () => reject(reader.error);
            reader.readAsArrayBuffer(file);
        });
    }
</script>
</body>
</html>

前端分為兩個步驟:

  1. 計算文件的 MD5 值,計算之后發送給服務端確定文件是否存在。
  2. 如果文件已經存在,則不需要繼續上傳文件;如果文件不存在,則開始上傳文件,上傳文件和 MD5 校驗請求類似,上面的案例代碼中我就沒有重復演示了,松哥在書里和之前的課程里都多次講過文件上傳,這里不再啰嗦。

四 分片上傳實戰

分片上傳關鍵是在前端對文件切片,比如一個 10MB 的文件切為 10 份,每份 1MB。每次上傳的時候,需要多一個參數記錄當前上傳的文件切片的起始位置。

比如一個 10MB 的文件,切為 10 份,每份 1MB,那么:

  • 第 0 片,從 0 開始,一共是 1024*1024 個字節。
  • 第 1 片,從 1024*1024 開始,一共是 1024*1024 個字節。
  • 第 2 片...

把這個搞懂,后面的代碼就好理解了。

后端實現

private static final String UPLOAD_DIR = System.getProperty("user.home") + "/uploads/";
/**
 * 上傳文件到指定位置
 *
 * @param file 上傳的文件
 * @param start 文件開始上傳的位置
 * @return ResponseEntity<String> 上傳結果
 */
@PostMapping("/upload2")
public ResponseEntity<String> resumeUpload(@RequestParam("file") MultipartFile file, @RequestParam("start") long start,@RequestParam("fileName") String fileName) {
    try {
        File directory = new File(UPLOAD_DIR);
        if (!directory.exists()) {
            directory.mkdirs();
        }
        File targetFile = new File(UPLOAD_DIR + fileName);
        RandomAccessFile randomAccessFile = new RandomAccessFile(targetFile, "rw");
        FileChannel channel = randomAccessFile.getChannel();
        channel.position(start);
        channel.transferFrom(file.getResource().readableChannel(), start, file.getSize());
        channel.close();
        randomAccessFile.close();
        return ResponseEntity.ok("上傳成功");
    } catch (Exception e) {
        System.out.println("上傳失敗: "+e.getMessage());
        return ResponseEntity.status(500).body("上傳失敗");
    }
}

后端每次處理的時候,需要先設置文件的起始位置。

前端調用

前端需要將文件切分成多個小塊,然后依次上傳。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>分片示例</title>
</head>
<body>
    <input type="file" id="fileInput" />
    <button onclick="startUpload()">開始上傳</button>

    <script>
        async function startUpload() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            if (!file) {
                alert("請選擇文件");
                return;
            }

            const filename = file.name;
            let start = 0;

            uploadFile(file, start);
        }

        async function uploadFile(file, start) {
            const chunkSize = 1024 * 1024; // 每個分片1MB
            const total = Math.ceil(file.size / chunkSize);

            for (let i = 0; i < total; i++) {
                const chunkStart = start + i * chunkSize;
                const chunkEnd = Math.min(chunkStart + chunkSize, file.size);
                const chunk = file.slice(chunkStart, chunkEnd);

                const formData = new FormData();
                formData.append('file', chunk);
                formData.append('start', chunkStart);
                formData.append('fileName', file.name);

                const response = await fetch('/file/upload2', {
                    method: 'POST',
                    body: formData
                });

                const result = await response.text();
                if (response.ok) {
                    console.log(`分片 ${i + 1}/${total} 上傳成功`);
                } else {
                    console.error(`分片 ${i + 1}/${total} 上傳失敗: ${result}`);
                    break;
                }
            }
        }
    </script>
</body>
</html>

五 斷點續傳實戰

斷點續傳的技術原理類似于分片上傳。

當文件已經上傳了一部分之后,斷了需要重新開始上傳。

那么我們的思路是這樣的:

  1. 前端先發送一個請求,檢查要上傳的文件在服務端是否已經存在,如果存在,目前大小是多少。
  2. 前端根據已經存在的大小,繼續上傳文件即可。

后端案例

先來看后端檢查的接口,如下:

@GetMapping("/check")
public ResponseEntity<Long> checkFile(@RequestParam("filename") String filename) {
    File file = new File(UPLOAD_DIR + filename);
    if (file.exists()) {
        return ResponseEntity.ok(file.length());
    } else {
        return ResponseEntity.ok(0L);
    }
}

如果文件存在,則返回已經存在的文件大小。

如果文件不存在,則返回 0,表示前端從頭開始上傳該文件。

前端調用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>斷點續傳示例</title>
</head>
<body>
<input type="file" id="fileInput"/>
<button onclick="startUpload()">開始上傳</button>

<script>
    async function startUpload() {
        const fileInput = document.getElementById('fileInput');
        const file = fileInput.files[0];
        if (!file) {
            alert("請選擇文件");
            return;
        }

        const filename = file.name;
        let start = await checkFile(filename);

        uploadFile(file, start);
    }

    async function checkFile(filename) {
        const response = await fetch(`/file/check?filename=${filename}`);
        const start = await response.json();
        return start;
    }

    async function uploadFile(file, start) {
        const chunkSize = 1024 * 1024; // 每個分片1MB
        const total = Math.ceil((file.size - start) / chunkSize);

        for (let i = 0; i < total; i++) {
            const chunkStart = start + i * chunkSize;
            const chunkEnd = Math.min(chunkStart + chunkSize, file.size);
            const chunk = file.slice(chunkStart, chunkEnd);

            const formData = new FormData();
            formData.append('file', chunk);
            formData.append('start', chunkStart);
            formData.append('fileName', file.name);

            const response = await fetch('/file/upload2', {
                method: 'POST',
                body: formData
            });

            const result = await response.text();
            if (response.ok) {
                console.log(`分片 ${i + 1}/${total} 上傳成功`);
            } else {
                console.error(`分片 ${i + 1}/${total} 上傳失敗: ${result}`);
                break;
            }
        }
    }
</script>
</body>
</html>

這個案例實際上是一個斷點續傳+分片上傳的案例,相關知識點并不難,小伙伴們可以自行體會下。

六 總結

好了,以上就是關于文件上傳中秒傳、斷點續傳和分片上傳的實戰分享。通過這些技術的應用,我們可以極大地提升文件上傳的效率和穩定性,改善用戶體驗。希望各位小伙伴在自己的項目中也能靈活運用這些技巧,解決實際問題。


責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2021-01-15 11:40:44

文件Java秒傳

2022-06-15 09:01:45

大文件秒傳分片上傳

2025-10-29 04:15:00

OSS分片CDN

2025-07-03 07:41:34

2017-08-08 08:45:44

前端文件斷點續傳

2011-03-04 16:41:57

FileZilla

2023-06-20 19:57:13

2020-04-02 20:07:17

前端vuenote.js

2023-03-09 12:04:38

Spring文件校驗

2021-01-18 05:19:11

數字指紋

2022-08-05 08:40:37

架構

2013-07-22 14:02:17

iOS開發ASIHTTPRequ

2024-07-02 10:18:18

2009-08-28 15:38:49

C#實現斷點續傳

2025-07-02 00:00:00

2025-06-27 02:32:00

2025-06-17 08:39:43

2017-12-20 15:11:48

iOS緩存文件斷點機制

2010-04-07 11:09:53

2011-03-01 14:12:12

FreebsdProftpd
點贊
收藏

51CTO技術棧公眾號

国产欧美日韩免费观看| 久久亚洲一级片| 国产成人激情视频| 不卡精品视频| 亚洲成人精品久久| 青青草免费观看免费视频在线| 国产精品视频一二| 欧美精品第三页| 国产精品正在播放| 在线观看污视频| 国内一区二区视频| 免费日韩在线观看| 国产在线视频不卡二| 在线视频不卡一区二区三区| 日本vs亚洲vs韩国一区三区 | 亚洲免费视频一区二区三区| 亚洲国产精品自拍| 久色视频在线播放| 91免费视频大全| 美女一区二区三区视频| 国产女同互慰高潮91漫画| 农村妇女精品一二区| 国产欧美久久久精品影院| 乌克兰美女av| 一区二区成人在线| 亚洲人性生活视频| 欧美在线色视频| 黄色免费在线网站| 亚洲精品美女网站| 一区在线不卡| 欧美在线观看一区二区三区| 精品国产一区二区三区香蕉沈先生| 国产成人一区二区| 韩国av一区| 一区二区三区四区视频在线| 成人99免费视频| 国产精美视频| 精品视频一区二区三区免费| 91九色在线看| 欧美日韩国产999| 久久看人人摘| 成人手机视频在线| 国产精品看片你懂得| 免费在线毛片| 亚洲图中文字幕| 美女毛片一区二区三区四区| 不卡一区二区三区视频| 国产资源精品在线观看| 国产一区二区视频免费在线观看 | 久久精品香蕉视频| 亚洲福利一区二区| 精品一性一色一乱农村| 欧美高跟鞋交xxxxhd| 欧美一级精品| 欧美午夜欧美| 国产欧美日韩卡一| 青青青青在线| 久久国产加勒比精品无码| 北条麻妃国产九九九精品小说| 欧美一区二视频在线免费观看| 91女神在线视频| 粉嫩av一区| 久久精品影视伊人网| 欧美国产三区| 毛片av在线播放| 亚洲成av人片| 樱花草涩涩www在线播放| 97精品国产97久久久久久免费 | 成人免费高清| 欧美精品九九久久| 国产精品嫩草99av在线| 久久久精品三级| 欧美美女一区二区| 日韩精品视频中文字幕| 国产免费一区| 中文字幕亚洲在| 91九色在线播放| 成人两性免费视频| 99久久久国产精品免费蜜臀| 超碰免费在线| 国模精品系列视频| 精品亚洲国产成人av制服丝袜| 黄色三级电影网站| 国产亚洲精品激情久久| 精品福利av| 免费网站www在线观看| 亚洲一区999| 欧美专区18| 爽爽视频在线观看| 98精品国产高清在线xxxx天堂| 黑人巨大精品欧美黑白配亚洲| 日韩a级作爱片一二三区免费观看| 超碰91人人草人人干| 蜜臀av一区二区在线免费观看| 污视频在线观看免费| 欧美激情久久久久| 国产一区二区导航在线播放| 免费在线毛片网站| 国产在线拍揄自揄视频不卡99| 国产日韩欧美制服另类| 性孕妇free特大另类| 国产精华一区| 亚洲国产精品天堂| 国产精品sss在线观看av| 久久成人福利视频| 欧美精品一区二区不卡| 欧美日韩一区二区高清| 一个人看的免费视频色| 欧美精品videosex牲欧美| 国产精品99精品久久免费| 伊人电影在线观看| 国产一区二区免费电影| 午夜电影久久久| 国产精品免费大片| 黄色国产网站在线观看| 欧美激情亚洲一区| 92精品国产成人观看免费| 肉色欧美久久久久久久免费看| 亚洲国产激情一区二区三区| 这里只有精品电影| 一本一本久久| 午夜在线观看视频| 97影院在线午夜| 一本色道综合亚洲| 一区二区中文字| 激情小视频在线| 超碰97网站| 欧美性一区二区| 日韩网站在线| 久久精品视频免费看| 久久99久久精品国产| 91精品国产综合久久蜜臀| 日韩国产欧美在线视频| 日本在线影院| 777精品久无码人妻蜜桃| 色婷婷综合久久久久中文字幕1| 成人一级片网址| 久久av影院| 黄色a级片免费| 91wwwcom在线观看| 亚洲成av人片| 在线日韩欧美| 蜜臀久久精品| 国产无套内射久久久国产| 孩xxxx性bbbb欧美| 亚洲精选在线视频| 欧美精品大片| 丰满大乳少妇在线观看网站| 欧美黄色免费网址| 国外色69视频在线观看| 五月天中文字幕一区二区| 伊人久久综合| 欧美成人ⅴideosxxxxx| 动漫av免费观看| 国产精品自产拍在线观| 欧美老女人在线| 国产精品一区二区黑丝| www国产精品| 三级av在线| 一本二本三本亚洲码| 欧美激情女人20p| 精品欧美国产一区二区三区| 亚欧美中日韩视频| 国产va免费精品观看精品| 久草在线资源视频在线观看| 免费成人深夜夜行视频| 久久综合亚洲社区| 大伊人狠狠躁夜夜躁av一区| 人人精品人人爱| 在线观看精品| 黄页网址在线观看| 青青草原成人| 九色精品美女在线| 在线欧美日韩精品| 福利一区二区在线| 郴州新闻综合频道在线直播| 美女精品导航| 在线观看免费播放网址成人| 精品国产一区二区三区久久久久久| 一本色道久久88综合亚洲精品ⅰ | 日韩你懂的在线观看| aaa国产一区| 亚洲精品中文字幕乱码| 国产精品av在线| 精品av在线播放| 国产精品综合久久| 91蜜臀精品国产自偷在线| 性欧美gay| 国产高清视频在线播放| 成人免费观看毛片| 精品一区日韩成人| 久久久久久综合网天天| 日韩欧美国产高清| 国产亚洲一区二区三区在线观看 | 欧美在线观看网址综合| 精品久久人人做人人爰| 亚洲精品综合在线| 国产精品一品二品| 欧美精品一线| 精品国产乱子伦一区二区|