Files
rootandClaude Opus 4.7 79ac89b458 Initial commit
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-11-07 10:47:18 +08:00

128 lines
3.0 KiB
Go

package uploadser
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/textproto"
"strconv"
"videoweb/app/service/configser"
"videoweb/pub/common/log"
"videoweb/pub/pubdef"
"videoweb/pub/storage/proxy"
)
const (
// 图片上传路径
imagePath = "/media/web/autopublish/image"
)
// ImageUploadResp 图片上传响应
type ImageUploadResp struct {
Code int `json:"code"`
Data ImageUploadData `json:"data"`
Hash bool `json:"hash"`
Msg string `json:"msg"`
Time string `json:"time"`
Tip string `json:"tip"`
}
type ImageUploadData struct {
Id string `json:"id"`
SourceNameFs string `json:"sourceNameFs"`
}
// ImageParam 图片上传入参
type ImageParam struct {
AppId string
Watermark int64
FileHeader *multipart.FileHeader
}
// targetDomain 获取转发目标域名
func targetDomain() string {
return proxy.DomainAddressRepair("", proxy.BucketVideoAddressV2)
}
// ForwardImage 转发图片上传
func ForwardImage(param *ImageParam) ([]byte, error) {
if param.AppId == "" {
param.AppId = strconv.Itoa(pubdef.AppId)
}
var buf bytes.Buffer
mw := multipart.NewWriter(&buf)
if err := mw.WriteField("appId", param.AppId); err != nil {
return nil, err
}
if param.Watermark == 1 {
watermarkImage := configser.GetConfigInfo().WatermarkImage
if err := mw.WriteField("watermarkImage", watermarkImage); err != nil {
return nil, err
}
}
if param.FileHeader != nil {
if err := writeFileField(mw, "file", param.FileHeader); err != nil {
return nil, err
}
}
if err := mw.Close(); err != nil {
return nil, err
}
url := targetDomain() + imagePath
return doMultipartPost(url, mw.FormDataContentType(), &buf)
}
// writeFileField 将 multipart.FileHeader 写入目标 multipart.Writer
func writeFileField(mw *multipart.Writer, fieldName string, fh *multipart.FileHeader) error {
src, err := fh.Open()
if err != nil {
return fmt.Errorf("open file: %w", err)
}
defer src.Close()
// 保留原始文件名和 Content-Type
h := make(textproto.MIMEHeader)
contentType := fh.Header.Get("Content-Type")
if contentType == "" {
contentType = "application/octet-stream"
}
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, fieldName, fh.Filename))
h.Set("Content-Type", contentType)
part, err := mw.CreatePart(h)
if err != nil {
return fmt.Errorf("create part: %w", err)
}
if _, err = io.Copy(part, src); err != nil {
return fmt.Errorf("copy file data: %w", err)
}
return nil
}
// doMultipartPost 发送 multipart/form-data 请求并返回响应 body
func doMultipartPost(url, contentType string, body io.Reader) ([]byte, error) {
req, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", contentType)
cli := &http.Client{}
resp, err := cli.Do(req)
if err != nil {
log.Error("uploadser:doMultipartPost failed", log.Any("url", url), log.E(err))
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}