From 59f6887b8858b224eebb52c81be37d46ac82b7a5 Mon Sep 17 00:00:00 2001 From: Sydonian <794346190@qq.com> Date: Thu, 11 Sep 2025 15:09:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sdks/sdks.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/sdks/sdks.go b/sdks/sdks.go index 4ed201c..0033250 100644 --- a/sdks/sdks.go +++ b/sdks/sdks.go @@ -2,6 +2,7 @@ package sdks import ( "bytes" + "crypto/sha256" "fmt" "io" "mime" @@ -12,6 +13,7 @@ import ( "github.com/google/go-querystring/query" "gitlink.org.cn/cloudream/common/consts/errorcode" + "gitlink.org.cn/cloudream/common/pkgs/iterator" "gitlink.org.cn/cloudream/common/utils/http2" "gitlink.org.cn/cloudream/common/utils/io2" "gitlink.org.cn/cloudream/common/utils/serder" @@ -86,6 +88,8 @@ type RequestBody interface { Length() int64 // 将内部值变成一个流,用于发送请求 IntoStream() io.ReadCloser + // 计算Sha256哈希,如果不方便计算,则返回nil + Hash() []byte } type StringBody struct { @@ -100,6 +104,11 @@ func (s *StringBody) IntoStream() io.ReadCloser { return io.NopCloser(bytes.NewReader([]byte(s.Value))) } +func (s *StringBody) Hash() []byte { + hash := sha256.Sum256([]byte(s.Value)) + return hash[:] +} + type BytesBody struct { Value []byte } @@ -112,6 +121,11 @@ func (b *BytesBody) IntoStream() io.ReadCloser { return io.NopCloser(bytes.NewReader(b.Value)) } +func (b *BytesBody) Hash() []byte { + hash := sha256.Sum256(b.Value) + return hash[:] +} + type StreamBody struct { Stream io.ReadCloser LengthHint int64 // 长度提示,如果长度未知,可以设置为-1 @@ -125,6 +139,10 @@ func (s *StreamBody) IntoStream() io.ReadCloser { return s.Stream } +func (s *StreamBody) Hash() []byte { + return nil +} + type APIRequest interface { MakeParam() *RequestParam } @@ -157,7 +175,12 @@ func MakeQueryParam(method string, path string, q any) *RequestParam { } } -func MakeMultipartParam(method string, path string, info any, stream io.ReadCloser) *RequestParam { +type UploadFileInfo struct { + FileName string + File io.ReadCloser +} + +func MakeUploadParam(method string, path string, info any, file UploadFileInfo) *RequestParam { data, err := serder.ObjectToJSONEx(info) if err != nil { // 开发人员应该保证param是可序列化的 @@ -168,19 +191,20 @@ func MakeMultipartParam(method string, path string, info any, stream io.ReadClos mw := multipart.NewWriter(pw) go func() { defer mw.Close() + err := mw.WriteField("info", string(data)) if err != nil { pw.CloseWithError(err) return } - fw, err := mw.CreateFormFile("file", "file") + fw, err := mw.CreateFormFile("file", file.FileName) if err != nil { pw.CloseWithError(err) return } - _, err = io.Copy(fw, stream) + _, err = io.Copy(fw, file.File) if err != nil { pw.CloseWithError(err) return @@ -201,6 +225,68 @@ func MakeMultipartParam(method string, path string, info any, stream io.ReadClos } } +type MultiUploadIter = iterator.Iterator[UploadFileInfo] + +func MakeMultiUploadParam(method string, path string, info any, files MultiUploadIter) *RequestParam { + data, err := serder.ObjectToJSONEx(info) + if err != nil { + // 开发人员应该保证param是可序列化的 + panic(err) + } + + pr, pw := io.Pipe() + mw := multipart.NewWriter(pw) + + go func() { + defer mw.Close() + + err := mw.WriteField("info", string(data)) + if err != nil { + pw.CloseWithError(err) + return + } + + for { + file, err := files.MoveNext() + if err == iterator.ErrNoMoreItem { + break + } + if err != nil { + pw.CloseWithError(fmt.Errorf("opening file: %w", err)) + return + } + + w, err := mw.CreateFormFile("files", url.PathEscape(file.FileName)) + if err != nil { + file.File.Close() + pw.CloseWithError(fmt.Errorf("create form file failed, err: %w", err)) + return + } + + _, err = io.Copy(w, file.File) + if err != nil { + file.File.Close() + pw.CloseWithError(err) + return + } + file.File.Close() + } + }() + + headers := http.Header{} + headers.Set("Content-Type", fmt.Sprintf("%s;boundary=%s", http2.ContentTypeMultiPart, mw.Boundary())) + + return &RequestParam{ + Method: method, + Path: path, + Header: headers, + Body: &StreamBody{ + Stream: pr, + LengthHint: -1, + }, + } +} + type APIResponse interface { ParseResponse(resp *http.Response) error }