Go語言實現(xiàn)大文件分片上傳與斷點續(xù)傳功能
在現(xiàn)代 Web 應(yīng)用中,大文件上傳是一個常見需求。直接上傳大文件會遇到網(wǎng)絡(luò)中斷、上傳超時、失敗后無法續(xù)傳等問題。為了解決這些痛點,通常采用 分片上傳 與 斷點續(xù)傳 的方案。
本文將帶你使用 Go 實現(xiàn)一個簡易的大文件分片上傳與斷點續(xù)傳服務(wù)。
核心思路
1.分片上傳
將大文件切分成多個小塊(chunk),逐塊上傳,避免單次上傳過大失敗。
2.斷點續(xù)傳
每個文件有唯一的標識(如 MD5、UUID),服務(wù)端記錄已上傳的分片信息,上傳中斷后可繼續(xù)上傳剩余分片。
.3合并分片
當所有分片上傳完成后,服務(wù)端將這些分片按順序合并成完整文件。
服務(wù)端實現(xiàn)
上傳接口設(shè)計
- 初始化上傳:生成 uploadId,返回客戶端。
- 上傳分片:
/upload/chunk?uploadId=xxx&chunkIndex=1 - 合并分片:所有分片傳完后,調(diào)用
/upload/merge合并。
Go 代碼示例
package main
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strconv"
)
// 臨時存放分片目錄
const uploadDir = "./uploads"
func main() {
// 創(chuàng)建目錄
os.MkdirAll(uploadDir, os.ModePerm)
http.HandleFunc("/upload/chunk", uploadChunkHandler)
http.HandleFunc("/upload/merge", mergeChunksHandler)
fmt.Println("服務(wù)已啟動: http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
// 上傳分片
func uploadChunkHandler(w http.ResponseWriter, r *http.Request) {
uploadId := r.URL.Query().Get("uploadId")
chunkIndex := r.URL.Query().Get("chunkIndex")
if uploadId == "" || chunkIndex == "" {
http.Error(w, "缺少參數(shù)", http.StatusBadRequest)
return
}
// 保存分片目錄
chunkDir := filepath.Join(uploadDir, uploadId)
os.MkdirAll(chunkDir, os.ModePerm)
// 分片文件路徑
chunkPath := filepath.Join(chunkDir, chunkIndex)
// 寫入文件
file, err := os.Create(chunkPath)
if err != nil {
http.Error(w, "無法保存分片", http.StatusInternalServerError)
return
}
defer file.Close()
_, err = io.Copy(file, r.Body)
if err != nil {
http.Error(w, "寫入分片失敗", http.StatusInternalServerError)
return
}
w.Write([]byte("分片上傳成功: " + chunkIndex))
}
// 合并分片
func mergeChunksHandler(w http.ResponseWriter, r *http.Request) {
uploadId := r.URL.Query().Get("uploadId")
totalChunks := r.URL.Query().Get("totalChunks")
if uploadId == "" || totalChunks == "" {
http.Error(w, "缺少參數(shù)", http.StatusBadRequest)
return
}
chunkDir := filepath.Join(uploadDir, uploadId)
mergedFile := filepath.Join(uploadDir, uploadId+"_merged")
// 創(chuàng)建目標文件
dst, err := os.Create(mergedFile)
if err != nil {
http.Error(w, "無法創(chuàng)建合并文件", http.StatusInternalServerError)
return
}
defer dst.Close()
// 合并分片
chunkCount, _ := strconv.Atoi(totalChunks)
for i := 1; i <= chunkCount; i++ {
chunkPath := filepath.Join(chunkDir, strconv.Itoa(i))
src, err := os.Open(chunkPath)
if err != nil {
http.Error(w, fmt.Sprintf("缺少分片 %d", i), http.StatusBadRequest)
return
}
_, err = io.Copy(dst, src)
src.Close()
if err != nil {
http.Error(w, "合并失敗", http.StatusInternalServerError)
return
}
}
w.Write([]byte("文件合并完成: " + mergedFile))
}
客戶端實現(xiàn)思路
前端/客戶端需要
- 將文件分割為多個 固定大小的 chunk。
- 順序調(diào)用 /upload/chunk 上傳分片。
- 上傳完成后調(diào)用 /upload/merge 合并文件。
- 如果上傳中斷,查詢已上傳分片,從斷點繼續(xù)。
示例偽代碼:
async function uploadFile(file) {
const chunkSize = 5 * 1024 * 1024; // 5MB
const totalChunks = Math.ceil(file.size / chunkSize);
const uploadId = generateUploadId(file); // 可用 MD5/UUID
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);
let formData = new FormData();
formData.append("file", chunk);
await fetch(`/upload/chunk?uploadId=${uploadId}&chunkIndex=${i+1}`, {
method: "POST",
body: chunk
});
}
await fetch(`/upload/merge?uploadId=${uploadId}&totalChunks=${totalChunks}`);
}
優(yōu)化點
分片校驗:上傳前計算 MD5,避免重復(fù)上傳。
并行上傳:可同時并發(fā)上傳多個分片,提升速度。
斷點續(xù)傳:服務(wù)端保存已上傳分片信息,客戶端查詢后跳過已完成分片。
秒傳功能:通過文件 Hash 判斷文件是否已存在,直接返回完成。
總結(jié)
通過 Go 實現(xiàn)大文件分片上傳與斷點續(xù)傳,可以有效提升上傳成功率和用戶體驗。實際項目中,還需要結(jié)合 數(shù)據(jù)庫記錄、鑒權(quán)、并發(fā)控制、文件存儲(如 OSS、S3、MinIO) 等功能,構(gòu)建更健壯的文件上傳系統(tǒng)。
到此這篇關(guān)于Go語言實現(xiàn)大文件分片上傳與斷點續(xù)傳功能的文章就介紹到這了,更多相關(guān)Go大文件上傳內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang實現(xiàn)讀取excel文件并轉(zhuǎn)換為JSON格式
本文介紹了如何使用Golang讀取Excel文件并將其轉(zhuǎn)換為JSON格式,通過安裝excelize依賴和創(chuàng)建readExcelToJSON方法,可以實現(xiàn)這一功能,如果需要轉(zhuǎn)換數(shù)據(jù)類型,可以修改相應(yīng)的代碼,需要的朋友可以參考下2025-03-03
淺析Golang如何向已關(guān)閉的chan讀寫數(shù)據(jù)
這篇文章主要為大家詳細介紹了Golang如何向已關(guān)閉的chan讀寫數(shù)據(jù),文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02

