Browse Source

增加预签名接口

feature_wq
Sydonian 8 months ago
parent
commit
79cb15624e
6 changed files with 471 additions and 4 deletions
  1. +8
    -2
      go.mod
  2. +13
    -2
      go.sum
  3. +143
    -0
      sdks/storage/cdsapi/presigned.go
  4. +125
    -0
      sdks/storage/cdsapi/presigned_test.go
  5. +77
    -0
      utils/serder/serder.go
  6. +105
    -0
      utils/serder/serder_test.go

+ 8
- 2
go.mod View File

@@ -1,9 +1,13 @@
module gitlink.org.cn/cloudream/common module gitlink.org.cn/cloudream/common


go 1.20
go 1.22

toolchain go1.23.2


require ( require (
github.com/antonfisher/nested-logrus-formatter v1.3.1 github.com/antonfisher/nested-logrus-formatter v1.3.1
github.com/aws/aws-sdk-go-v2 v1.36.3
github.com/aws/aws-sdk-go-v2/credentials v1.17.62
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/imdario/mergo v0.3.15 github.com/imdario/mergo v0.3.15
@@ -21,14 +25,16 @@ require (
golang.org/x/exp v0.0.0-20230519143937-03e91628a987 golang.org/x/exp v0.0.0-20230519143937-03e91628a987
) )


require github.com/aws/smithy-go v1.22.2 // indirect

require ( require (
github.com/benbjohnson/clock v1.3.0 // indirect github.com/benbjohnson/clock v1.3.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-querystring v1.1.0
github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect


+ 13
- 2
go.sum View File

@@ -1,5 +1,11 @@
github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ=
github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU=
github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8=
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
@@ -15,15 +21,17 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -51,6 +59,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
github.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY= github.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY=
github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -75,6 +84,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@@ -150,6 +160,7 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 143
- 0
sdks/storage/cdsapi/presigned.go View File

@@ -0,0 +1,143 @@
package cdsapi

import (
"context"
"fmt"
"net/http"
"net/url"
"time"

v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/google/go-querystring/query"
cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
)

const (
AuthService = "jcs"
AuthRegion = "any"
)

type PresignedService struct {
*Client
accessKey string
secretKey string
}

func (c *Client) Presigned(accessKey string, secretKey string) *PresignedService {
return &PresignedService{
Client: c,
accessKey: accessKey,
secretKey: secretKey,
}
}

const PresignedObjectDownloadByPathPath = "/v1/presigned/object/downloadByPath"

type PresignedObjectDownloadByPath struct {
UserID cdssdk.UserID `form:"userID" url:"userID" binding:"required"`
PackageID cdssdk.PackageID `form:"packageID" url:"packageID" binding:"required"`
Path string `form:"path" url:"path" binding:"required"`
Offset int64 `form:"offset" url:"offset,omitempty"`
Length *int64 `form:"length" url:"length,omitempty"`
}

func (c *PresignedService) ObjectDownload(req PresignedObjectDownloadByPath, expireIn int) (string, error) {
return c.presign(req, PresignedObjectDownloadByPathPath, http.MethodGet, expireIn)
}

const PresignedObjectUploadPath = "/v1/presigned/object/upload"

type PresignedObjectUpload struct {
UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"`
PackageID cdssdk.PackageID `form:"packageID" binding:"required" url:"packageID"`
Path string `form:"path" binding:"required" url:"path"`
Affinity cdssdk.StorageID `form:"affinity" url:"affinity,omitempty"`
LoadTo []cdssdk.StorageID `form:"loadTo" url:"loadTo,omitempty"`
LoadToPath []string `form:"loadToPath" url:"loadToPath,omitempty"`
}

type PresignedObjectUploadResp struct {
Object cdssdk.Object `json:"object"`
}

func (c *PresignedService) ObjectUpload(req PresignedObjectUpload, expireIn int) (string, error) {
return c.presign(req, PresignedObjectUploadPath, http.MethodPost, expireIn)
}

const PresignedObjectNewMultipartUploadPath = "/v1/presigned/object/newMultipartUpload"

type PresignedObjectNewMultipartUpload struct {
UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"`
PackageID cdssdk.PackageID `form:"packageID" binding:"required" url:"packageID"`
Path string `form:"path" binding:"required" url:"path"`
}

type PresignedObjectNewMultipartUploadResp struct {
Object cdssdk.Object `json:"object"`
}

func (c *PresignedService) ObjectNewMultipartUpload(req PresignedObjectNewMultipartUpload, expireIn int) (string, error) {
return c.presign(req, PresignedObjectNewMultipartUploadPath, http.MethodPost, expireIn)
}

const PresignedObjectUploadPartPath = "/v1/presigned/object/uploadPart"

type PresignedObjectUploadPart struct {
UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"`
ObjectID cdssdk.ObjectID `form:"objectID" binding:"required" url:"objectID"`
Index int `form:"index" binding:"required" url:"index"`
}

type PresignedUploadPartResp struct{}

func (c *PresignedService) ObjectUploadPart(req PresignedObjectUploadPart, expireIn int) (string, error) {
return c.presign(req, PresignedObjectUploadPartPath, http.MethodPost, expireIn)
}

const PresignedObjectCompleteMultipartUploadPath = "/v1/presigned/object/completeMultipartUpload"

type PresignedObjectCompleteMultipartUpload struct {
UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"`
ObjectID cdssdk.ObjectID `form:"objectID" binding:"required" url:"objectID"`
Indexes []int `form:"indexes" binding:"required" url:"indexes"`
}

type PresignedObjectCompleteMultipartUploadResp struct {
Object cdssdk.Object `json:"object"`
}

func (c *PresignedService) ObjectCompleteMultipartUpload(req PresignedObjectCompleteMultipartUpload, expireIn int) (string, error) {
return c.presign(req, PresignedObjectCompleteMultipartUploadPath, http.MethodPost, expireIn)
}

func (c *PresignedService) presign(req any, path string, method string, expireIn int) (string, error) {
u, err := url.Parse(c.baseURL)
if err != nil {
return "", err
}
u = u.JoinPath(path)

us, err := query.Values(req)
if err != nil {
return "", err
}
us.Add("X-Expires", fmt.Sprintf("%v", expireIn))

u.RawQuery = us.Encode()

prod := credentials.NewStaticCredentialsProvider(c.accessKey, c.secretKey, "")
cred, err := prod.Retrieve(context.TODO())
if err != nil {
return "", err
}

r, err := http.NewRequest(method, u.String(), nil)
if err != nil {
return "", err
}

signer := v4.NewSigner()
signedURL, _, err := signer.PresignHTTP(context.Background(), cred, r, "", AuthService, AuthRegion, time.Now())
return signedURL, err
}

+ 125
- 0
sdks/storage/cdsapi/presigned_test.go View File

@@ -0,0 +1,125 @@
package cdsapi

import (
"testing"

. "github.com/smartystreets/goconvey/convey"
"gitlink.org.cn/cloudream/common/pkgs/types"
)

func Test_Presigned(t *testing.T) {
cli := NewClient(&Config{
URL: "http://localhost:7890",
})

Convey("下载文件", t, func() {
pre := cli.Presigned("123456", "123456")
url, err := pre.ObjectDownload(PresignedObjectDownloadByPath{
UserID: 1,
PackageID: 3,
Path: "example.java",
Offset: 1,
Length: types.Ref(int64(100)),
}, 100)
So(err, ShouldEqual, nil)
t.Logf("url: %s", url)
})

Convey("上传文件", t, func() {
pre := cli.Presigned("123456", "123456")
url, err := pre.ObjectUpload(PresignedObjectUpload{
UserID: 1,
PackageID: 3,
Path: "example.java",
}, 100)
So(err, ShouldEqual, nil)
t.Logf("url: %s", url)
})
}

func Test_PresignedObjectDownload(t *testing.T) {
cli := NewClient(&Config{
URL: "http://localhost:7890",
})

Convey("下载文件", t, func() {
pre := cli.Presigned("123456", "123456")
url, err := pre.ObjectDownload(PresignedObjectDownloadByPath{
UserID: 1,
PackageID: 3,
Path: "example.java",
// Offset: 1,
// Length: types.Ref(int64(100)),
}, 100)
So(err, ShouldEqual, nil)
t.Logf("url: %s", url)
})
}

func Test_PresignedObjectUpload(t *testing.T) {
cli := NewClient(&Config{
URL: "http://localhost:7890",
})

Convey("上传文件", t, func() {
pre := cli.Presigned("123456", "123456")
url, err := pre.ObjectUpload(PresignedObjectUpload{
UserID: 1,
PackageID: 3,
Path: "example.java",
}, 100)
So(err, ShouldEqual, nil)
t.Logf("url: %s", url)
})
}

func Test_PresignedNewMultipartUpload(t *testing.T) {
cli := NewClient(&Config{
URL: "http://localhost:7890",
})

Convey("启动分片上传", t, func() {
pre := cli.Presigned("123456", "123456")
url, err := pre.ObjectNewMultipartUpload(PresignedObjectNewMultipartUpload{
UserID: 1,
PackageID: 3,
Path: "example.java",
}, 600)
So(err, ShouldEqual, nil)
t.Logf("url: %s", url)
})
}

func Test_PresignedObjectUploadPart(t *testing.T) {
cli := NewClient(&Config{
URL: "http://localhost:7890",
})

Convey("上传分片", t, func() {
pre := cli.Presigned("123456", "123456")
url, err := pre.ObjectUploadPart(PresignedObjectUploadPart{
UserID: 1,
ObjectID: 7,
Index: 3,
}, 600)
So(err, ShouldEqual, nil)
t.Logf("url: %s", url)
})
}

func Test_PresignedCompleteMultipartUpload(t *testing.T) {
cli := NewClient(&Config{
URL: "http://localhost:7890",
})

Convey("合并分片", t, func() {
pre := cli.Presigned("123456", "123456")
url, err := pre.ObjectCompleteMultipartUpload(PresignedObjectCompleteMultipartUpload{
UserID: 1,
ObjectID: 7,
Indexes: []int{1, 2, 3},
}, 600)
So(err, ShouldEqual, nil)
t.Logf("url: %s", url)
})
}

+ 77
- 0
utils/serder/serder.go View File

@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings"


jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@@ -189,3 +190,79 @@ func ObjectToMap(obj any) (map[string]any, error) {
} }
return mp, dec.Decode(obj) return mp, dec.Decode(obj)
} }

