为什么上传大小约为2.5 MiB或更大的文件会导致连接重置?

huangapple go评论137阅读模式
英文:

Why does uploading files ~2,5 MiB or larger cause a connection reset?

问题

我们正在尝试通过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) {
    // 如果请求大于1 MiB,则发送错误
	if r.ContentLength > 1<<20 {
        // 如果大于2.5 MiB,这将打印2次或更多
		log.Println("文件太大")
        // 而且这个错误永远不会到达,而是会重置连接
		http.Error(w, "响应太大", 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" >上传</button>
  </form>
  </body>
</html>

上传大约2.4 MiB文件时的输出

$ go run main.go
2021/11/23 22:00:14 文件太大

浏览器中也显示"请求太大"

上传大约2.5 MiB文件时的输出

$ go run main.go
2021/11/23 22:03:25 文件太大
2021/11/23 22:03:25 文件太大

浏览器现在显示连接被重置。

英文:

We are trying to implement image uploading through POST requests. We also want to limit the images to ~1,0 MiB. It works fine on smaller images, but anything ~2,5 MiB or larger causes the connection to reset. It also seems to send multiple requests after the first to the same handler.

main.go:

package main

import (
	&quot;log&quot;
	&quot;net/http&quot;
)

func main() {
	http.HandleFunc(&quot;/&quot;, uploadHandler)
	http.ListenAndServe(&quot;localhost:8080&quot;, nil)
}

func uploadHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method == &quot;POST&quot; {
		postHandler(w, r)
		return
	} else {
		http.ServeFile(w, r, &quot;index.html&quot;)
	}
}

func postHandler(w http.ResponseWriter, r *http.Request) {
    // Send an error if the request is larger than 1 MiB
	if r.ContentLength &gt; 1&lt;&lt;20 {
        // if larger than ~2,5 MiB, this will print 2 or more times
		log.Println(&quot;File too large&quot;)
        // And this error will never arrive, instead a Connection reset
		http.Error(w, &quot;response too large&quot;, http.StatusRequestEntityTooLarge)
		return
	}
	return
}

index.html:

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;title&gt;&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;form method=&quot;POST&quot; enctype=&quot;multipart/form-data&quot;&gt;
      &lt;input type=&quot;file&quot; accept=&quot;image/*&quot; name=&quot;profile-picture&quot;&gt;&lt;br&gt;
      &lt;button type=&quot;submit&quot; &gt;Upload&lt;/button&gt;
  &lt;/form&gt;
  &lt;/body&gt;
&lt;/html&gt;

Output when uploading a ~2,4 MiB file

$ go run main.go
2021/11/23 22:00:14 File too large

It also shows "request too large" in the browser

Output when uploading ~2,5 MiB file

$ go run main.go
2021/11/23 22:03:25 File too large
2021/11/23 22:03:25 File too large

The browser now shows that the connection was reset

答案1

得分: 3

客户端正在尝试向服务器发送数据。服务器并不读取数据,只查看头部信息并关闭连接。客户端将此解释为“连接被重置”。这超出了你的控制范围。

不要仅仅检查头部信息,因为头部信息可能是错误的,可以使用http.MaxBytesReader来读取实际内容,但如果内容太大则报错。

const MAX_UPLOAD_SIZE = 1<<20

func postHandler(w http.ResponseWriter, r *http.Request) {
    // 使用一个读取器将请求体包装起来,当超过 MAX_UPLOAD_SIZE 时会报错
    r.Body = http.MaxBytesReader(w, r.Body, MAX_UPLOAD_SIZE)

    // 正常读取请求体,并检查是否有错误发生
    if err := r.ParseMultipartForm(MAX_UPLOAD_SIZE); err != nil {
        // 我们假设发生错误是因为请求体太大。
        // 当然,还有其他可能的错误原因,你需要查看 err 来确定具体原因。
        log.Println("文件太大")
        http.Error(w, "您的文件太大", http.StatusRequestEntityTooLarge)
        return
    }

    fmt.Fprintf(w, "上传成功")
}

详细信息请参阅如何在Go中处理文件上传

英文:

The client is trying to send data to the server. The server is not reading the data, it's just looking at the headers and closing the connection. The client is interpreting this as "connection was reset". This is out of your control.

Instead of checking the header, the header can lie, use http.MaxBytesReader to read the actual content, but error if it is too large.

const MAX_UPLOAD_SIZE = 1&lt;&lt;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&#39;re assuming it errored because the body is too large.
        // There are other reasons it could error, you&#39;ll have to
        // look at err to figure that out.
        log.Println(&quot;File too large&quot;)
        http.Error(w, &quot;Your file is too powerful&quot;, http.StatusRequestEntityTooLarge)
        return
    }

    fmt.Fprintf(w, &quot;Upload successful&quot;)
}

See How to process file uploads in Go for more detail.

huangapple
  • 本文由 发表于 2021年11月24日 04:06:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/70087184.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定