为啥上传文件 ~2,5 MiB 或更大会导致连接重置?
Posted
技术标签:
【中文标题】为啥上传文件 ~2,5 MiB 或更大会导致连接重置?【英文标题】:Why does uploading files ~2,5 MiB or larger cause a connection reset?为什么上传文件 ~2,5 MiB 或更大会导致连接重置? 【发布时间】:2022-01-02 07:37:26 【问题描述】:我们正在尝试通过 POST 请求实现图片上传。我们还希望将图像限制在 ~1,0 MiB。它适用于较小的图像,但任何 ~2,5 MiB 或更大的东西都会导致连接重置。它似乎也在第一个请求之后向同一个处理程序发送多个请求。
main.go:
package main
import (
"log"
"net/http"
)
func main()
http.HandleFunc("/", uploadHandler)
http.ListenAndServe("localhost:8080", nil)
func uploadHandler(w http.ResponseWriter, r *http.Request)
if r.Method == "POST"
postHandler(w, r)
return
else
http.ServeFile(w, r, "index.html")
func postHandler(w http.ResponseWriter, r *http.Request)
// Send an error if the request is larger than 1 MiB
if r.ContentLength > 1<<20
// if larger than ~2,5 MiB, this will print 2 or more times
log.Println("File too large")
// And this error will never arrive, instead a Connection reset
http.Error(w, "response too large", http.StatusRequestEntityTooLarge)
return
return
index.html:
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form method="POST" enctype="multipart/form-data">
<input type="file" accept="image/*" name="profile-picture"><br>
<button type="submit" >Upload</button>
</form>
</body>
</html>
上传 ~2,4 MiB 文件时的输出
$ go run main.go
2021/11/23 22:00:14 File too large
它还在浏览器中显示“请求太大”
上传 ~2,5 MiB 文件时的输出
$ go run main.go
2021/11/23 22:03:25 File too large
2021/11/23 22:03:25 File too large
浏览器现在显示连接已重置
【问题讨论】:
if r.ContentLength > 1<<20
即 1,048,576 字节或 1MiB。 postHandler
认为任何大于 1M 的东西都太大了。
@Schwern 按预期工作。我们希望在上传时限制图片上传大小,虽然我们不想在上传的图片太大时重置连接,也不希望浏览器发送多个请求。虽然在帖子中添加了一些说明
浏览器和 Go 服务之间有什么关系?有反向代理、负载均衡器、WAF、CDN等吗?
@Adrian 两者之间不应该有任何东西,它完全是本地和自包含的代码。
@urist 那么这绝对是问题所在。您实际上并没有在处理程序中读取请求,因此您在客户端上遇到了写入超时。
【参考方案1】:
客户端正在尝试向服务器发送数据。服务器没有读取数据,它只是查看标头并关闭连接。客户端将此解释为“连接已重置”。这是你无法控制的。
不检查header,header可以说谎,使用http.MaxBytesReader
读取实际内容,但是太大会报错。
const MAX_UPLOAD_SIZE = 1<<20
func postHandler(w http.ResponseWriter, r *http.Request)
// Wrap the body in a reader that will error at MAX_UPLOAD_SIZE
r.Body = http.MaxBytesReader(w, r.Body, MAX_UPLOAD_SIZE)
// Read the body as normal. Check for an error.
if err := r.ParseMultipartForm(MAX_UPLOAD_SIZE); err != nil
// We're assuming it errored because the body is too large.
// There are other reasons it could error, you'll have to
// look at err to figure that out.
log.Println("File too large")
http.Error(w, "Your file is too powerful", http.StatusRequestEntityTooLarge)
return
fmt.Fprintf(w, "Upload successful")
更多详情请参阅How to process file uploads in Go。
【讨论】:
这似乎在一定程度上可以发挥作用,并且实际上适合我们的需求。但是,它仍然会导致使用足够大的图像(例如this)进行连接重置,例如,如果我想将 MAX_UPLOAD_SIZE 提高到20 << 20
,无论出于何种原因,我都无法上传该大小的任何内容。跨度>
我还认为r.ContentLength
可以在上传任何文件之前用作快速检查,即使我知道它可能被欺骗。我想你可以在客户端实现这个
我想我从上面的评论中弄清楚了为什么连接重置了,你需要真正开始使用数据(例如r.FormFile
),否则它只会停止并返回,然后关闭一旦开始对客户端发送的常量数据感到恼火,连接就开始了。以上是关于为啥上传文件 ~2,5 MiB 或更大会导致连接重置?的主要内容,如果未能解决你的问题,请参考以下文章