// Package clients 提供一些内部接口的 http client 实现 package clients import ( "bytes" "context" "encoding/json" "io" "mime/multipart" "net/http" "go.uber.org/zap" ) type PostApiResp[T any] struct { Data T `json:"data"` } type PostFileResp struct { Url string `json:"url"` } // PublicClient 公共文件客户端 type PublicClient struct { Client *http.Client // 客户端 Logger *zap.Logger // 日志 } // PostFileBytes 上传文件 返回上传的 url 参数 bs 为文件内容 func (c PublicClient) PostFileBytes(ctx context.Context, name string, bs []byte) (string, error) { return c.PostFile(ctx, name, bytes.NewReader(bs)) } // PostFile 上传文件 返回上传的 url func (c PublicClient) PostFile(ctx context.Context, name string, r io.Reader) (string, error) { var b bytes.Buffer mw, err := c.writeMultipart(&b, name, r) if err != nil { return "", err } const u = "https://public.listensoft.net/api/uploadFile" req, err := http.NewRequestWithContext(ctx, http.MethodPost, u, &b) if err != nil { return "", err } req.Header.Set("Content-Type", mw.FormDataContentType()) c.Logger.Debug("上传文件", zap.String("url", u), zap.String("name", name)) resp, err := c.Client.Do(req) if err != nil { return "", err } defer resp.Body.Close() bs, err := io.ReadAll(resp.Body) if err != nil { return "", err } var response PostApiResp[PostFileResp] err = json.Unmarshal(bs, &response) if err != nil { return "", err } fu := response.Data.Url c.Logger.Debug("上传文件成功", zap.String("name", name), zap.String("fileUrl", fu)) return fu, nil } func (c PublicClient) writeMultipart(w io.Writer, name string, r io.Reader) (*multipart.Writer, error) { mw := multipart.NewWriter(w) err := writeFileFromReader(mw, "upFile", name, r) if err != nil { return nil, err } err = mw.WriteField("path", "/mnt/bank/banks/") if err != nil { return nil, err } err = mw.WriteField("name", name) if err != nil { return nil, err } err = mw.Close() if err != nil { return nil, err } return mw, nil }