You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang大文件Multipart流式上传至外部API,规避内存溢出

流式上传大文件至multipart/form-data接口(Golang实现)

完全可以通过流式方式避免大文件占用内存的问题,核心思路是不预先将整个multipart内容(包括文件)写入内存缓冲区,而是直接将数据流式写入请求Body,让HTTP客户端逐步发送数据。

关键实现方案

利用io.Pipe()实现读写端的流式传输,结合multipart.Writer直接向管道写端写入表单内容,HTTP请求的Body则使用管道的读端,这样文件内容会被分块传输,不会一次性加载到内存中。

完整代码示例

package main

import (
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"os"
)

func main() {
	// 打开目标大文件
	file, err := os.Open("large-file.bin")
	if err != nil {
		log.Fatalf("打开文件失败: %v", err)
	}
	defer file.Close()

	// 创建管道:读端作为请求Body,写端用于写入multipart内容
	pr, pw := io.Pipe()
	defer pr.Close()

	// 初始化multipart写入器,绑定管道写端
	writer := multipart.NewWriter(pw)
	defer writer.Close()

	// 启动goroutine写入multipart内容(避免阻塞主goroutine)
	go func() {
		defer pw.Close() // 写入完成后关闭写端,告知读端数据结束

		// 写入普通表单字段(按需添加)
		descPart, err := writer.CreateFormField("description")
		if err != nil {
			log.Printf("创建表单字段失败: %v", err)
			return
		}
		if _, err := descPart.Write([]byte("大文件流式上传测试")); err != nil {
			log.Printf("写入表单字段失败: %v", err)
			return
		}

		// 创建文件字段
		filePart, err := writer.CreateFormFile("media", file.Name())
		if err != nil {
			log.Printf("创建文件字段失败: %v", err)
			return
		}

		// 流式复制文件内容到multipart字段:仅缓存小块数据,不加载整个文件
		if _, err := io.Copy(filePart, file); err != nil {
			log.Printf("复制文件内容失败: %v", err)
			return
		}

		// 结束multipart写入
		if err := writer.Close(); err != nil {
			log.Printf("关闭multipart写入器失败: %v", err)
		}
	}()

	// 构建POST请求
	req, err := http.NewRequest(http.MethodPost, "https://somehost/media", pr)
	if err != nil {
		log.Fatalf("创建请求失败: %v", err)
	}
	defer req.Body.Close()

	// 设置正确的Content-Type(包含multipart边界标识)
	req.Header.Set("Content-Type", writer.FormDataContentType())

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

	// 处理响应
	log.Printf("响应状态码: %d", resp.StatusCode)
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Printf("读取响应内容失败: %v", err)
		return
	}
	log.Printf("响应内容: %s", string(respBody))
}

核心说明

  1. io.Pipe()的作用:实现读写端的实时数据传输,写端写入的内容会立即被读端读取,不会在内存中缓存完整文件。
  2. goroutine异步写入:避免io.Copy(阻塞操作)与client.Do()(同样阻塞)互相等待,保证请求能正常发送。
  3. 避免内存堆积:对比直接将multipart内容写入bytes.Buffer的方式,流式传输仅会保留HTTP客户端默认的小块传输缓冲区,内存占用极低。

内容的提问来源于stack exchange,提问作者vkstack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 20:05:12