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

时间:2017-05-14 00:12:35

标签: 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)
}

服务器代码:

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))
}

在localhost上运行服务器后,运行客户端代码,我得到:

Copied 1448401 bytes for uploading...
Successfully uploaded apk file: 200

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

Error retrieving apk file: unexpected EOF

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

1 个答案:

答案 0 :(得分:2)

错误表示读者在请求正文结束后需要更多数据。缺少的数据是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)
}