Files
bl/common/contrib/files/zhuanzhuan/zhuanzhuan.go

149 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package zhuanzhuan
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"time"
)
// 全局配置 - 建议通过环境变量或配置文件加载,此处保持原有结构
const (
uploadURL = "https://dsyy.zhuanzhuan.com/uploadFile"
savePath = "./uploads" // 文件保存根路径,需替换为实际路径
timeout = 30 * time.Second
defaultCT = "image/jpeg"
)
// UploadResponse 定义接口返回的JSON结构
type UploadResponse struct {
Status bool `json:"status"`
Data []UploadData `json:"data"`
}
// UploadData 定义返回数据中的单个文件信息
type UploadData struct {
URL string `json:"url"`
}
// UploadFile 上传文件到指定接口
// fileKey: 文件标识(不含后缀),接口会拼接 .jpg 查找本地文件
// 返回值: 上传成功后的文件URL错误信息
func UploadFile(fileKey string) (string, error) {
// 1. 校验参数
if fileKey == "" {
return "", fmt.Errorf("fileKey 不能为空")
}
// 2. 拼接本地文件路径
fileName := fmt.Sprintf("%s.jpg", fileKey)
localPath := filepath.Join(savePath, fileName)
// 3. 检查并打开文件
file, err := os.Open(localPath)
if err != nil {
return "", fmt.Errorf("打开本地文件失败: %w, 路径: %s", err, localPath)
}
defer file.Close() // 延迟关闭文件,确保资源释放
// 4. 构建multipart/form-data请求体
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
defer bodyWriter.Close() // 延迟关闭writer简化代码
// 4.1 添加表单字段picDirType
if err := bodyWriter.WriteField("picDirType", "1"); err != nil {
return "", fmt.Errorf("写入表单字段 picDirType 失败: %w", err)
}
// 4.2 添加文件字段
fileWriter, err := bodyWriter.CreateFormFile("uploadfiles", fileName)
if err != nil {
return "", fmt.Errorf("创建文件表单字段失败: %w", err)
}
// 复制文件内容到请求体
if _, err := io.Copy(fileWriter, file); err != nil {
return "", fmt.Errorf("复制文件内容到请求体失败: %w", err)
}
// 5. 创建HTTP POST请求
contentType := bodyWriter.FormDataContentType() // 自动包含boundary无需手动拼接
req, err := http.NewRequest(http.MethodPost, uploadURL, bodyBuf)
if err != nil {
return "", fmt.Errorf("创建HTTP请求失败: %w", err)
}
// 6. 设置请求头对齐原有Java版本请求头
setRequestHeaders(req, contentType)
// 7. 发送HTTP请求
client := &http.Client{Timeout: timeout}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("发送HTTP请求失败: %w", err)
}
defer resp.Body.Close() // 延迟关闭响应体
// 8. 读取响应内容
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应内容失败: %w", err)
}
// 9. 解析JSON响应
var uploadResp UploadResponse
if err := json.Unmarshal(respBody, &uploadResp); err != nil {
return "", fmt.Errorf("解析JSON响应失败: %w, 响应内容: %s", err, string(respBody))
}
// 10. 校验响应结果并提取URL
if err := validateUploadResponse(uploadResp, respBody); err != nil {
return "", err
}
return uploadResp.Data[0].URL, nil
}
// setRequestHeaders 设置HTTP请求头抽离为独立函数提升可读性
func setRequestHeaders(req *http.Request, contentType string) {
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Accept", "*/*")
req.Header.Set("Origin", "https://m.zhuanzhuan.com")
req.Header.Set("User-Agent", "Mozilla/5.0")
req.Header.Set("Content-Type", contentType)
req.Header.Set("Referer", "https://m.zhuanzhuan.com/ec/")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Accept-Encoding", "gzip,deflate")
}
// validateUploadResponse 校验上传响应结果,抽离为独立函数
func validateUploadResponse(resp UploadResponse, respBody []byte) error {
if !resp.Status {
return fmt.Errorf("接口返回失败状态, 响应内容: %s", string(respBody))
}
if len(resp.Data) == 0 {
return fmt.Errorf("接口返回数据为空, 响应内容: %s", string(respBody))
}
if resp.Data[0].URL == "" {
return fmt.Errorf("接口返回URL为空, 响应内容: %s", string(respBody))
}
return nil
}
// 示例调用
func main() {
// 替换为实际的文件标识(不含.jpg后缀
fileKey := "test-file-key"
url, err := UploadFile(fileKey)
if err != nil {
fmt.Printf("文件上传失败: %v\n", err)
return
}
fmt.Printf("文件上传成功访问URL: %s\n", url)
}