使用Golang的net/http包通过cURL表单发送文件
使用Golang的net/http包通过cURL表单发送文件 你好!
我正在尝试发起一个等效于 curl -d "file=@filename" https://xxx/upload 的 POST 请求。
我尝试了以下方法:
formData := url.Values{
"file": {"@" + filename},
}
//req, err := http.NewRequest("POST", url, strings.NewReader(form.Encode()))
resp, err := http.PostForm("https://api.anonfiles.com/upload", formData)
我认为我应该使用 CreateFormFile,但它的文档有些令人困惑。
任何帮助都将不胜感激!
更多关于使用Golang的net/http包通过cURL表单发送文件的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这取决于相关缓冲区的实现。
虽然实现通常力求以不需要在内存中保存完整内容的方式进行,除非直接从内存读取或写入内存。
更多关于使用Golang的net/http包通过cURL表单发送文件的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你好,Sean!非常感谢你的回复。
我忘记更新这个帖子了,但我最终用一个与你展示的方法类似的方式解决了问题:
mpb := bytes.NewBuffer(nil)
mw := multipart.NewWriter(mpb)
partWriter, err := mw.CreateFormFile("file", filename)
checkErr(err)
fileReader, err := os.Open(filename)
checkErr(err)
io.Copy(partWriter, fileReader)
mw.Close()
不过,我对此还有一个疑问。io.Copy 是否真的将文件内容复制到了计算机的 RAM 中?如果是的话,是否有更好的方法来上传大文件?
再次感谢你抽出时间!
你好,Vinicius,
那个 file=@filename 的写法是 curl 特有的。在 Go 中,你可能需要自己处理读取数据。我想下面的代码可以做到(或者至少能让你开始):
func POSTFile(filename string) (*http.Response, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
return POSTData(f)
}
func POSTData(r io.Reader) (*http.Response, error) {
sb := strings.Builder{}
if _, err := io.Copy(sb, r); err != nil {
return nil, err
}
formData := url.Values{
"file": []string{sb.String()},
}
return http.PostForm("https://api.anonfiles.com/upload", formData)
}
请注意,这只在你的文件是文本文件时才有效,但我想这与 curl 的 -d, --data 参数的使用方式是匹配的。
要使用Golang的net/http包发送文件,你需要使用multipart/form-data编码,而不是简单的application/x-www-form-urlencoded。以下是正确的实现方式:
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func uploadFile(url, filename string) error {
// 打开文件
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
// 创建multipart writer
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// 创建表单文件字段
part, err := writer.CreateFormFile("file", filename)
if err != nil {
return err
}
// 复制文件内容到表单
_, err = io.Copy(part, file)
if err != nil {
return err
}
// 关闭writer以完成multipart消息
err = writer.Close()
if err != nil {
return err
}
// 创建请求
req, err := http.NewRequest("POST", url, body)
if err != nil {
return err
}
// 设置Content-Type头
req.Header.Set("Content-Type", writer.FormDataContentType())
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// 读取响应
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Printf("Response: %s\n", respBody)
return nil
}
func main() {
err := uploadFile("https://api.anonfiles.com/upload", "example.txt")
if err != nil {
fmt.Printf("Error: %v\n", err)
}
}
如果你想要更简洁的写法,也可以使用http.Post:
func uploadFileSimple(url, filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, _ := writer.CreateFormFile("file", filename)
io.Copy(part, file)
writer.Close()
resp, err := http.Post(url, writer.FormDataContentType(), body)
if err != nil {
return err
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
fmt.Printf("Response: %s\n", respBody)
return nil
}
关键点:
- 使用
multipart.NewWriter创建multipart writer - 使用
CreateFormFile创建文件字段,第一个参数是表单字段名(对应cURL中的file=),第二个参数是文件名 - 必须设置正确的Content-Type头:
multipart/form-data,并包含boundary - 这完全等同于
curl -F "file=@filename"命令

