如何使用Golang net/http服务器接收上传的文件?

use*_*784 27 multipartform-data go httpserver

我正在玩Muxnet/http.最近,我试图获得一个带有一个端点的简单服务器来接受文件上传.

这是我到目前为止的代码:

server.go

package main

import (
    "fmt"
    "github.com/gorilla/mux"
    "log"
    "net/http"
)

func main() {
    router := mux.NewRouter()
    router.
        Path("/upload").
        Methods("POST").
        HandlerFunc(UploadCsv)
    fmt.Println("Starting")
    log.Fatal(http.ListenAndServe(":8080", router))
}
Run Code Online (Sandbox Code Playgroud)

endpoint.go

package main

import (
    "fmt"
    "net/http"
)

func UploadFile(w http.ResponseWriter, r *http.Request) {
    err := r.ParseMultipartForm(5 * 1024 * 1024)
    if err != nil {
        panic(err)
    }

    fmt.Println(r.FormValue("fileupload"))
}
Run Code Online (Sandbox Code Playgroud)

我想我已经把问题缩小到实际从里面的请求中检索身体了UploadFile.当我运行此cURL命令时:

curl http://localhost:8080/upload -F "fileupload=@test.txt" -vvv
Run Code Online (Sandbox Code Playgroud)

我得到一个空响应(正如预期的那样;我不打印到ResponseWriter),但我只是在我运行服务器的提示符处打印了一个新的(空)行,而不是请求正文.

我将文件作为multipart发送(AFAIK,暗示使用-F而不是-dcURL),cURL的详细输出显示502字节发送:

$ curl http://localhost:8080/upload -F "fileupload=@test.txt" -vvv
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> POST /upload HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.51.0
> Accept: */*
> Content-Length: 520
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------b578878d86779dc5
> 
< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Date: Fri, 18 Nov 2016 19:01:50 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
< 
* Curl_http_done: called premature == 0
* Connection #0 to host localhost left intact
Run Code Online (Sandbox Code Playgroud)

使用net/httpGo中的服务器接收作为多部分表单数据上载的文件的正确方法是什么?

ret*_*oot 24

这是一个简单的例子

func ReceiveFile(w http.ResponseWriter, r *http.Request) {
    var Buf bytes.Buffer
    // in your case file would be fileupload
    file, header, err := r.FormFile("file")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    name := strings.Split(header.Filename, ".")
    fmt.Printf("File name %s\n", name[0])
    // Copy the file data to my buffer
    io.Copy(&Buf, file)
    // do something with the contents...
    // I normally have a struct defined and unmarshal into a struct, but this will
    // work as an example
    contents := Buf.String()
    fmt.Println(contents)
    // I reset the buffer in case I want to use it again
    // reduces memory allocations in more intense projects
    Buf.Reset()
    // do something else
    // etc write header
    return
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,为了避免内存消耗,您可以直接在`io.Copy(&Buf,file)`上写入ondisk,其中Buf将替换为File处理程序. (3认同)

Ale*_*tau 11

您应该使用FormFile而不是FormValue:

file, handler, err := r.FormFile("fileupload")
defer file.Close()

// copy example
f, err := os.OpenFile("./downloaded", os.O_WRONLY|os.O_CREATE, 0666)
defer f.Close()
io.Copy(f, file)
Run Code Online (Sandbox Code Playgroud)

  • `“ ./test /” + handler.Filename` –通常来说,写入具有可由外部输入控制的路径的文件是一个非常糟糕的主意。不要这样 (2认同)

kei*_*ira 10

这是我写的一个函数来帮助我上传我的文件。你可以在这里查看完整版本。golang如何上传文件

package helpers

import (
    "io"
    "net/http"
    "os"
)

// This function returns the filename(to save in database) of the saved file
// or an error if it occurs
func FileUpload(r *http.Request) (string, error) {
    // ParseMultipartForm parses a request body as multipart/form-data
    r.ParseMultipartForm(32 << 20)

    file, handler, err := r.FormFile("file") // Retrieve the file from form data

    if err != nil {
        return "", err
    }
    defer file.Close()                       // Close the file when we finish

    // This is path which we want to store the file
    f, err := os.OpenFile("/pathToStoreFile/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)

    if err != nil {
        return "", err
    }

    // Copy the file to the destination path
    io.Copy(f, file)

    return handler.Filename, nil
}
Run Code Online (Sandbox Code Playgroud)