github.com/chanxuehong/wechat@v0.0.0-20230222024006-36f0325263cd/mp/media/download.go (about) 1 package media 2 3 import ( 4 "fmt" 5 "io" 6 "mime" 7 "net/http" 8 "net/url" 9 "os" 10 11 "github.com/chanxuehong/wechat/internal/debug/api" 12 "github.com/chanxuehong/wechat/internal/debug/api/retry" 13 "github.com/chanxuehong/wechat/mp/core" 14 "github.com/chanxuehong/wechat/util" 15 ) 16 17 // Download 下载多媒体到文件. 18 // 19 // 请注意, 视频文件不支持下载 20 func Download(clt *core.Client, mediaId, filepath string) (written int64, err error) { 21 file, err := os.Create(filepath) 22 if err != nil { 23 return 24 } 25 defer func() { 26 file.Close() 27 if err != nil { 28 os.Remove(filepath) 29 } 30 }() 31 32 return DownloadToWriter(clt, mediaId, file) 33 } 34 35 // DownloadToWriter 下载多媒体到 io.Writer. 36 // 37 // 请注意, 视频文件不支持下载 38 func DownloadToWriter(clt *core.Client, mediaId string, writer io.Writer) (written int64, err error) { 39 httpClient := clt.HttpClient 40 if httpClient == nil { 41 httpClient = util.DefaultMediaHttpClient 42 } 43 44 var incompleteURL = "https://api.weixin.qq.com/cgi-bin/media/get?media_id=" + url.QueryEscape(mediaId) + "&access_token=" 45 var errorResult core.Error 46 47 token, err := clt.Token() 48 if err != nil { 49 return 50 } 51 52 hasRetried := false 53 RETRY: 54 finalURL := incompleteURL + url.QueryEscape(token) 55 written, err = httpDownloadToWriter(httpClient, finalURL, writer, &errorResult) 56 if err != nil { 57 return 58 } 59 if written > 0 { 60 return 61 } 62 63 switch errorResult.ErrCode { 64 case core.ErrCodeOK: 65 return // 基本不会出现 66 case core.ErrCodeInvalidCredential, core.ErrCodeAccessTokenExpired: 67 retry.DebugPrintError(errorResult.ErrCode, errorResult.ErrMsg, token) 68 if !hasRetried { 69 hasRetried = true 70 errorResult = core.Error{} 71 if token, err = clt.RefreshToken(token); err != nil { 72 return 73 } 74 retry.DebugPrintNewToken(token) 75 goto RETRY 76 } 77 retry.DebugPrintFallthrough(token) 78 fallthrough 79 default: 80 err = &errorResult 81 return 82 } 83 } 84 85 func httpDownloadToWriter(clt *http.Client, url string, writer io.Writer, errorResult *core.Error) (written int64, err error) { 86 api.DebugPrintGetRequest(url) 87 httpResp, err := clt.Get(url) 88 if err != nil { 89 return 0, err 90 } 91 defer httpResp.Body.Close() 92 93 if httpResp.StatusCode != http.StatusOK { 94 return 0, fmt.Errorf("http.Status: %s", httpResp.Status) 95 } 96 97 ContentDisposition := httpResp.Header.Get("Content-Disposition") 98 ContentType, _, _ := mime.ParseMediaType(httpResp.Header.Get("Content-Type")) 99 if ContentDisposition != "" && ContentType != "text/plain" && ContentType != "application/json" { 100 // 返回的是媒体流 101 return io.Copy(writer, httpResp.Body) 102 } else { 103 // 返回的是错误信息 104 return 0, api.DecodeJSONHttpResponse(httpResp.Body, errorResult) 105 } 106 }