// 1. 尝试解开所有引用
//
// 2. nil值将会是空字符串
func ObjectToMapString(obj any) (map[string]string, error) {
if obj == nil {
return make(map[string]string), nil
}

v := reflect.ValueOf(obj)
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
if !v.IsValid() {
return make(map[string]string), nil
}
if v.Kind() != reflect.Struct {
return nil, fmt.Errorf("type %v is not a struct", v.Type())
}

mp := make(map[string]string)
objectToMapString(v, mp)
return mp, nil
}

func objectToMapString(val reflect.Value, mp map[string]string) {
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
vf := val.Field(i)
tf := typ.Field(i)
if tf.Anonymous {
objectToMapString(vf, mp)
continue
}

fieldName := tf.Name

omitEmpty := false
jsonTag := tf.Tag.Get("json")
if jsonTag != "" {
tagParts := strings.Split(jsonTag, ",")
fieldName = strings.TrimSpace(tagParts[0])
if len(tagParts) > 1 {
for _, tagPart := range tagParts[1:] {
tagPart = strings.TrimSpace(tagPart)
if tagPart == "omitempty" {
omitEmpty = true
}
}
}
}

for vf.Kind() == reflect.Ptr {
vf = vf.Elem()
}

if !vf.IsValid() {
if omitEmpty {
continue
}

mp[fieldName] = ""
continue
}

if vf.IsZero() && omitEmpty {
continue
}

if vf.Kind() == reflect.Array {

}

mp[fieldName] = fmt.Sprintf("%v", vf)
}
}

