diff --git a/officialaccount/material/material.go b/officialaccount/material/material.go index 0cb961f..4850cae 100644 --- a/officialaccount/material/material.go +++ b/officialaccount/material/material.go @@ -4,9 +4,11 @@ import ( "encoding/json" "errors" "fmt" - "github.com/silenceper/wechat/v2/officialaccount/context" "github.com/silenceper/wechat/v2/util" + "io" + "os" + "path" ) const ( @@ -160,8 +162,8 @@ type resAddMaterial struct { URL string `json:"url"` } -// AddMaterial 上传永久性素材(处理视频需要单独上传) -func (material *Material) AddMaterial(mediaType MediaType, filename string) (mediaID string, url string, err error) { +// AddMaterialFromReader 上传永久性素材(处理视频需要单独上传),从 io.Reader 中读取 +func (material *Material) AddMaterialFromReader(mediaType MediaType, directory string, reader io.Reader) (mediaID string, url string, err error) { if mediaType == MediaTypeVideo { err = errors.New("永久视频素材上传使用 AddVideo 方法") return @@ -174,8 +176,7 @@ func (material *Material) AddMaterial(mediaType MediaType, filename string) (med uri := fmt.Sprintf("%s?access_token=%s&type=%s", addMaterialURL, accessToken, mediaType) var response []byte - var directory = filename - response, err = util.PostFile("media", nil, "", directory, uri) + response, err = util.PostFileFromReader("media", directory, uri, reader) if err != nil { return } @@ -193,13 +194,24 @@ func (material *Material) AddMaterial(mediaType MediaType, filename string) (med return } +// AddMaterial 上传永久性素材(处理视频需要单独上传) +func (material *Material) AddMaterial(mediaType MediaType, directory string) (mediaID string, url string, err error) { + f, err := os.Open(directory) + if err != nil { + return + } + defer func() { _ = f.Close() }() + + return material.AddMaterialFromReader(mediaType, directory, f) +} + type reqVideo struct { Title string `json:"title"` Introduction string `json:"introduction"` } -// AddVideo 永久视频素材文件上传 -func (material *Material) AddVideo(filename, title, introduction string) (mediaID string, url string, err error) { +// AddVideoFromReader 永久视频素材文件上传,从 io.Reader 中读取 +func (material *Material) AddVideoFromReader(directory, title, introduction string, reader io.Reader) (mediaID string, url string, err error) { var accessToken string accessToken, err = material.GetAccessToken() if err != nil { @@ -217,12 +229,15 @@ func (material *Material) AddVideo(filename, title, introduction string) (mediaI if err != nil { return } - + // 获取文件名 + fileName := path.Base(directory) fields := []util.MultipartFormField{ { - IsFile: true, - Fieldname: "media", - Filename: filename, + IsFile: true, + Fieldname: "media", + Filename: fileName, + Directory: directory, + FileReader: reader, }, { IsFile: false, @@ -251,6 +266,17 @@ func (material *Material) AddVideo(filename, title, introduction string) (mediaI return } +// AddVideo 永久视频素材文件上传 +func (material *Material) AddVideo(directory, title, introduction string) (mediaID string, url string, err error) { + f, err := os.Open(directory) + if err != nil { + return "", "", err + } + defer func() { _ = f.Close() }() + + return material.AddVideoFromReader(directory, title, introduction, f) +} + type reqDeleteMaterial struct { MediaID string `json:"media_id"` } diff --git a/util/http.go b/util/http.go index b5d4d9c..7090a63 100644 --- a/util/http.go +++ b/util/http.go @@ -13,6 +13,7 @@ import ( "mime/multipart" "net/http" "os" + "path" "golang.org/x/crypto/pkcs12" ) @@ -150,12 +151,17 @@ func PostJSONWithRespContentType(uri string, obj interface{}) ([]byte, string, e func PostFile(fieldName string, data []byte, fileName string, directory string, uri string) ([]byte, error) { var fileContent []byte var isFile bool - // 数据为空且文件目录不为空则按文件形式上传 + // 判断 if len(data) == 0 && directory != "" { + fileName = path.Base(directory) isFile = true - } else { + } else if len(data) > 0 && fileName != "" { fileContent = data + isFile = false + } else { + return nil, fmt.Errorf("Invalid argument: required stream data and filename or only directory") } + fields := []MultipartFormField{ { IsFile: isFile, @@ -168,13 +174,30 @@ func PostFile(fieldName string, data []byte, fileName string, directory string, return PostMultipartForm(fields, uri) } +// PostFileFromReader 上传文件,从 io.Reader 中读取 +func PostFileFromReader(filedName, directory, uri string, reader io.Reader) ([]byte, error) { + // 获取文件名 + fileName := path.Base(directory) + fields := []MultipartFormField{ + { + IsFile: true, + Fieldname: filedName, + Filename: fileName, + Directory: directory, + FileReader: reader, + }, + } + return PostMultipartForm(fields, uri) +} + // MultipartFormField 保存文件或其他字段信息 type MultipartFormField struct { - IsFile bool - Fieldname string - Value []byte - Filename string - Directory string + IsFile bool + Fieldname string + Value []byte + Filename string + Directory string + FileReader io.Reader } // PostMultipartForm 上传文件或其他多个字段 @@ -187,21 +210,27 @@ func PostMultipartForm(fields []MultipartFormField, uri string) (respBody []byte for _, field := range fields { if field.IsFile { - fileWriter, e := bodyWriter.CreateFormFile(field.Fieldname, field.Directory) + fileWriter, e := bodyWriter.CreateFormFile(field.Fieldname, field.Filename) if e != nil { err = fmt.Errorf("error writing to buffer , err=%v", e) return } - fh, e := os.Open(field.Directory) - if e != nil { - err = fmt.Errorf("error opening file , err=%v", e) - return - } - defer fh.Close() - - if _, err = io.Copy(fileWriter, fh); err != nil { - return + if field.FileReader == nil { + fh, e := os.Open(field.Directory) + if e != nil { + err = fmt.Errorf("error opening file , err=%v", e) + return + } + _, err = io.Copy(fileWriter, fh) + _ = fh.Close() + if err != nil { + return + } + } else { + if _, err = io.Copy(fileWriter, field.FileReader); err != nil { + return + } } } else { partWriter, e := bodyWriter.CreateFormFile(field.Fieldname, field.Filename) @@ -226,7 +255,7 @@ func PostMultipartForm(fields []MultipartFormField, uri string) (respBody []byte } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, err + return nil, fmt.Errorf("http code error : uri=%v , statusCode=%v", uri, resp.StatusCode) } respBody, err = io.ReadAll(resp.Body) return