171 lines
4.3 KiB
Go
171 lines
4.3 KiB
Go
package pngcm
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/rand"
|
||
"encoding/base64"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"mime/multipart"
|
||
"net/http"
|
||
"os"
|
||
"time"
|
||
|
||
"blazing/cool"
|
||
"blazing/cool/coolfile"
|
||
|
||
"github.com/gogf/gf/v2/errors/gerror"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
)
|
||
|
||
// Pngcm 实现coolfile.Driver接口,用于远程图片上传
|
||
type Pngcm struct{}
|
||
|
||
// 远程上传接口响应结构
|
||
type uploadResponse struct {
|
||
Code int `json:"code"`
|
||
Url string `json:"url"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
// Upload 上传文件到远程服务
|
||
func (p *Pngcm) Upload(ctx g.Ctx, filepath, filename string) (string, error) {
|
||
// 检查本地文件是否存在
|
||
if _, err := os.Stat(filepath); err != nil {
|
||
return "", gerror.Newf("上传文件不存在: %s, 错误: %v", filepath, err)
|
||
}
|
||
|
||
// 生成27位随机字符串,类似PHP的Str::random(27)
|
||
randomStr, err := randString(27)
|
||
if err != nil {
|
||
return "", gerror.Newf("生成随机字符串失败: %v", err)
|
||
}
|
||
uuid := "o_" + randomStr
|
||
|
||
// 构建表单数据
|
||
var requestBody bytes.Buffer
|
||
writer := multipart.NewWriter(&requestBody)
|
||
|
||
// 添加普通表单字段
|
||
if err := writer.WriteField("name", filename); err != nil {
|
||
return "", gerror.Newf("写入name字段失败: %v", err)
|
||
}
|
||
if err := writer.WriteField("uuid", uuid); err != nil {
|
||
return "", gerror.Newf("写入uuid字段失败: %v", err)
|
||
}
|
||
if err := writer.WriteField("sign", fmt.Sprintf("%d", time.Now().Unix())); err != nil {
|
||
return "", gerror.Newf("写入sign字段失败: %v", err)
|
||
}
|
||
|
||
// 添加文件字段
|
||
file, err := os.Open(filepath)
|
||
if err != nil {
|
||
return "", gerror.Newf("打开文件失败: %v", err)
|
||
}
|
||
defer file.Close()
|
||
|
||
fileField, err := writer.CreateFormFile("file", filename)
|
||
if err != nil {
|
||
return "", gerror.Newf("创建文件表单字段失败: %v", err)
|
||
}
|
||
if _, err := io.Copy(fileField, file); err != nil {
|
||
return "", gerror.Newf("复制文件内容失败: %v", err)
|
||
}
|
||
|
||
// 完成表单构建
|
||
if err := writer.Close(); err != nil {
|
||
return "", gerror.Newf("关闭表单写入器失败: %v", err)
|
||
}
|
||
|
||
// 创建HTTP请求
|
||
req, err := http.NewRequestWithContext(ctx, "POST", "https://img.wnflb2023.com/application/upload.php", &requestBody)
|
||
if err != nil {
|
||
return "", gerror.Newf("创建请求失败: %v", err)
|
||
}
|
||
|
||
// 设置请求头
|
||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||
req.Header.Set("Referer", "https://img.wnflb2023.com/")
|
||
|
||
// 发送请求
|
||
client := &http.Client{
|
||
Timeout: 30 * time.Second, // 设置超时时间
|
||
}
|
||
resp, err := client.Do(req)
|
||
if err != nil {
|
||
return "", gerror.Newf("发送请求失败: %v", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 读取响应内容
|
||
respBody, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return "", gerror.Newf("读取响应内容失败: %v", err)
|
||
}
|
||
|
||
// 检查HTTP状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return "", gerror.Newf("接口返回非200状态码: %d, 响应内容: %s", resp.StatusCode, string(respBody))
|
||
}
|
||
|
||
// 解析JSON响应
|
||
var uploadResp uploadResponse
|
||
if err := json.Unmarshal(respBody, &uploadResp); err != nil {
|
||
return "", gerror.Newf("解析响应JSON失败: %v, 响应内容: %s", err, string(respBody))
|
||
}
|
||
|
||
// 处理业务响应
|
||
if uploadResp.Code == 200 {
|
||
return uploadResp.Url, nil
|
||
} else if uploadResp.Message != "" {
|
||
return "", gerror.Newf("上传失败请重试: %s", uploadResp.Message)
|
||
} else {
|
||
return "", gerror.Newf("上传失败!接口错误,响应内容: %s", string(respBody))
|
||
}
|
||
}
|
||
|
||
// GetMode 获取驱动模式信息
|
||
func (p *Pngcm) GetMode() (data interface{}, err error) {
|
||
data = g.MapStrStr{
|
||
"mode": cool.Config.File.Mode,
|
||
"type": "pngcm",
|
||
}
|
||
return
|
||
}
|
||
|
||
// New 创建驱动实例
|
||
func (p *Pngcm) New() coolfile.Driver {
|
||
return &Pngcm{}
|
||
}
|
||
|
||
// New 实例化驱动
|
||
func New() coolfile.Driver {
|
||
return &Pngcm{}
|
||
}
|
||
|
||
// 生成指定长度的随机字符串(字母数字混合)
|
||
func randString(length int) (string, error) {
|
||
b := make([]byte, length)
|
||
_, err := rand.Read(b)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
// 使用base64编码确保字符安全,然后截取指定长度
|
||
return base64.URLEncoding.EncodeToString(b)[:length], nil
|
||
}
|
||
|
||
func init() {
|
||
var (
|
||
err error
|
||
driverObj = New()
|
||
driverNames = g.SliceStr{"pngcm"}
|
||
)
|
||
// 注册驱动
|
||
for _, driverName := range driverNames {
|
||
if err = coolfile.Register(driverName, driverObj); err != nil {
|
||
panic(err)
|
||
}
|
||
}
|
||
}
|