英文:
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
文件,仅供演示之用。它主要由两个部分组成:handler
和main
函数。让我们从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标准库的http
、io
和fmt
包)。我们只需要将从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
函数有更多的逻辑处理。让我们用列表的形式来总结一下:
- 创建一个HTTP服务器来处理传入的请求。我们使用
HandleFile
处理程序来回复对/example
端点的请求。 - 使用
sync.WaitGroup
在一个单独的goroutine中运行服务器。通过这种方式,服务器可以同时运行,我们可以在程序中继续发送HTTP请求。 - 准备一个HTTP请求,其中包含从本地文件系统中读取的文件内容(在我们的例子中是
file.txt
)。我们将Content-Type
标头设置为application/octet-stream
,并将文件句柄传递给NewRequest
函数。 - 发送请求并打印HTTP响应负载的内容。
- 关闭服务器,并使用
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:
- It creates an HTTP server to serve the incoming requests. We instrument it to reply to the request against the
/example
endpoint with theHandleFile
handler we created before. - 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. - We prepare an HTTP Request with the content of a file read from the local file system (in our case
file.txt
). We set theContent-Type
header toapplication/octet-stream
and we pass to theNewRequest
function the handle to the file. - We issue the request and we print the content of the HTTP response payload.
- 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!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论