Initial commit: web_hghl_server
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
package uploadser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"strconv"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/pubdef"
|
||||
"videoweb/pub/storage/proxy"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultUploadUserName = "questionnaires"
|
||||
// 分片上传路径
|
||||
videoBodyPath = "/media/web/autopublish/videoBody"
|
||||
// 分片完成路径
|
||||
videoOkPath = "/media/web/autopublish/videoOk"
|
||||
// 图片上传路径
|
||||
imagePath = "/media/web/autopublish/image"
|
||||
)
|
||||
|
||||
// VideoBodyResp 视频分片上传响应
|
||||
type VideoBodyResp struct {
|
||||
Code int `json:"code"`
|
||||
Data VideoBodyData `json:"data"`
|
||||
Hash bool `json:"hash"`
|
||||
Msg string `json:"msg"`
|
||||
Time string `json:"time"`
|
||||
Tip string `json:"tip"`
|
||||
}
|
||||
|
||||
// VideoBodyData 视频分片上传响应数据
|
||||
type VideoBodyData struct {
|
||||
Id string `json:"id"`
|
||||
TaskId string `json:"taskId"`
|
||||
SourceNameFs string `json:"sourceNameFs"`
|
||||
}
|
||||
|
||||
// VideoOkResp 视频上传完成响应
|
||||
type VideoOkResp struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Tip string `json:"tip"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// VideoBodyParam 视频分片上传入参
|
||||
type VideoBodyParam struct {
|
||||
TaskId string
|
||||
Pos string
|
||||
AppId string
|
||||
UploadUserName string
|
||||
FileHeader *multipart.FileHeader // 分片文件
|
||||
}
|
||||
|
||||
// ImageParam 图片上传入参
|
||||
type ImageParam struct {
|
||||
AppId string
|
||||
FileHeader *multipart.FileHeader
|
||||
}
|
||||
|
||||
// targetDomain 获取转发目标域名
|
||||
func targetDomain() string {
|
||||
return proxy.DomainAddressRepair("", proxy.BucketVideoAddressV2)
|
||||
}
|
||||
|
||||
// ForwardVideoBody 转发视频分片上传
|
||||
func ForwardVideoBody(param *VideoBodyParam) ([]byte, error) {
|
||||
if param.AppId == "" {
|
||||
param.AppId = strconv.Itoa(pubdef.AppId)
|
||||
}
|
||||
if param.UploadUserName == "" {
|
||||
param.UploadUserName = defaultUploadUserName
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
mw := multipart.NewWriter(&buf)
|
||||
|
||||
fields := map[string]string{
|
||||
"taskId": param.TaskId,
|
||||
"pos": param.Pos,
|
||||
"appId": param.AppId,
|
||||
"uploadUserName": param.UploadUserName,
|
||||
}
|
||||
for k, v := range fields {
|
||||
if err := mw.WriteField(k, v); err != nil {
|
||||
return nil, fmt.Errorf("write field %s: %w", k, 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() + videoBodyPath
|
||||
return doMultipartPost(url, mw.FormDataContentType(), &buf)
|
||||
}
|
||||
|
||||
// ForwardVideoOk 转发视频分片完成通知
|
||||
func ForwardVideoOk(taskId string) ([]byte, error) {
|
||||
body := map[string]string{"taskId": taskId}
|
||||
jsonBytes, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url := targetDomain() + videoOkPath
|
||||
resp, err := http.Post(url, "application/json", bytes.NewReader(jsonBytes)) //nolint:noctx
|
||||
if err != nil {
|
||||
log.Error("uploadser:ForwardVideoOk failed", log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
// 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.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)
|
||||
}
|
||||
Reference in New Issue
Block a user