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) }