+ 105
- 0
utils/serder/serder_test.go View File

@@ -694,3 +694,108 @@ func Test_ObjectToJSONEx4(t *testing.T) {
So(ret[0].(*StCallback).Value, ShouldEqual, "called") So(ret[0].(*StCallback).Value, ShouldEqual, "called")
}) })
} }

type StStringer struct {
}

func (s StStringer) String() string {
return "StStringer"
}

func Test_ObjectToMapString(t *testing.T) {
Convey("结构体", t, func() {
type StEmb struct {
IntRef *int `json:"intRef,omitempty"`
BoolRef **bool `json:"boolRef"`
StrRef *string `json:"strRef"`
}

type St struct {
StEmb
Int int
Bool bool
Str string
StStringer *StStringer
}

st := St{
StEmb: StEmb{
IntRef: types.Ref(123),
BoolRef: types.Ref(types.Ref(true)),
},
Int: 456,
Bool: false,
Str: "test",
StStringer: &StStringer{},
}

mp, err := ObjectToMapString(st)
So(err, ShouldBeNil)

So(mp["intRef"], ShouldEqual, "123")
So(mp["boolRef"], ShouldEqual, "true")
So(mp["strRef"], ShouldEqual, "")
So(mp["Int"], ShouldEqual, "456")
So(mp["Bool"], ShouldEqual, "false")
So(mp["Str"], ShouldEqual, "test")
So(mp["StStringer"], ShouldEqual, "StStringer")
})

Convey("结构体引用", t, func() {
type StEmb struct {
IntRef *int `json:"intRef,omitempty"`
BoolRef **bool `json:"boolRef"`
StrRef *string `json:"strRef"`
}

type St struct {
StEmb
Int int
Bool bool
Str string
StStringer *StStringer
}

st := St{
StEmb: StEmb{
IntRef: types.Ref(123),
BoolRef: types.Ref(types.Ref(true)),
},
Int: 456,
Bool: false,
Str: "test",
StStringer: &StStringer{},
}

mp, err := ObjectToMapString(&st)
So(err, ShouldBeNil)

So(mp["intRef"], ShouldEqual, "123")
So(mp["boolRef"], ShouldEqual, "true")
So(mp["strRef"], ShouldEqual, "")
So(mp["Int"], ShouldEqual, "456")
So(mp["Bool"], ShouldEqual, "false")
So(mp["Str"], ShouldEqual, "test")
So(mp["StStringer"], ShouldEqual, "StStringer")
})

Convey("nil", t, func() {
mp, err := ObjectToMapString(nil)
So(err, ShouldBeNil)
So(mp, ShouldResemble, map[string]string{})
})

Convey("nil指针", t, func() {
type St struct{}
var st *St
mp, err := ObjectToMapString(st)
So(err, ShouldBeNil)
So(mp, ShouldResemble, map[string]string{})
})

Convey("非结构体", t, func() {
mp, err := ObjectToMapString(123)
So(err, ShouldNotBeNil)
So(mp, ShouldBeNil)
})
}

Loading…
Cancel
Save