Go 低内存大文件上传

介绍

当涉及到文件上传时,低内存占用的客户端是非常重要的。在本篇博客中,我们将介绍如何使用Go语言实现一个低内存占用的HTTP文件上传客户端。

流程

首先,让我们先讨论一下HTTP文件上传的基本原理。HTTP文件上传是通过POST请求向服务器发送数据的。请求的头部包含文件的元数据,请求的主体则包含文件的二进制数据。由于HTTP请求和响应在网络上传输的过程中可能会被分割成多个小块,因此上传大文件时会占用大量的内存。

为了解决这个问题,我们可以使用Go语言的 multipart 文件上传。multipart 文件上传是将文件分割成多个部分进行上传,每个部分的大小可以根据需求进行调整。这种方法可以使上传过程中所占用的内存量大大降低,从而可以处理更大的文件。

但是,即使是使用了 multipart 来进行文件上传,对于大文件的上传来说, 还是会占用大量的内存,因此我们还需要使用到别的手段来降低内存占用

io.Pipe

io.Pipe 提供了一个管道,可以在两个不同的 goroutine 之间传递数据,这对于需要在流式数据中传递大量数据时非常有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main

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

func main() {
// 打开要上传的文件
file, err := os.Open("example.txt")
if err != nil {
panic(err)
}
defer file.Close()

// 创建一个管道
reader, writer := io.Pipe()

// 创建一个多部分表单写入器,将文件写入到管道中
go func() {
defer writer.Close()
multipartWriter := multipart.NewWriter(writer)
part, err := multipartWriter.CreateFormFile("file", "example.txt")
if err != nil {
panic(err)
}
io.Copy(part, file)
multipartWriter.Close()
}()

// 创建一个HTTP请求
request, err := http.NewRequest("POST", "http://localhost:8080/upload", reader)
if err != nil {
panic(err)
}
request.Header.Set("Content-Type", "multipart/form-data")

// 发送请求
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()

// 处理响应
responseBody, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
fmt.Println(string(responseBody))
}

在上面的代码中,我们创建了一个管道,将文件数据写入到管道中,并将管道中的数据作为HTTP请求的主体发送到服务器。由于管道中的数据是按需传输的,因此可以大大降低内存的占用。并且使用了mime/multipart包来处理多部分表单数据。创建HTTP请求时,我们将请求的主体设置为缓冲区的内容,并设置请求头部的Content-Type字段为multipart/form-data,以指示请求包含多部分表单数据。

在读取响应主体时,我们使用了 io.ReadAll 函数,这将响应主体的所有数据读入内存中,并返回一个字节数组,这可能会导致内存使用量较高。因此,在处理响应时,您可以根据需要对其进行修改以降低内存使用量。

总之,使用 io.Pipe 可以轻松实现低内存占用的HTTP文件上传客户端,这对于处理大型文件和数据流非常有用。


Go 低内存大文件上传
https://blog.zhangliangliang.cc/post/go-buffer-less-multipart-form.html
作者
Bobby Zhang
发布于
2023年2月14日
许可协议