答案:通过自定义ProgressReader包装文件流,在读取时实时统计已上传字节数并回调更新进度,结合multipart上传实现golang文件上传进度显示。

在golang中实现文件上传进度显示,关键在于监控上传过程中已发送的数据量。标准的 net/http 包本身不提供进度回调机制,因此需要通过自定义 io.Reader 来实现对数据读取过程的拦截和统计。
1. 使用带进度的Reader包装原始文件
核心思路是创建一个包装了原文件的 io.Reader,在每次读取时更新已上传字节数,并调用回调函数显示进度。
示例代码:
package main import ( "fmt" "io" "mime/multipart" "net/http" "os" "sync" ) // ProgressReader 记录上传进度 type ProgressReader struct { file *os.File total int64 read int64 callback func(read, total int64) mu sync.Mutex } func (pr *ProgressReader) Read(p []byte) (n int, err error) { n, err = pr.file.Read(p) pr.mu.Lock() pr.read += int64(n) pr.callback(pr.read, pr.total) pr.mu.Unlock() return n, err } func (pr *ProgressReader) Close() error { return pr.file.Close() }
2. 构造带进度的HTTP请求
使用 multipart/form-data 格式上传文件,在构建请求体时将原始文件替换为 ProgressReader。
func uploadFileWithProgress(filepath, url string) error { file, err := os.Open(filepath) if err != nil { return err } defer file.Close() fileInfo, _ := file.Stat() total := fileInfo.Size() body := &bytes.Buffer{} writer := multipart.NewWriter(body) // 创建带进度的Reader progressReader := &ProgressReader{ file: file, total: total, callback: func(read, total int64) { fmt.Printf("r上传进度: %d/%d (%.2f%%)", read, total, float64(read)/float64(total)*100) }, } part, err := writer.CreateFormFile("upload", filepath) if err != nil { return err } _, err = io.Copy(part, progressReader) if err != nil { return err } writer.Close() req, err := http.NewRequest("POST", url, body) if err != nil { return err } req.Header.Set("Content-Type", writer.FormDataContentType()) client := &http.Client{} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() fmt.Println("n上传完成") return nil }
3. 调用上传函数
只需调用封装好的函数即可看到实时进度输出。
立即学习“go语言免费学习笔记(深入)”;
func main() { err := uploadFileWithProgress("./example.zip", "https://httpbin.org/post") if err != nil { fmt.Println("上传失败:", err) } }
这种方式适用于客户端主动上传文件的场景。如果是在服务端接收大文件并想通知前端进度,则需结合数据库或redis记录状态,并通过websocket或轮询方式推送进度。
基本上就这些,核心就是用一个中间层Reader来“监听”数据流动。


