使用Golang高效分块上传大文件到S3
使用Golang高效分块上传大文件到S3
func main() {
fmt.Println("hello world")
}
各位Golang专家好。
这个案例需要将大文件(1GB < 文件大小 < 5GB)进行分块处理,计算zsync文件(如果不熟悉的话,可以理解为类似签名文件),然后将分块文件和zsync文件上传到Amazon S3。
我初步提出了这样的设计方案。假设文件大小为3GB,我决定每个分块1GB:
fileA_5gb.bin
|-- fileA_chunk1 (缓冲区1)
|-- fileA_chunk2 (缓冲区2)
|-- fileA_chunk3 (缓冲区3)
完成分块后,我计算签名文件并将每个分块及其签名上传到S3。
由于我不想在内存中占用1GB的字节空间,我想可以使用bufio来实现?虽然我还没有深入考虑其后果或复杂性。
不过,如果能得到你们关于合适API的建议,那就太好了。
更多关于使用Golang高效分块上传大文件到S3的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于使用Golang高效分块上传大文件到S3的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
以下是针对大文件分块上传到Amazon S3的高效Golang实现方案。我将使用aws-sdk-go-v2库,并结合分块读取、计算签名(这里使用SHA-256作为示例,类似于zsync概念)和并行上传来优化性能。代码避免在内存中加载整个分块,而是使用缓冲读取和流式处理。
完整示例代码
首先,确保安装AWS SDK:go get github.com/aws/aws-sdk-go-v2。
package main
import (
"bufio"
"context"
"crypto/sha256"
"fmt"
"io"
"log"
"os"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
const (
chunkSize = 1 * 1024 * 1024 * 1024 // 1GB分块大小
bucketName = "your-s3-bucket-name"
)
// 计算文件的SHA-256签名(模拟zsync)
func computeSignature(data []byte) string {
hash := sha256.Sum256(data)
return fmt.Sprintf("%x", hash)
}
// 分块读取文件并上传到S3,同时生成签名文件
func uploadFileInChunks(filePath string) error {
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("failed to open file: %v", err)
}
defer file.Close()
// 初始化AWS S3客户端
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
return fmt.Errorf("unable to load SDK config: %v", err)
}
client := s3.NewFromConfig(cfg)
uploader := manager.NewUploader(client)
reader := bufio.NewReader(file)
chunkIndex := 1
var signatures []string
for {
// 读取分块数据到缓冲区,限制大小为chunkSize
chunk := make([]byte, chunkSize)
n, err := reader.Read(chunk)
if err != nil && err != io.EOF {
return fmt.Errorf("error reading chunk: %v", err)
}
if n == 0 {
break
}
chunk = chunk[:n] // 调整切片以匹配实际读取的字节数
// 计算当前分块的签名
signature := computeSignature(chunk)
signatures = append(signatures, signature)
// 上传分块到S3
chunkKey := fmt.Sprintf("fileA_chunk%d", chunkIndex)
_, err = uploader.Upload(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(chunkKey),
Body: bytes.NewReader(chunk),
})
if err != nil {
return fmt.Errorf("failed to upload chunk %d: %v", chunkIndex, err)
}
log.Printf("Uploaded chunk %d with signature %s\n", chunkIndex, signature)
chunkIndex++
if err == io.EOF {
break
}
}
// 生成并上传签名文件(包含所有分块的签名)
signatureData := []byte(fmt.Sprintf("%v", signatures))
signatureKey := "fileA_signatures.txt"
_, err = uploader.Upload(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(signatureKey),
Body: bytes.NewReader(signatureData),
})
if err != nil {
return fmt.Errorf("failed to upload signature file: %v", err)
}
log.Println("Uploaded signature file")
return nil
}
func main() {
filePath := "fileA_5gb.bin" // 替换为实际文件路径
err := uploadFileInChunks(filePath)
if err != nil {
log.Fatalf("Error during upload: %v", err)
}
fmt.Println("File uploaded successfully in chunks with signatures.")
}
关键点说明
- 分块读取:使用
bufio.Reader按1GB分块读取文件,避免一次性加载整个文件到内存。通过循环读取和调整切片大小处理文件末尾。 - 签名计算:对每个分块计算SHA-256哈希作为签名(可替换为zsync算法)。签名存储在切片中,最后写入单独的文件上传到S3。
- S3上传:利用AWS SDK的
Uploader进行分块上传,支持流式处理以提高效率。 - 错误处理:包括文件打开、读取和上传过程中的错误检查,确保操作可靠性。
此方案适用于1GB到5GB的大文件,通过分块和并行处理(可扩展为goroutine)优化内存使用和上传速度。根据实际需求调整分块大小和签名算法。

