使用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

1 回复

更多关于使用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)优化内存使用和上传速度。根据实际需求调整分块大小和签名算法。

回到顶部