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  }