This commit is contained in:
songjc 2025-07-02 15:40:08 +08:00
commit 2906a80c03
6 changed files with 110 additions and 50 deletions

View File

@ -3,6 +3,7 @@ package http
import (
"fmt"
"io"
"mime"
"mime/multipart"
"net/http"
"net/url"
@ -11,7 +12,9 @@ import (
"github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/common/utils/http2"
"gitlink.org.cn/cloudream/common/utils/math2"
"gitlink.org.cn/cloudream/common/utils/serder"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
@ -69,27 +72,53 @@ func (s *ObjectService) ListByIDs(ctx *gin.Context) {
ctx.JSON(http.StatusOK, types.OK(cliapi.ObjectListByIDsResp{Objects: objs}))
}
type ObjectUpload struct {
Info cliapi.ObjectUploadInfo `form:"info" binding:"required"`
Files []*multipart.FileHeader `form:"files"`
}
func (s *ObjectService) Upload(ctx *gin.Context) {
log := logger.WithField("HTTP", "Object.Upload")
var req ObjectUpload
if err := ctx.ShouldBind(&req); err != nil {
log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
contType := ctx.GetHeader("Content-Type")
mtype, params, err := mime.ParseMediaType(contType)
if err != nil {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "parse content-type: %v", err))
return
}
if mtype != http2.ContentTypeMultiPart {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "content-type %v not supported", mtype))
return
}
copyToPath := make([]clitypes.JPath, 0, len(req.Info.CopyToPath))
for _, p := range req.Info.CopyToPath {
boundary := params["boundary"]
if boundary == "" {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "missing boundary in content-type"))
return
}
mr := multipart.NewReader(ctx.Request.Body, boundary)
p, err := mr.NextPart()
if err != nil {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "read next part: %v", err))
return
}
var info cliapi.ObjectUploadInfo
err = serder.JSONToObjectStream(p, &info)
if err != nil {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "parse upload info: %v", err))
return
}
if info.PackageID == 0 {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "missing packageID in upload info"))
return
}
copyToPath := make([]clitypes.JPath, 0, len(info.CopyToPath))
for _, p := range info.CopyToPath {
copyToPath = append(copyToPath, clitypes.PathFromJcsPathString(p))
}
up, err := s.svc.Uploader.BeginUpdate(req.Info.PackageID, req.Info.Affinity, req.Info.CopyTo, copyToPath)
up, err := s.svc.Uploader.BeginUpdate(info.PackageID, info.Affinity, info.CopyTo, copyToPath)
if err != nil {
log.Warnf("begin update: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("begin update: %v", err)))
@ -98,26 +127,30 @@ func (s *ObjectService) Upload(ctx *gin.Context) {
defer up.Abort()
var pathes []string
for _, file := range req.Files {
f, err := file.Open()
for {
file, err := mr.NextPart()
if err == io.EOF {
break
}
if err != nil {
log.Warnf("open file: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("open file %v: %v", file.Filename, err)))
log.Warnf("read next part: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("read next part: %v", err)))
return
}
path, err := url.PathUnescape(file.Filename)
path, err := url.PathUnescape(file.FileName())
if err != nil {
log.Warnf("unescape filename: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("unescape filename %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("unescape filename %v: %v", file.FileName(), err)))
return
}
path = filepath.ToSlash(path)
err = up.Upload(clitypes.PathFromJcsPathString(path), f)
err = up.Upload(clitypes.PathFromJcsPathString(path), file)
if err != nil {
log.Warnf("uploading file: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("uploading file %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("uploading file %v: %v", file.FileName(), err)))
return
}
pathes = append(pathes, path)

View File

@ -5,6 +5,7 @@ import (
"archive/zip"
"fmt"
"io"
"mime"
"mime/multipart"
"net/http"
"net/url"
@ -13,6 +14,8 @@ import (
"github.com/gin-gonic/gin"
"gitlink.org.cn/cloudream/common/consts/errorcode"
"gitlink.org.cn/cloudream/common/pkgs/logger"
"gitlink.org.cn/cloudream/common/utils/http2"
"gitlink.org.cn/cloudream/common/utils/serder"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http/types"
cliapi "gitlink.org.cn/cloudream/jcs-pub/client/sdk/api/v1"
@ -106,33 +109,54 @@ func (s *PackageService) Create(ctx *gin.Context) {
}))
}
type PackageCreateUpload struct {
Info cliapi.PackageCreateUploadInfo `form:"info" binding:"required"`
Files []*multipart.FileHeader `form:"files"`
}
func (s *PackageService) CreateLoad(ctx *gin.Context) {
log := logger.WithField("HTTP", "Package.CreateUpload")
var req PackageCreateUpload
if err := ctx.ShouldBind(&req); err != nil {
log.Warnf("binding body: %s", err.Error())
ctx.JSON(http.StatusBadRequest, types.Failed(ecode.BadArgument, "missing argument or invalid argument"))
contType := ctx.GetHeader("Content-Type")
mtype, params, err := mime.ParseMediaType(contType)
if err != nil {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "parse content-type: %v", err))
return
}
if mtype != http2.ContentTypeMultiPart {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "content-type %v not supported", mtype))
return
}
if len(req.Info.CopyTo) != len(req.Info.CopyToPath) {
boundary := params["boundary"]
if boundary == "" {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "missing boundary in content-type"))
return
}
mr := multipart.NewReader(ctx.Request.Body, boundary)
p, err := mr.NextPart()
if err != nil {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "read next part: %v", err))
return
}
var info cliapi.PackageCreateUploadInfo
err = serder.JSONToObjectStream(p, &info)
if err != nil {
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "parse upload info: %v", err))
return
}
if len(info.CopyTo) != len(info.CopyToPath) {
log.Warnf("CopyTo and CopyToPath count not match")
ctx.JSON(http.StatusOK, types.Failed(ecode.BadArgument, "CopyTo and CopyToPath count not match"))
return
}
copyToPath := make([]clitypes.JPath, 0, len(req.Info.CopyToPath))
for _, p := range req.Info.CopyToPath {
copyToPath := make([]clitypes.JPath, 0, len(info.CopyToPath))
for _, p := range info.CopyToPath {
copyToPath = append(copyToPath, clitypes.PathFromJcsPathString(p))
}
up, err := s.svc.Uploader.BeginCreateUpload(req.Info.BucketID, req.Info.Name, req.Info.CopyTo, copyToPath)
up, err := s.svc.Uploader.BeginCreateUpload(info.BucketID, info.Name, info.CopyTo, copyToPath)
if err != nil {
log.Warnf("begin package create upload: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, "%v", err))
@ -141,26 +165,29 @@ func (s *PackageService) CreateLoad(ctx *gin.Context) {
defer up.Abort()
var pathes []string
for _, file := range req.Files {
f, err := file.Open()
for {
file, err := mr.NextPart()
if err == io.EOF {
break
}
if err != nil {
log.Warnf("open file: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("open file %v: %v", file.Filename, err)))
log.Warnf("read next part: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("read next part: %v", err)))
return
}
path, err := url.PathUnescape(file.Filename)
path, err := url.PathUnescape(file.FileName())
if err != nil {
log.Warnf("unescape filename: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("unescape filename %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("unescape filename %v: %v", file.FileName(), err)))
return
}
path = filepath.ToSlash(path)
err = up.Upload(clitypes.PathFromJcsPathString(path), f)
err = up.Upload(clitypes.PathFromJcsPathString(path), file)
if err != nil {
log.Warnf("uploading file: %s", err.Error())
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("uploading file %v: %v", file.Filename, err)))
ctx.JSON(http.StatusOK, types.Failed(ecode.OperationFailed, fmt.Sprintf("uploading file %v: %v", file.FileName(), err)))
return
}
pathes = append(pathes, path)

View File

@ -82,7 +82,7 @@ func (c *ObjectService) ListByIDs(req ObjectListByIDs) (*ObjectListByIDsResp, er
const ObjectUploadPath = "/object/upload"
type ObjectUpload struct {
ObjectUploadInfo
Info ObjectUploadInfo
Files UploadObjectIterator `json:"-"`
}
@ -114,7 +114,7 @@ func (c *ObjectService) Upload(req ObjectUpload) (*ObjectUploadResp, error) {
return nil, err
}
infoJSON, err := serder.ObjectToJSON(req)
infoJSON, err := serder.ObjectToJSON(req.Info)
if err != nil {
return nil, fmt.Errorf("upload info to json: %w", err)
}

View File

@ -32,7 +32,7 @@ func Test_PackageGet(t *testing.T) {
So(err, ShouldBeNil)
_, err = cli.Object().Upload(ObjectUpload{
ObjectUploadInfo: ObjectUploadInfo{
Info: ObjectUploadInfo{
PackageID: createResp.Package.PackageID,
},
Files: iterator.Array(
@ -84,7 +84,7 @@ func Test_Object(t *testing.T) {
So(err, ShouldBeNil)
_, err = cli.Object().Upload(ObjectUpload{
ObjectUploadInfo: ObjectUploadInfo{
Info: ObjectUploadInfo{
PackageID: createResp.Package.PackageID,
Affinity: stgAff,
},
@ -153,7 +153,7 @@ func Test_Storage(t *testing.T) {
So(err, ShouldBeNil)
_, err = cli.Object().Upload(ObjectUpload{
ObjectUploadInfo: ObjectUploadInfo{
Info: ObjectUploadInfo{
PackageID: createResp.Package.PackageID,
},
Files: iterator.Array(
@ -250,7 +250,7 @@ func Test_Sign(t *testing.T) {
So(err, ShouldBeNil)
_, err = cli.Object().Upload(ObjectUpload{
ObjectUploadInfo: ObjectUploadInfo{
Info: ObjectUploadInfo{
PackageID: createResp.Package.PackageID,
},
Files: iterator.Array(

View File

@ -91,7 +91,7 @@ func puto(c *cobra.Command, ctx *cmd.CommandContext, opt option, args []string)
startTime := time.Now()
_, err = ctx.Client.Object().Upload(cliapi.ObjectUpload{
ObjectUploadInfo: cliapi.ObjectUploadInfo{
Info: cliapi.ObjectUploadInfo{
PackageID: pkgID,
},
Files: iterator.Array(&cliapi.UploadingObject{

View File

@ -130,7 +130,7 @@ func putp(c *cobra.Command, ctx *cmd.CommandContext, opt option, args []string)
startTime := time.Now()
_, err = ctx.Client.Object().Upload(cliapi.ObjectUpload{
ObjectUploadInfo: cliapi.ObjectUploadInfo{
Info: cliapi.ObjectUploadInfo{
PackageID: pkgID,
},
Files: iterator.Array(&cliapi.UploadingObject{
@ -154,7 +154,7 @@ func putp(c *cobra.Command, ctx *cmd.CommandContext, opt option, args []string)
startTime := time.Now()
_, err = ctx.Client.Object().Upload(cliapi.ObjectUpload{
ObjectUploadInfo: cliapi.ObjectUploadInfo{
Info: cliapi.ObjectUploadInfo{
PackageID: pkgID,
},
Files: iter,