Files
2026-11-07 10:47:32 +08:00

132 lines
3.5 KiB
Go

package proxy
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime"
"mime/multipart"
"net/http"
"net/textproto"
"path/filepath"
"strconv"
"strings"
"time"
"videoweb/pub/common/log"
"videoweb/pub/pubdef"
)
const mediaAutopublishImagePath = "/media/web/autopublish/image"
type mediaImageUploadResp struct {
Code int `json:"code"`
Data struct {
Id string `json:"id"`
SourceNameFs string `json:"sourceNameFs"`
} `json:"data"`
Msg string `json:"msg"`
}
// UploadAutopublishImage 按 App /api/app/upload/image 同样方式上传到 media
// POST {media}/media/web/autopublish/image,表单字段 file + appId,不加盖水印。
func UploadAutopublishImage(fileName string, data []byte) (string, error) {
if len(data) == 0 {
return "", fmt.Errorf("图片内容为空")
}
fileName = sanitizeAutopublishFileName(fileName)
var buf bytes.Buffer
mw := multipart.NewWriter(&buf)
if err := mw.WriteField("appId", strconv.Itoa(pubdef.AppId)); err != nil {
return "", err
}
if err := writeAutopublishImageFile(mw, fileName, data); err != nil {
return "", err
}
if err := mw.Close(); err != nil {
return "", err
}
base := strings.TrimRight(DomainAddressRepair("", BucketVideoAddressV2), "/")
if base == "" || (!strings.HasPrefix(base, "http://") && !strings.HasPrefix(base, "https://")) {
return "", fmt.Errorf("media地址未配置")
}
url := base + mediaAutopublishImagePath
req, err := http.NewRequest(http.MethodPost, url, &buf)
if err != nil {
return "", err
}
req.Header.Set("Content-Type", mw.FormDataContentType())
cli := &http.Client{Timeout: 60 * time.Second}
resp, err := cli.Do(req)
if err != nil {
log.Error("上传media图片失败", log.Any("url", url), log.E(err))
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("media上传图片失败,状态码 %d,响应 %s", resp.StatusCode, trimRespBody(body))
}
var result mediaImageUploadResp
if err = json.Unmarshal(body, &result); err != nil {
return "", fmt.Errorf("解析media上传图片响应失败: %w,响应 %s", err, trimRespBody(body))
}
if result.Code != Success {
msg := strings.TrimSpace(result.Msg)
if msg == "" {
msg = "unknown"
}
return "", fmt.Errorf("media上传图片失败: %s,响应 %s", msg, trimRespBody(body))
}
pathStr := strings.TrimSpace(result.Data.SourceNameFs)
if pathStr == "" {
return "", fmt.Errorf("media上传图片返回空路径")
}
return pathStr, nil
}
func writeAutopublishImageFile(mw *multipart.Writer, fileName string, data []byte) error {
h := make(textproto.MIMEHeader)
contentType := mime.TypeByExtension(strings.ToLower(filepath.Ext(fileName)))
if contentType == "" {
contentType = "image/jpeg"
}
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, fileName))
h.Set("Content-Type", contentType)
part, err := mw.CreatePart(h)
if err != nil {
return err
}
_, err = part.Write(data)
return err
}
func sanitizeAutopublishFileName(fileName string) string {
fileName = filepath.Base(strings.TrimSpace(fileName))
fileName = strings.ReplaceAll(fileName, `"`, "")
if fileName == "" || fileName == "." || !strings.Contains(fileName, ".") {
return "playlet_cover.jpg"
}
for _, r := range fileName {
if r > 127 || r < 32 || r == '/' || r == '\\' {
return "playlet_cover.jpg"
}
}
return fileName
}
func trimRespBody(body []byte) string {
s := strings.TrimSpace(string(body))
if len(s) > 300 {
return s[:300]
}
return s
}