149 lines
4.4 KiB
Go
149 lines
4.4 KiB
Go
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)
|
||
}
|