From 9742f0946e6dd35f9609b7fb4c6d832443407172 Mon Sep 17 00:00:00 2001 From: Sydonian <794346190@qq.com> Date: Mon, 28 Jul 2025 10:06:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8A=9F=E8=83=BD=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sdks/sdks.go | 67 ++++++++++++++++++++++++++++++++++++++++++ utils/serder/serder.go | 16 ++++++++++ 2 files changed, 83 insertions(+) diff --git a/sdks/sdks.go b/sdks/sdks.go index 2e41880..4ed201c 100644 --- a/sdks/sdks.go +++ b/sdks/sdks.go @@ -4,6 +4,8 @@ import ( "bytes" "fmt" "io" + "mime" + "mime/multipart" "net/http" "net/url" "strings" @@ -155,6 +157,50 @@ func MakeQueryParam(method string, path string, q any) *RequestParam { } } +func MakeMultipartParam(method string, path string, info any, stream io.ReadCloser) *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 + } + + fw, err := mw.CreateFormFile("file", "file") + if err != nil { + pw.CloseWithError(err) + return + } + + _, err = io.Copy(fw, stream) + if err != nil { + pw.CloseWithError(err) + return + } + }() + + 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 } @@ -200,6 +246,27 @@ func ParseCodeDataJSONResponse[T any](resp *http.Response, ret T) error { return fmt.Errorf("unknow response content type: %s, status: %d, body[:200]: %s", contType, resp.StatusCode, strCont) } +func ParseFileResponse(resp *http.Response) (string, io.ReadCloser, error) { + contType := resp.Header.Get("Content-Type") + if strings.Contains(contType, http2.ContentTypeJSON) { + var err error + var r CodeDataResponse[any] + + if err = serder.JSONToObjectStreamExRaw(resp.Body, &r); err != nil { + return "", nil, fmt.Errorf("parsing response: %w", err) + } + + return "", nil, &CodeMessageError{Code: r.Code, Message: r.Message} + } + + _, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition")) + if err != nil { + return "", nil, fmt.Errorf("parsing content disposition: %w", err) + } + + return params["filename"], resp.Body, nil +} + func IsErrorCode(err error, code string) bool { if e, ok := err.(*CodeMessageError); ok { return e.Code == code diff --git a/utils/serder/serder.go b/utils/serder/serder.go index cbc7fbb..295db19 100644 --- a/utils/serder/serder.go +++ b/utils/serder/serder.go @@ -99,6 +99,22 @@ func ObjectToJSON(obj any) ([]byte, error) { // 将对象转为JSON字符串。如果需要支持解析TypeUnion类型,则使用"Ex"结尾的同名函数。 func ObjectToJSONStream(obj any) io.ReadCloser { + pr, pw := io.Pipe() + enc := defaultAPI.NewEncoder(pw) + + go func() { + err := enc.Encode(obj) + if err != nil && err != io.EOF { + pw.CloseWithError(err) + } else { + pw.Close() + } + }() + + return pr +} + +func ObjectToJSONStreamEx(obj any) io.ReadCloser { pr, pw := io.Pipe() enc := json.NewEncoder(pw)