GO语言 GOLANG 上传微信电子小票图片、GO HTTP POST 图片文件、GO 上传图片文件、multipart/form-data、image/jpeg、image/png。
GO 环境:
go version go1.21.4 windows/amd64
为了方便调试,我把http header Authorization、报文 meta json、都放在了TXT里。
package test import ( "bytes" "encoding/json" "fmt" "io" "log" person "main/models" "net/http" "os" "path/filepath" "strings" ) func TestWxXpUp() { fmt.Println(" 微信电子小票上传接口 ") // 设置代理 // os.Setenv("HTTP_PROXY", "http://127.0.0.1:8899") // os.Setenv("HTTPS_PROXY", "https://127.0.0.1:9743") // 获取当前工作目录 dir, err := os.Getwd() if err != nil { fmt.Println("无法获取当前工作目录:", err) return } boundary := "ASSDFWDFBFWEFWWDF" //可以自己设定,需要比较复杂的字符串作 // 图片文件 START picFilePath := "D:\\GO语言源码\\L1\\微信截图_20231124093553.jpg" var picBytes []byte if _, err := os.Lstat(picFilePath); err == nil { file, _ := os.Open(picFilePath) defer file.Close() picBytes, _ = io.ReadAll(file) } else { log.Fatal("文件不存在:" + picFilePath) } // 图片文件 END bodyFilePath := filepath.Join(dir, "xp_body.txt") headerAuthPath := filepath.Join(dir, "xp_header.txt") // 报文 meta json bodyBytes, err := os.ReadFile(bodyFilePath) if err != nil { fmt.Println("无法读取文件:", err) return } strBodyMeta := string(bodyBytes) fmt.Println("文件内容:" + strBodyMeta) // http header Authorization headerBytes, err := os.ReadFile(headerAuthPath) if err != nil { fmt.Println("无法读取文件:", err) return } strHeader := string(headerBytes) fmt.Println("文件内容:" + strHeader) fileContentType := "image/jpeg" pngsuffix := ".png" //用文件后缀判定图片类型,不一定准确 picFilePathLowered := strings.ToLower(picFilePath) if endsWith(picFilePathLowered, pngsuffix) { fileContentType = "image/png" } // HTTP 拼装开始 picData := "--" + boundary + "\r\n" // HTTP 文本组装 picData = picData + "Content-Disposition:form-data;name=\"meta\"\r\nContent-Type:application/json\r\n\r\n" picData = picData + string(bodyBytes) + "\r\n" picData = picData + "--" + boundary + "\r\n" // HTTP 文件组装 picData = picData + "Content-Disposition: form-data; name=\"file\"; filename=\"" + picFilePath + "\"\r\n" + "Content-Type: " + fileContentType + "\r\n\r\n" picData = picData + string(picBytes) + "\r\n" //最后的boundary 尾部会加2个- picData = picData + "--" + boundary + "--" var myUrl = "https://api.mch.weixin.qq.com/v3/marketing/shopping-receipt/shoppingreceipts" // 创建一个HTTP请求,并设置请求方法和URL req, err := http.NewRequest("POST", myUrl, strings.NewReader(picData)) if err != nil { fmt.Println("创建请求时发生错误:", err) return } // 设置请求的Content-Type头部为 req.Header.Set("Content-Type", "multipart/form-data;boundary="+boundary) req.Header.Set("Authorization", strHeader) // 设置Accept 和 User-Agent 不然也会报错 req.Header.Set("User-Agent", "MyCustomUserAgent") req.Header.Set("Accept", "application/json") // 创建一个HTTP客户端并发送请求 client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("发送请求时发生错误:", err) return } defer resp.Body.Close() // 读取响应的内容 body := &bytes.Buffer{} _, err = body.ReadFrom(resp.Body) if err != nil { fmt.Println("读取响应时发生错误:", err) return } // 打印响应的状态码和内容 fmt.Println("响应状态码:", resp.StatusCode) fmt.Println("响应内容:", body.String()) } func endsWith(str, suffix string) bool { return len(str) >= len(suffix) && str[len(str)-len(suffix):] == suffix }
控制台输出:
微信电子小票上传接口 文件body内容:{"transaction_id":"4200001992202311246726XXXX","openid":"ooIeqs39EYwsHoRh_q0GmQE_XXXX","sha256":"7a205561ba1995dac8f41851dd256731393a64db7435f75befa876c4dd3dc5c5","transaction_sub_mchid":"40082XXXX"} 文件header auth内容:WECHATPAY2-SHA256-RSA2048 mchid="21220XXXX",nonce_str="e804ba603e06410b9a04ffe5575fe385",signature="U+uBfn3xQIWmonvjvloWIupNPduKpfOut3y6YqETM7WjKxO6eCjn+pRPbT642XTiWeK7gEMsGF0V4n1ttgpd50870zUSepqbuU6jJ8TFwzE5cUQTvKs+7LJTy72XooS4wx8X+mAgwyR6nnhlRbdNAeQE+2TDVhs98juhvlGrxeuaNBavtHs/Hvp8kVcjsi/FXtNmU7ugMIS7EpSxgnF7qCf3x9MtTyzI1hvsq7oIjFcK3TY3+qDRQpcxYkiDXrB/eJmVipHbNAj+nfwMqlK3dUi0kftTIEnLkT36NKNuI+7B/Wy5hiv8dfrZZYkNVo9HYkvjO+rQRuj3QDgtlJROCQ==",timestamp="1700813854",serial_no="7B3505BC4F006C713E217C2E3FCE6ED5xxxxx" 响应状态码: 401 响应内容: {"code":"SIGN_ERROR","message":"Http头Authorization中的timestamp与发起请求的时间不得超过5分钟"}
重点:
picData = picData + "Content-Disposition: form-data; name=\"file\"; filename=\"" + picFilePath + "\"\r\n" + "Content-Type: " + fileContentType + "\r\n\r\n"
这段原生报文里,一个空格都不能多,一个引号都不能少。否则微信会报错。
先前用C#调试,老报错,尝试POSTMAN,居然是通的,把“https://api.mch.weixin.qq.com”改成 HTTP的,然后用FIDDLER 抓包。和C#程序生成的报文做对比,才发现少了些空格,把它改成POSTMAN一样的格式后,成功。
--
合集:
GO语言
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)