How can I receive an uploaded file using a Golang net/http server?

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

How can I receive an uploaded file using a Golang net/http server?

问题

我正在玩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))
}

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

我认为问题的关键在于在UploadFile函数中获取请求的主体。当我运行以下cURL命令时:

curl http://localhost:8080/upload -F "fileupload=@test.txt" -vvv

我得到一个空的响应(预期的结果;我没有将其打印到ResponseWriter),但是我只在运行服务器的提示符下得到一个新的(空的)行,而不是请求主体。

我是以多部分形式发送文件的(根据使用-F而不是-d在cURL中的含义),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

在Go中,使用net/http服务器接收以多部分表单数据形式上传的文件的正确方法是什么?

英文:

I'm playing around with Mux and net/http. Lately, I'm trying to get a simple server with one endpoint to accept a file upload.

Here's the code I've got so far:

server.go

package main

import (
	&quot;fmt&quot;
	&quot;github.com/gorilla/mux&quot;
	&quot;log&quot;
	&quot;net/http&quot;
)

func main() {
	router := mux.NewRouter()
	router.
		Path(&quot;/upload&quot;).
		Methods(&quot;POST&quot;).
		HandlerFunc(UploadCsv)
	fmt.Println(&quot;Starting&quot;)
	log.Fatal(http.ListenAndServe(&quot;:8080&quot;, router))
}

endpoint.go

package main

import (
	&quot;fmt&quot;
	&quot;net/http&quot;
)

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

   	fmt.Println(r.FormValue(&quot;fileupload&quot;))
}

I think I've narrowed the issue down to actually retrieving the body from the request inside UploadFile. When I run this cURL command:

curl http://localhost:8080/upload -F &quot;fileupload=@test.txt&quot; -vvv

I get an empty response (as expected; I'm not printing to the ResponseWriter), but I just get a new (empty) line printed at the prompt where I'm running the server, instead of the request body.

I'm sending the file as multipart (AFAIK, implied by using -F rather than -d in cURL), and cURL's verbose output is showing 502 bytes sent:

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

What's the proper way to receive files uploaded as multipart form data using a net/http server in Go?

答案1

得分: 51

以下是翻译好的内容:

这是一个快速示例

func ReceiveFile(w http.ResponseWriter, r *http.Request) {
    r.ParseMultipartForm(32 << 20) // 限制最大输入长度!
    var buf bytes.Buffer
    // 在你的情况下,file 将是 fileupload
    file, header, err := r.FormFile("file")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    name := strings.Split(header.Filename, ".")
    fmt.Printf("文件名 %s\n", name[0])
    // 将文件数据复制到缓冲区
    io.Copy(&buf, file)
    // 处理文件内容...
    // 我通常会定义一个结构体并将其解组为结构体,但这只是一个示例
    contents := buf.String()
    fmt.Println(contents)
    // 如果需要再次使用缓冲区,我会重置它
    // 这样可以减少在更复杂的项目中的内存分配
    buf.Reset()
    // 做其他事情
    // 例如写入头部
    return
}

希望对你有帮助!

英文:

Here's a quick example

func ReceiveFile(w http.ResponseWriter, r *http.Request) {
	r.ParseMultipartForm(32 &lt;&lt; 20) // limit your max input length!
	var buf bytes.Buffer
    // in your case file would be fileupload
	file, header, err := r.FormFile(&quot;file&quot;)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	name := strings.Split(header.Filename, &quot;.&quot;)
	fmt.Printf(&quot;File name %s\n&quot;, name[0])
	// Copy the file data to my buffer
	io.Copy(&amp;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
}

答案2

得分: 21

你应该使用FormFile而不是FormValue

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

// 复制示例
f, err := os.OpenFile("./downloaded", os.O_WRONLY|os.O_CREATE, 0666)
defer f.Close()
io.Copy(f, file)
英文:

You should use FormFile instead of FormValue:

file, fileHeader, err := r.FormFile(&quot;fileupload&quot;)
defer file.Close()

// copy example
f, err := os.OpenFile(&quot;./downloaded&quot;, os.O_WRONLY|os.O_CREATE, 0666)
defer f.Close()
io.Copy(f, file)

答案3

得分: 11

这是我编写的一个函数,用于帮助我上传文件。你可以在这里查看完整版本。如何在Golang中上传文件

package helpers

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

// 这个函数返回保存在数据库中的文件名,或者如果出现错误则返回错误
func FileUpload(r *http.Request) (string, error) {
	// ParseMultipartForm将请求体解析为multipart/form-data
	r.ParseMultipartForm(32 << 20)

	file, handler, err := r.FormFile("file") // 从表单数据中获取文件

	if err != nil {
		return "", err
	}
	defer file.Close() // 完成后关闭文件

	// 这是我们想要存储文件的路径
	f, err := os.OpenFile("/pathToStoreFile/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)

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

	// 将文件复制到目标路径
	io.Copy(f, file)

	return handler.Filename, nil
}
英文:

Here a function i wrote to help me in uploading my files.You can check the full version here . How to upload files in golang

package helpers

import (
	&quot;io&quot;
	&quot;net/http&quot;
	&quot;os&quot;
)

// 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 &lt;&lt; 20)

	file, handler, err := r.FormFile(&quot;file&quot;) // Retrieve the file from form data
	
	if err != nil {
		return &quot;&quot;, err
	}
    defer file.Close()                       // Close the file when we finish

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

	if err != nil {
		return &quot;&quot;, err
	}

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

	return handler.Filename, nil
}

1: https://kenyaappexperts.com/blog/how-to-upload-files-in-go-step-by-step/ "How to upload files in Golang"

huangapple
  • 本文由 发表于 2016年11月19日 03:04:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/40684307.html
匿名

发表评论

匿名网友

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

确定