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

148 lines
4.7 KiB
Go
Raw Normal View History

package main
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"time"
)
// 全局配置与PHP代码保持一致
const (
// imgdd上传接口地址
imgddUploadURL = "https://imgdd.com/upload"
// 必需的Referer
imgddReferer = "https://imgdd.com/"
// 请求超时时间
imgddTimeout = 30 * time.Second
)
// ImgddUploadResponse imgdd接口返回JSON结构
// 对应PHP响应中的url和message字段
type ImgddUploadResponse struct {
URL string `json:"url"` // 上传成功返回的图片URL
Message string `json:"message"` // 上传失败返回的提示信息
}
// ImgddImgUpload imgdd图床上传函数完全对齐PHP功能
// filepath: 本地文件路径
// filename: 上传时的自定义文件名对应PHP的setPostFilename
// 返回值: 图片URL错误信息
func ImgddImgUpload(filepath string, filename string) (string, error) {
// 1. 参数校验
if filepath == "" {
return "", errors.New("本地文件路径不能为空")
}
if filename == "" {
return "", errors.New("上传文件名不能为空")
}
// 2. 打开本地文件
file, err := os.Open(filepath)
if err != nil {
return "", fmt.Errorf("打开本地文件失败: %w, 路径: %s", err, filepath)
}
defer file.Close() // 延迟关闭文件,确保资源释放
// 3. 构建multipart/form-data请求体
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
defer bodyWriter.Close() // 延迟关闭writer确保请求体完整
// 3.1 添加文件字段(字段名"image"对应PHP的'image' => $file设置自定义上传文件名
fileWriter, err := bodyWriter.CreateFormFile("image", filename)
if err != nil {
return "", fmt.Errorf("创建文件表单字段失败: %w", err)
}
// 3.2 复制文件内容到请求体
if _, err := io.Copy(fileWriter, file); err != nil {
return "", fmt.Errorf("复制文件内容到请求体失败: %w", err)
}
// 4. 创建HTTP POST请求
contentType := bodyWriter.FormDataContentType() // 自动包含boundary无需手动拼接
req, err := http.NewRequest(http.MethodPost, imgddUploadURL, bodyBuf)
if err != nil {
return "", fmt.Errorf("创建HTTP请求失败: %w", err)
}
// 5. 设置请求头与PHP代码保持一致
setImgddRequestHeaders(req, contentType)
// 6. 发送HTTP请求
client := &http.Client{
Timeout: imgddTimeout,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true, // 注意:字段名是 InsecureSkipVerify非 TLSInsecureSkipVerify
},
},
}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("发送HTTP请求失败: %w", err)
}
defer resp.Body.Close() // 延迟关闭响应体
// 7. 读取响应内容
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应内容失败: %w", err)
}
// 8. 解析JSON响应
var imgddResp ImgddUploadResponse
if err := json.Unmarshal(respBody, &imgddResp); err != nil {
return "", fmt.Errorf("解析JSON响应失败: %w, 响应内容: %s", err, string(respBody))
}
// 9. 按PHP逻辑判断响应结果
if imgddResp.URL != "" {
// 存在url字段上传成功
return imgddResp.URL, nil
} else if imgddResp.Message != "" {
// 存在message字段返回对应错误
return "", fmt.Errorf("上传失败请重试(%s", imgddResp.Message)
} else {
// 既无url也无message接口错误
return "", fmt.Errorf("上传失败!接口错误,响应内容: %s", string(respBody))
}
}
// setImgddRequestHeaders 设置imgdd必需的请求头
func setImgddRequestHeaders(req *http.Request, contentType string) {
// 设置Content-Type包含multipart boundary
req.Header.Set("Content-Type", contentType)
// 设置Referer与PHP一致接口可能做了来源校验
req.Header.Set("Referer", imgddReferer)
// 补充通用请求头,提升兼容性
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Accept", "*/*")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Accept-Encoding", "gzip, deflate, br")
}
// 示例调用
func main() {
// 配置本地文件路径和上传文件名(请替换为实际值)
localFilePath := "./test.jpg" // 本地图片实际路径
uploadFilename := "custom-img.jpg" // 上传时的自定义文件名(可包含后缀)
// 调用imgdd上传函数
imgURL, err := ImgddImgUpload(localFilePath, uploadFilename)
if err != nil {
fmt.Printf("imgdd图床上传失败: %v\n", err)
return
}
fmt.Printf("imgdd图床上传成功图片URL: %s\n", imgURL)
}