如何使用Go语言解析一个HTTP请求中的文件和JSON数据?

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

How do you parse both a file and JSON data out of one HTTP request with Go?

问题

我被卡住了,试图弄清楚如何从一个 Angularjs 前端的 HTTP 请求表单中解析出 PDF 文档和 JSON 数据。请求的有效载荷如下:

HTTP 请求

Content-Disposition: form-data; name="file"; filename="Parent Handbook.pdf"
Content-Type: application/pdf

Content-Disposition: form-data; name="doc"

{"title":"test","cat":"test cat","date":20142323}

"file" 是 PDF 文件,"doc" 是我想解析的 JSON 数据。

我的处理程序可以成功解析和保存文件,但无法从 JSON 中获取任何内容。有什么想法吗?

func (s *Server) PostFileHandler(w http.ResponseWriter, r *http.Request) {
    const _24K = (1 << 20) * 24
    err := r.ParseMultipartForm(_24K)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    doc := Doc{}
    jsonDecoder := json.NewDecoder(r.Body)
    fmt.Println(r.Body)
    err = jsonDecoder.Decode(&doc)
    if err != nil {
        fmt.Println(err.Error())
    }
    fmt.Println(doc.Title, doc.Url, doc.Cat, doc.Date)
    doc.Id = len(docs) + 1
    err = s.db.Insert(&doc)
    checkErr(err, "Insert failed")

    // files := m.File["myFile"]
    for _, fheaders := range r.MultipartForm.File {
        for _, hdr := range fheaders {
            var infile multipart.File
            infile, err = hdr.Open()
            // defer infile.Close()
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            doc.Url = hdr.Filename
            fmt.Println(hdr.Filename)
            var outfile *os.File
            outfile, err = os.Create("./docs/" + hdr.Filename)
            // defer outfile.Close()
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            _, err = io.Copy(outfile, infile)
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }

        }
    }
    s.Ren.JSON(w, http.StatusOK, &doc)
    // http.Error(w, "hit file server", http.StatusOK)
}
英文:

I am stuck trying to figure out how to parse both a PDF document and JSON data out of one http request form from an Angularjs front end.
The request payload is

HTTP Request

Content-Disposition: form-data; name="file"; filename="Parent Handbook.pdf"
Content-Type: application/pdf

Content-Disposition: form-data; name="doc"

{"title":"test","cat":"test cat","date":20142323}

The “file” is the pdf and “doc” is the json data I want to parse.

My handler can parse and save the file just fine but fails to get anything out of the Json. Any ideas?

func (s *Server) PostFileHandler(w http.ResponseWriter, r *http.Request) {
const _24K = (1 &lt;&lt; 20) * 24
err := r.ParseMultipartForm(_24K)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
doc := Doc{}
jsonDecoder := json.NewDecoder(r.Body)
fmt.Println(r.Body)
err = jsonDecoder.Decode(&amp;doc)
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(doc.Title, doc.Url, doc.Cat, doc.Date)
doc.Id = len(docs) + 1
err = s.db.Insert(&amp;doc)
checkErr(err, &quot;Insert failed&quot;)
// files := m.File[&quot;myFile&quot;]
for _, fheaders := range r.MultipartForm.File {
for _, hdr := range fheaders {
var infile multipart.File
infile, err = hdr.Open()
// defer infile.Close()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
doc.Url = hdr.Filename
fmt.Println(hdr.Filename)
var outfile *os.File
outfile, err = os.Create(&quot;./docs/&quot; + hdr.Filename)
// defer outfile.Close()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
_, err = io.Copy(outfile, infile)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}
s.Ren.JSON(w, http.StatusOK, &amp;doc)
// http.Error(w, &quot;hit file server&quot;, http.StatusOK)
}

答案1

得分: 5

在你的示例中,你试图读取r.Body,好像它是请求中被剥离出来的PDF部分,但实际上并不是。你需要分别处理PDF和JSON两个部分。
可以使用http.(*Request).MultipartReader()来实现。

r.MultipartReader()会返回一个mime/multipart.Reader对象,因此你可以使用r.NextPart()函数迭代处理每个部分。

所以你的处理函数应该像这样:

func (s *Server) PostFileHandler(w http.ResponseWriter, r *http.Request) {
mr, err := r.MultipartReader()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
doc := Doc{}
for {
part, err := mr.NextPart()
// 这是正常情况,没有更多的部分了
if err == io.EOF {
break
}
// 一些错误
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// PDF 'file' 部分
if part.FormName() == "file" {
doc.Url = part.FileName()
fmt.Println("URL:", part.FileName())
outfile, err := os.Create("./docs/" + part.FileName())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer outfile.Close()
_, err = io.Copy(outfile, part)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
// JSON 'doc' 部分
if part.FormName() == "doc" {
jsonDecoder := json.NewDecoder(part)
err = jsonDecoder.Decode(&doc)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Println(doc.Title, doc.Url, doc.Cat, doc.Date)
}
}
doc.Id = len(docs) + 1
err = s.db.Insert(&doc)
checkErr(err, "Insert failed")
s.Ren.JSON(w, http.StatusOK, &doc)
}
英文:

In your example you're trying to read r.Body as if it was stripped out of PDF part of the request, but it isn't. You need to process separately both parts, PDF and JSON.
Use http.(*Request).MultipartReader() for that.

r.MultipartReader() will return you mime/multipart.Reader object, so you can iterate over parts with r.NextPart() function and process each part separately.

So your handler function should be like this:

func (s *Server) PostFileHandler(w http.ResponseWriter, r *http.Request) {
mr, err := r.MultipartReader()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
doc := Doc{}
for {
part, err := mr.NextPart()
// This is OK, no more parts
if err == io.EOF {
break
}
// Some error
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// PDF &#39;file&#39; part
if part.FormName() == &quot;file&quot; {
doc.Url = part.FileName()
fmt.Println(&quot;URL:&quot;, part.FileName())
outfile, err := os.Create(&quot;./docs/&quot; + part.FileName())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer outfile.Close()
_, err = io.Copy(outfile, part)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
// JSON &#39;doc&#39; part
if part.FormName() == &quot;doc&quot; {
jsonDecoder := json.NewDecoder(part)
err = jsonDecoder.Decode(&amp;doc)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Println(doc.Title, doc.Url, doc.Cat, doc.Date)
}
}
doc.Id = len(docs) + 1
err = s.db.Insert(&amp;doc)
checkErr(err, &quot;Insert failed&quot;)
s.Ren.JSON(w, http.StatusOK, &amp;doc)
}

huangapple
  • 本文由 发表于 2015年3月21日 19:08:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/29181995.html
匿名

发表评论

匿名网友

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

确定