读取多部分请求会导致意外的 EOF 错误

dar*_*ord 3 go

Go版本:1.6.3 macos

我正在尝试编写一个 api 来将 apk 文件(大多数情况下为几 MB)上传到服务器。这是客户端代码:

func syncApk(apkFile *os.File) {
    defer apkFile.Close()
    var buffer bytes.Buffer
    writer := multipart.NewWriter(&buffer)
    defer writer.Close()
    part, err := writer.CreateFormFile("apk", filepath.Base(apkFile.Name()))
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error creating form file: %v\n", err)
        return
    }

    size, err := io.Copy(part, apkFile)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error copying apk file data: %v\n", err)
        return
    }
    fmt.Fprintf(os.Stdout, "Copied %v bytes for uploading...\n", size)

    response, err := http.Post("http://localhost:8080/upload", writer.FormDataContentType(), &buffer)

    if err != nil {
        fmt.Fprintf(os.Stderr, "Error making POST request to sync apk: %v\n", err)
        return
    }

    fmt.Fprintf(os.Stdout, "Successfully uploaded apk file: %v\n", response.StatusCode)
}
Run Code Online (Sandbox Code Playgroud)

服务器代码:

func main() {
    server := http.Server{
        Addr: ":8080",
    }
    http.HandleFunc("/upload", doApkUpload)
    server.ListenAndServe()
}

func doApkUpload(w http.ResponseWriter, r *http.Request) {
    file, _, err := r.FormFile("apk")
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error retrieving apk file: %v\n", err)
        return
    }

    data, err := ioutil.ReadAll(file)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error reading apk file content: %v", err)
        return
    }

    fmt.Fprintf(os.Stdout, string(data))
}
Run Code Online (Sandbox Code Playgroud)

在本地主机上运行服务器后,我运行客户端代码,得到:

Copied 1448401 bytes for uploading...
Successfully uploaded apk file: 200
Run Code Online (Sandbox Code Playgroud)

看起来多部分文件写入正确。但是我在服务器端看到这个错误:

Error retrieving apk file: unexpected EOF
Run Code Online (Sandbox Code Playgroud)

知道问题出在哪里吗?谢谢!

Cer*_*món 9

该错误表明读取器期望在请求正文结束后获得更多数据。缺失的数据是由multipart Close方法写入的尾随边界结束线。

在编写所有部分之后、发布表单之前调用 Close 方法。

func syncApk(apkFile *os.File) {
    defer apkFile.Close()
    var buffer bytes.Buffer
    writer := multipart.NewWriter(&buffer)
    part, err := writer.CreateFormFile("apk", filepath.Base(apkFile.Name()))
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error creating form file: %v\n", err)
        return
    }

    size, err := io.Copy(part, apkFile)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error copying apk file data: %v\n", err)
        return
    }
    fmt.Fprintf(os.Stdout, "Copied %v bytes for uploading...\n", size)
    writer.Close()
    response, err := http.Post("http://localhost:8080/upload", writer.FormDataContentType(), &buffer)

    if err != nil {
        fmt.Fprintf(os.Stderr, "Error making POST request to sync apk: %v\n", err)
        return
    }
    defer response.Body.Clse()

    fmt.Fprintf(os.Stdout, "Successfully uploaded apk file: %v\n", response.StatusCode)
}
Run Code Online (Sandbox Code Playgroud)