在Go中的多部分HTTP请求

3

我正在尝试使用Go编写一个实用程序,通过向服务器发送多部分http请求进行身份验证和上传文件。一切似乎都很顺利,但是文件没有到达服务器。进一步查看后发现请求中的多部分为空。以下是代码和请求输出。在我的Go代码中缺少什么?

代码:(我已更改了URL...)

package main

import (
    "net/http"
    "mime/multipart"
    "strings"
    "fmt"
    "io/ioutil"
    "io"
    "os"
    "bytes"
    "flag"
    "encoding/json"
)

var (
    filename = flag.String("filename", "", "file to upload")
    name = flag.String("name", "", "name to give file on server")
    username = flag.String("username", "", "username for authentication")
    password = flag.String("password", "", "password for authentication")
)

func main() {
    flag.Parse()

    // Create multipart
    var b bytes.Buffer
    w := multipart.NewWriter(&b)
    f, _ := os.Open(*filename) //open file to send
    defer f.Close()
    fw, err := w.CreateFormFile("file", *name) //give file a name
    if err != nil {
        fmt.Println(err)
    }
    if _, err :=  io.Copy(fw, f); err != nil { //copy the file to the multipart buffer
        fmt.Println(err)
    }
    w.Close()

    // print the head of the multipart data
    bs := b.Bytes()
    fmt.Printf("%+v\n\n", string(bs[:1000]))

    // Send authentication/login
    r, e := http.Post("https://mysite/login", "application/json", strings.NewReader(fmt.Sprintf("{\"username\":\"%s\",\"password\":\"%s\"}", *username, *password)))

    if e != nil {
        fmt.Println(e)
    } else {
        // Get the token from the body
        type Body struct {
            Token string
        }
        // convert json to get the token
        body, _ := ioutil.ReadAll(r.Body)
        bd := bytes.NewBuffer(body)
        dec := json.NewDecoder(bd)
        var m Body
        dec.Decode(&m)

        // Upload file
        req, err := http.NewRequest("POST", "https://mysite/api/apps", &b)
        if err != nil {
            fmt.Printf("%v\n", err)
        }
        req.Header.Set("Authentication", fmt.Sprintf("Bearer: %s", m.Token))
        req.Header.Set("Content-Type", w.FormDataContentType())
        client := &http.Client{}
        res, err := client.Do(req)
        if err != nil {
            fmt.Printf("%v\n", err)
        }
        // print status and request body
        fmt.Println(res.Status)
        fmt.Printf("%+v\n", res.Request)
    }
}

我打印的第一件事是字节缓冲区b,其中包含多部分数据,从这里看起来一切都很好。(它是一个XML文件)
--83451b003d8e5cc38c0e8f60ad318e522cad4818cf293745c84ec36d26d5
Content-Disposition: form-data; name="file"; filename="snapshot-162224-820-99"
Content-Type: application/octet-stream

<manifest>
  <projects>
    <project name=........

下面我会打印请求的状态。
200 OK

然后我打印出请求结构,这是我发现MultipartForm为空的地方。

&{Method:GET URL:https://mysite/home/ Proto: ProtoMajor:0 ProtoMinor:0 Header:map[Authentication:[Bearer: DY0LCJL0g] Content-Type:[multipart/form-data; boundary=83451b003d8e5cc38c0e8f60ad318e522cad4818cf293745c84ec36d26d5] Referer:[http://mysite/home/]] Body:<nil> GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host: Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:0xc42018a360 ctx:<nil>}

请尝试不打印缓冲区,我在某个地方读到过缓冲区只能被消耗一次! - Nidhin David
不要忽略错误 - Jonathan Hall
我检查错误并将其打印出来。 - dangeroushobo
也许,服务器期望您正确设置内容类型,而不是application/octet-stream,即text/xmlapplication/xml?在这种情况下,不要使用CreateFormFile,而是使用CreatePart然后手动设置内容类型。 - putu
还有一件事,打印的请求方法是 GET,但在你的代码中,你使用的是 POST。你打印的是正确的吗? - putu
1个回答

0

我非常怀疑服务器是否真的收到了什么。打印的响应体为nil是http.Response中预期且有记录的行为。

    // Request is the request that was sent to obtain this Response.
    // Request's Body is nil (having already been consumed).
    // This is only populated for Client requests.
    Request *Request

如果您想调试发送的请求正文,您应该使用模拟服务器或代理。

另外,您的代码尝试登录是不会起作用的。它没有维护登录信息的cookie,因此后来的请求无法利用它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接