Golang中如何获取API上传进度

Golang中如何获取API上传进度 我通过以下链接实现了上传功能:

stackoverflow.com

http, upload, go, progress

但在上传过程中,我不知道如何通过另一个GET请求获取进度信息…

1 回复

更多关于Golang中如何获取API上传进度的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,要实现通过另一个GET请求获取上传进度,需要在上传过程中维护一个全局的进度状态,并在独立的HTTP端点中提供查询功能。以下是完整的实现方案:

1. 定义进度跟踪结构

package main

import (
    "fmt"
    "io"
    "net/http"
    "sync"
    "time"
)

type UploadProgress struct {
    TotalBytes    int64
    BytesRead     int64
    UploadID      string
    StartTime     time.Time
    mu            sync.RWMutex
}

var (
    uploads = make(map[string]*UploadProgress)
    uploadsMutex sync.RWMutex
)

2. 实现自定义Reader跟踪进度

type ProgressReader struct {
    reader    io.Reader
    progress  *UploadProgress
}

func (pr *ProgressReader) Read(p []byte) (int, error) {
    n, err := pr.reader.Read(p)
    
    if n > 0 {
        pr.progress.mu.Lock()
        pr.progress.BytesRead += int64(n)
        pr.progress.mu.Unlock()
    }
    
    return n, err
}

3. 实现文件上传处理器

func uploadHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }

    uploadID := r.URL.Query().Get("upload_id")
    if uploadID == "" {
        uploadID = fmt.Sprintf("upload_%d", time.Now().UnixNano())
    }

    // 解析multipart表单
    err := r.ParseMultipartForm(32 << 20) // 32MB
    if err != nil {
        http.Error(w, "Failed to parse form", http.StatusBadRequest)
        return
    }

    file, header, err := r.FormFile("file")
    if err != nil {
        http.Error(w, "Failed to get file", http.StatusBadRequest)
        return
    }
    defer file.Close()

    // 创建进度跟踪
    progress := &UploadProgress{
        TotalBytes: header.Size,
        BytesRead:  0,
        UploadID:   uploadID,
        StartTime:  time.Now(),
    }

    // 存储进度信息
    uploadsMutex.Lock()
    uploads[uploadID] = progress
    uploadsMutex.Unlock()

    // 清理函数
    defer func() {
        uploadsMutex.Lock()
        delete(uploads, uploadID)
        uploadsMutex.Unlock()
    }()

    // 使用进度跟踪Reader包装文件
    progressReader := &ProgressReader{
        reader:   file,
        progress: progress,
    }

    // 模拟文件处理(实际应用中可能是保存到磁盘或其他存储)
    buffer := make([]byte, 1024)
    for {
        _, err := progressReader.Read(buffer)
        if err == io.EOF {
            break
        }
        if err != nil {
            http.Error(w, "Upload failed", http.StatusInternalServerError)
            return
        }
        
        // 模拟处理延迟
        time.Sleep(10 * time.Millisecond)
    }

    w.WriteHeader(http.StatusOK)
    fmt.Fprintf(w, "Upload completed: %s", uploadID)
}

4. 实现进度查询端点

func progressHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }

    uploadID := r.URL.Query().Get("upload_id")
    if uploadID == "" {
        http.Error(w, "Missing upload_id parameter", http.StatusBadRequest)
        return
    }

    uploadsMutex.RLock()
    progress, exists := uploads[uploadID]
    uploadsMutex.RUnlock()

    if !exists {
        http.Error(w, "Upload not found", http.StatusNotFound)
        return
    }

    progress.mu.RLock()
    percentage := float64(0)
    if progress.TotalBytes > 0 {
        percentage = float64(progress.BytesRead) / float64(progress.TotalBytes) * 100
    }
    
    response := map[string]interface{}{
        "upload_id":   progress.UploadID,
        "bytes_read":  progress.BytesRead,
        "total_bytes": progress.TotalBytes,
        "percentage":  fmt.Sprintf("%.2f", percentage),
        "elapsed":     time.Since(progress.StartTime).String(),
    }
    progress.mu.RUnlock()

    w.Header().Set("Content-Type", "application/json")
    fmt.Fprintf(w, `{"upload_id":"%s","bytes_read":%d,"total_bytes":%d,"percentage":%s,"elapsed":"%s"}`,
        response["upload_id"], response["bytes_read"], response["total_bytes"], 
        response["percentage"], response["elapsed"])
}

5. 主函数和路由设置

func main() {
    http.HandleFunc("/upload", uploadHandler)
    http.HandleFunc("/progress", progressHandler)
    
    fmt.Println("Server starting on :8080")
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        fmt.Printf("Server failed: %v\n", err)
    }
}

使用示例

上传文件:

curl -X POST -F "file=@largefile.zip" "http://localhost:8080/upload?upload_id=test123"

查询进度:

curl "http://localhost:8080/progress?upload_id=test123"

响应示例:

{
    "upload_id": "test123",
    "bytes_read": 5242880,
    "total_bytes": 10485760,
    "percentage": "50.00",
    "elapsed": "2.5s"
}

这个实现提供了线程安全的进度跟踪,支持并发上传,并通过独立的GET端点实时查询上传进度。在实际生产环境中,可以考虑使用Redis等外部存储来替代内存映射,以支持分布式部署。

回到顶部