发布二进制数据(application/octet-stream)

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

Post Binary data (application/octet-stream)

问题

我想上传一个内容类型设置为application/octet-stream的文件,并将文件的二进制数据放在请求的正文中。在golang中,我该如何做到这一点?以下是我的起始代码:

package main

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
)

func main() {
	file, err := os.Open("file.pdf")
	if err != nil {
		log.Fatalln(err)
	}

	req, err := http.NewRequest("POST", fmt.Sprintf("https://example.com/upload"), file)
	req.Header.Add("Content-Type", "application/octet-stream")

	if err != nil {
		log.Fatalln(err)
	}

	client := &http.Client{
		Transport: &http.Transport{
			DisableCompression: true,
		},
	}

	resp, err := client.Do(req)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Body.Close()

	reqDump, err := httputil.DumpRequest(req, true)
	if err != nil {
		log.Fatalln(err)
	}

	fmt.Println(reqDump)
}

以上是将文件内容以二进制形式上传的示例代码。你可以将文件路径修改为你要上传的文件路径,并根据需要修改请求的URL。

英文:

i want to upload a file with content-type set to application/octet-stream and the binary data of the File in the body of the Request. how would i do that in golang here is my starting code:

package main

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
)

func main() {
	file, err := os.Open("file.pdf")
	if err != nil {
		log.Fatalln(err)
	}

	req, err := http.NewRequest("POST", fmt.Sprintf("https://example.com/upload"), nil)
	req.Header.Add("Content-Type", "application/octet-stream")

	if err != nil {
		log.Fatalln(err)
	}

	client := &http.Client{
		Transport: &http.Transport{
			DisableCompression: true,
		},
	}

	resp, err := client.Do(req)
	if err != nil {
		log.Fatalln(err)
	}
	defer resp.Body.Close()

	reqDump, err := httputil.DumpRequest(req, true)
	if err != nil {
		log.Fatalln(err)
	}

	fmt.Println(reqDump)
}

答案1

得分: 0

我整理了一个答案,可以帮助你解决问题。所有的代码都属于main.go文件,仅供演示之用。它主要由两个部分组成:handlermain函数。让我们从handler开始。

HTTP处理程序

func HandleFile(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	fmt.Println("HandleFile")
	data, err := io.ReadAll(r.Body)
	if err != nil {
		panic(err)
	}
	defer r.Body.Close()
	w.Write(data)
}

在这里,我只依赖于你使用的内容(只使用Go标准库的httpiofmt包)。我们只需要将从HTTP请求中读取的传入负载写入响应流中。

main函数

func main() {
	r := http.NewServeMux()
	r.HandleFunc("/example", HandleFile)
	srv := http.Server{
		Addr:    ":8000",
		Handler: r,
	}

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := srv.ListenAndServe(); err != nil {
			fmt.Println(err.Error())
		}
	}()

	file, err := os.Open("file.txt")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	req, err := http.NewRequest(http.MethodPost, "http://localhost:8000/example", file)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Content-Type", "application/octet-stream")
	client := &http.Client{
		Transport: &http.Transport{
			DisableCompression: true,
		},
	}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	data, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(data))

	srv.Shutdown(context.Background())
	wg.Wait()
}

相比之下,main函数有更多的逻辑处理。让我们用列表的形式来总结一下:

  1. 创建一个HTTP服务器来处理传入的请求。我们使用HandleFile处理程序来回复对/example端点的请求。
  2. 使用sync.WaitGroup在一个单独的goroutine中运行服务器。通过这种方式,服务器可以同时运行,我们可以在程序中继续发送HTTP请求。
  3. 准备一个HTTP请求,其中包含从本地文件系统中读取的文件内容(在我们的例子中是file.txt)。我们将Content-Type标头设置为application/octet-stream,并将文件句柄传递给NewRequest函数。
  4. 发送请求并打印HTTP响应负载的内容。
  5. 关闭服务器,并使用wg.Wait方法告诉WaitGroup等待所有的goroutine完成。

最后的考虑

这里的代码肯定可以进行改进。不过,为了演示的目的,我更倾向于让它尽可能接近原始代码,以便让你更好地理解如何在HTTP服务器上发布二进制文件,以及如何在同一个程序中同时运行客户端和服务器。如果有什么不清楚的地方,请告诉我,谢谢!

英文:

I put together an answer that could help you to figure out the problem. All of the code belongs to the main.go file just for the sake of the demo. It has two main parts: the handler and the main function. Let's start with the handler.

The HTTP handler

func HandleFile(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	fmt.Println("HandleFile")
	data, err := io.ReadAll(r.Body)
	if err != nil {
		panic(err)
	}
	defer r.Body.Close()
	w.Write(data)
}

Here, I just relied on what you used (only the Go standard library: http, io, and fmt packages). We only need to write on the response stream the incoming payload we read from the HTTP request.

The main function

func main() {
	r := http.NewServeMux()
	r.HandleFunc("/example", HandleFile)
	srv := http.Server{
		Addr:    ":8000",
		Handler: r,
	}

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := srv.ListenAndServe(); err != nil {
			fmt.Println(err.Error())
		}
	}()

	file, err := os.Open("file.txt")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	req, err := http.NewRequest(http.MethodPost, "http://localhost:8000/example", file)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Content-Type", "application/octet-stream")
	client := &http.Client{
		Transport: &http.Transport{
			DisableCompression: true,
		},
	}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	data, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(data))

	srv.Shutdown(context.Background())
	wg.Wait()
}

Conversely, the main function has more logic to deal with. Let's recap them in a list:

  1. It creates an HTTP server to serve the incoming requests. We instrument it to reply to the request against the /example endpoint with the HandleFile handler we created before.
  2. We used the sync.WaitGroup to run the server in a separate goroutine. Thanks to this, the server is up and running but we can continue our program and send HTTP requests within it.
  3. We prepare an HTTP Request with the content of a file read from the local file system (in our case file.txt). We set the Content-Type header to application/octet-stream and we pass to the NewRequest function the handle to the file.
  4. We issue the request and we print the content of the HTTP response payload.
  5. We shutdown the server and tell to the WaitGroup to wait for all of the goroutines with the method wg.Wait.

Final Considerations

The code written down here could be improved for sure. Anyway, just for the sake of the demo, I preferred to let it be as close as I can to the original one to let you understand better how to post a binary file on an HTTP server and how to simultaneously run a client and server within the same program. If something is not clear, just let me know, thanks!

huangapple
  • 本文由 发表于 2023年5月9日 17:29:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76207798.html
匿名

发表评论

匿名网友

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

确定