如何用Golang实现文件上传进度显示

来源:程序开发作者:梦乃头衔:网络博主
导读:本期聚焦于小伙伴创作的《如何用Golang实现文件上传进度显示》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何用Golang实现文件上传进度显示》有用,将其分享出去将是对创作者最好的鼓励。

在Golang中实现文件上传进度显示,核心是通过自定义实现io.Reader接口,在读取文件内容时同步记录已读取的字节数,再通过回调函数将进度信息传递给上层业务。这种方式不需要修改HTTP Client的底层逻辑,适配性较强。

如何用Golang实现文件上传进度显示

实现原理

HTTP Client上传文件时,会将文件内容作为请求体发送给服务端。我们可以封装一个带进度跟踪的读取器,在每次读取文件数据时,更新已读取的字节数,计算当前上传进度,再触发进度回调。整体流程如下:

  • 打开本地待上传的文件,获取文件总大小
  • 创建自定义进度读取器,包装文件句柄
  • 将进度读取器作为HTTP请求的Body传入
  • 在进度读取器的Read方法中更新已读字节数,计算进度并回调

自定义进度读取器实现

首先定义进度读取器的结构体,包含原始文件句柄、文件总大小、已读字节数和进度回调函数:

package main

import (
	"io"
	"os"
)

// 进度回调函数类型,参数分别是已读字节数和总字节数
type ProgressFunc func(readBytes, totalBytes int64)

// 进度读取器结构体
type ProgressReader struct {
	reader     io.Reader // 原始读取器
	total      int64     // 文件总大小
	readBytes  int64     // 已读取字节数
	progressFn ProgressFunc // 进度回调函数
}

// 实现Read方法,符合io.Reader接口
func (pr *ProgressReader) Read(p []byte) (n int, err error) {
	// 调用原始读取器的Read方法
	n, err = pr.reader.Read(p)
	if n > 0 {
		// 更新已读字节数
		pr.readBytes += int64(n)
		// 触发进度回调
		if pr.progressFn != nil {
			pr.progressFn(pr.readBytes, pr.total)
		}
	}
	return
}

完整上传示例

接下来结合HTTP Client实现完整的文件上传功能,包含进度打印逻辑:

package main

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
)

func main() {
	// 待上传的文件路径
	filePath := "./test_file.zip"
	// 上传目标URL
	uploadURL := "http://127.0.0.1:8080/upload"

	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		fmt.Printf("打开文件失败: %vn", err)
		return
	}
	defer file.Close()

	// 获取文件信息,得到文件总大小
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("获取文件信息失败: %vn", err)
		return
	}
	totalSize := fileInfo.Size()

	// 创建multipart表单写入器
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// 创建表单文件字段
	part, err := writer.CreateFormFile("file", filepath.Base(filePath))
	if err != nil {
		fmt.Printf("创建表单字段失败: %vn", err)
		return
	}

	// 创建进度读取器,包装文件句柄
	progressReader := &ProgressReader{
		reader:    file,
		total:     totalSize,
		readBytes: 0,
		progressFn: func(readBytes, totalBytes int64) {
			// 计算进度百分比
			percent := float64(readBytes) / float64(totalBytes) * 100
			fmt.Printf("上传进度: %.2f%% (%d/%d 字节)n", percent, readBytes, totalBytes)
		},
	}

	// 将文件内容写入表单字段,这里使用进度读取器读取文件
	_, err = io.Copy(part, progressReader)
	if err != nil {
		fmt.Printf("写入文件内容失败: %vn", err)
		return
	}

	// 关闭multipart写入器,生成结束边界
	err = writer.Close()
	if err != nil {
		fmt.Printf("关闭表单写入器失败: %vn", err)
		return
	}

	// 创建HTTP请求
	req, err := http.NewRequest("POST", uploadURL, body)
	if err != nil {
		fmt.Printf("创建请求失败: %vn", err)
		return
	}
	// 设置Content-Type为multipart表单类型
	req.Header.Set("Content-Type", writer.FormDataContentType())

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("发送请求失败: %vn", err)
		return
	}
	defer resp.Body.Close()

	// 读取响应
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("读取响应失败: %vn", err)
		return
	}
	fmt.Printf("上传完成,服务端响应: %sn", respBody)
}

注意事项

在实际使用中需要注意以下几点:

  • 如果上传的文件较大,进度回调的触发频率会比较高,可根据需求增加节流逻辑,比如每上传1%再触发一次回调,避免频繁打印日志影响性能
  • 进度读取器的Read方法可能被多次调用,已读字节数的累加逻辑需要保证线程安全,如果是并发场景需要加锁
  • 如果上传过程中出现网络错误,已读字节数不会回滚,可根据业务需求调整进度计算的基准

服务端简易示例

为了测试上传功能,这里提供一个简易的Golang HTTP服务端代码,接收上传的文件:

package main

import (
	"fmt"
	"net/http"
	"os"
)

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	// 限制上传文件大小为100MB
	r.ParseMultipartForm(100 << 20)
	// 获取上传的文件
	file, handler, err := r.FormFile("file")
	if err != nil {
		fmt.Fprintf(w, "获取文件失败: %v", err)
		return
	}
	defer file.Close()

	// 创建本地文件保存上传内容
	dst, err := os.Create("./upload_" + handler.Filename)
	if err != nil {
		fmt.Fprintf(w, "创建文件失败: %v", err)
		return
	}
	defer dst.Close()

	// 拷贝文件内容
	_, err = io.Copy(dst, file)
	if err != nil {
		fmt.Fprintf(w, "保存文件失败: %v", err)
		return
	}
	fmt.Fprintf(w, "文件上传成功")
}

func main() {
	http.HandleFunc("/upload", uploadHandler)
	fmt.Println("服务端启动,监听8080端口")
	http.ListenAndServe(":8080", nil)
}

GolangHTTP_Client文件上传进度显示修改时间:2026-06-21 23:15:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。