为啥上传文件 ~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 &gt; 1&lt;&lt;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 &lt;&lt; 20,无论出于何种原因,我都无法上传该大小的任何内容。跨度> 我还认为r.ContentLength 可以在上传任何文件之前用作快速检查,即使我知道它可能被欺骗。我想你可以在客户端实现这个 我想我从上面的评论中弄清楚了为什么连接重置了,你需要真正开始使用数据(例如r.FormFile),否则它只会停止并返回,然后关闭一旦开始对客户端发送的常量数据感到恼火,连接就开始了。

以上是关于为啥上传文件 ~2,5 MiB 或更大会导致连接重置?的主要内容,如果未能解决你的问题,请参考以下文章

无法增加文件上传大小 WAMP

为啥总是显示数据加载失败,之前都正常的。

为啥我QQ头像老是显示上传失败

格子达为啥上传文件是空的

为啥上传到 iTunes 连接时出现缺少 64 位支持?

苹果手表连接手机为啥显示要更新