Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/wizjin/weixin
Browse files Browse the repository at this point in the history
  • Loading branch information
wizjin committed Feb 3, 2014
2 parents abed080 + f53ac4a commit 9cdff3b
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 12 deletions.
30 changes: 25 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,21 +101,37 @@ func Func(w weixin.ResponseWriter, r *weixin.Request) {
* `PostMusic(music)` 发送音乐消息
* `PostNews(articles)` 发送图文消息

### 下载多媒体文件
### 上传/下载多媒体文件

使用如下函数可以用来上传多媒体文件:

`UploadMediaFromFile(mediaType string, filepath string)`

示例 (用一张本地图片来返回图片消息):

```Go
func ReciveMessage(w weixin.ResponseWriter, r *weixin.Request) {
mediaId, err := w.UploadMediaFromFile(weixin.MediaTypeImage, "/my-file-path") // 上传本地文件并获取MediaID
if err != nil {
w.ReplyText("保存图片失败")
} else {
w.ReplyImage(mediaId) // 利用获取的MediaId来返回图片消息
}
}
```

使用如下函数可以用来下载多媒体文件:

`DownloadMedia(mediaId string, writer io.Writer)`
`DownloadMediaToFile(mediaId string, filepath string)`

示例 (收到一条图片消息,然后保存图片到本地文件):

```Go
func ReciveImageMessage(w weixin.ResponseWriter, r *weixin.Request) {
file, err := os.Create("/my-file-path") // 创建本地文件文件
err := w.DownloadMediaToFile(r.MediaId, "/my-file-path") // 下载文件并保存到本地
if err != nil {
w.ReplyText("保存图片失败")
} else {
w.DownloadMedia(r.MediaId, file) // 下载文件并保存到本地
w.ReplyText("保存图片成功")
}
}
Expand All @@ -134,7 +150,11 @@ This project is licensed under the MIT license, see [LICENSE](LICENSE).

## 更新日志

### Version 0.3 - upcoming
### Version 0.4 - upcoming

* 创建/换取二维码

### Version 0.3 - 2014/01/07

* 多媒体文件处理:上传/下载多媒体文件

Expand Down
114 changes: 107 additions & 7 deletions weixin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import (
"io"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"regexp"
"sort"
"time"
Expand All @@ -37,11 +40,14 @@ const (
MsgTypeEventUnsubscribe = msgEvent + "\\." + EventUnsubscribe
MsgTypeEventScan = msgEvent + "\\." + EventScan
MsgTypeEventClick = msgEvent + "\\." + EventClick

// Media type
MediaTypeImage = "image"
MediaTypeVoice = "voice"
MediaTypeVideo = "video"
MediaTypeThumb = "thumb"
// Weixin host URL
weixinHost = "https://api.weixin.qq.com/cgi-bin"
weixinFileURL = "http://file.api.weixin.qq.com/cgi-bin/media"

// Reply format
replyText = "<xml>%s<MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content></xml>"
replyImage = "<xml>%s<MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[%s]]></MediaId></Image></xml>"
Expand Down Expand Up @@ -120,6 +126,9 @@ type ResponseWriter interface {
PostMusic(music *Music) error
PostNews(articles []Article) error
// Media operator
UploadMediaFromFile(mediaType string, filepath string) (string, error)
DownloadMediaToFile(mediaId string, filepath string) error
UploadMedia(mediaType string, filename string, reader io.Reader) (string, error)
DownloadMedia(mediaId string, writer io.Writer) error
}

Expand Down Expand Up @@ -253,6 +262,7 @@ func (wx *Weixin) PostMusic(touser string, music *Music) error {
return postMessage(wx.tokenChan, &msg)
}

// Post news message
func (wx *Weixin) PostNews(touser string, articles []Article) error {
var msg struct {
ToUser string `json:"touser"`
Expand All @@ -267,6 +277,32 @@ func (wx *Weixin) PostNews(touser string, articles []Article) error {
return postMessage(wx.tokenChan, &msg)
}

// Upload media from local file
func (wx *Weixin) UploadMediaFromFile(mediaType string, fp string) (string, error) {
file, err := os.Open(fp)
if err != nil {
return "", err
}
defer file.Close()
return wx.UploadMedia(mediaType, filepath.Base(fp), file)
}

// Download media and save to local file
func (wx *Weixin) DownloadMediaToFile(mediaId string, fp string) error {
file, err := os.Create(fp)
if err != nil {
return err
}
defer file.Close()
return wx.DownloadMedia(mediaId, file)
}

// Upload media with media
func (wx *Weixin) UploadMedia(mediaType string, filename string, reader io.Reader) (string, error) {
return uploadMedia(wx.tokenChan, mediaType, filename, reader)
}

// Download media with media
func (wx *Weixin) DownloadMedia(mediaId string, writer io.Writer) error {
return downloadMedia(wx.tokenChan, mediaId, writer)
}
Expand Down Expand Up @@ -351,7 +387,7 @@ func authAccessToken(appid string, secret string) (string, time.Duration) {
if err := json.Unmarshal(body, &res); err != nil {
log.Println("Parse access token failed: ", err)
} else {
log.Printf("AuthAccessToken token=%s expires_in=%d", res.AccessToken, res.ExpiresIn)
//log.Printf("AuthAccessToken token=%s expires_in=%d", res.AccessToken, res.ExpiresIn)
return res.AccessToken, time.Duration(res.ExpiresIn * 1000 * 1000 * 1000)
}
}
Expand Down Expand Up @@ -391,7 +427,7 @@ func postMessage(c chan accessToken, msg interface{}) error {
return err
}
var result response
if err := xml.Unmarshal(reply, &result); err != nil {
if err := json.Unmarshal(reply, &result); err != nil {
return err
} else {
switch result.ErrorCode {
Expand All @@ -408,6 +444,55 @@ func postMessage(c chan accessToken, msg interface{}) error {
return errors.New("WeiXin post message too many times")
}

func uploadMedia(c chan accessToken, mediaType string, filename string, reader io.Reader) (string, error) {
reqURL := weixinFileURL + "/upload?type=" + mediaType + "&access_token="
for i := 0; i < 3; i++ {
token := <-c
if time.Since(token.expires).Seconds() < 0 {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, err := bodyWriter.CreateFormFile("filename", filename)
if err != nil {
return "", err
}
if _, err = io.Copy(fileWriter, reader); err != nil {
return "", err
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
r, err := http.Post(reqURL+token.token, contentType, bodyBuf)
if err != nil {
return "", err
}
defer r.Body.Close()
reply, err := ioutil.ReadAll(r.Body)
if err != nil {
return "", err
}
var result struct {
response
Type string `json:"type"`
MediaId string `json:"media_id"`
CreatedAt int64 `json:"created_at"`
}
err = json.Unmarshal(reply, &result)
if err != nil {
return "", err
} else {
switch result.ErrorCode {
case 0:
return result.MediaId, nil
case 42001: // access_token timeout and retry
continue
default:
return "", errors.New(fmt.Sprintf("WeiXin upload[%d]: %s", result.ErrorCode, result.ErrorMessage))
}
}
}
}
return "", errors.New("WeiXin upload media too many times")
}

func downloadMedia(c chan accessToken, mediaId string, writer io.Writer) error {
reqURL := weixinFileURL + "/get?media_id=" + mediaId + "&access_token="
for i := 0; i < 3; i++ {
Expand All @@ -427,7 +512,7 @@ func downloadMedia(c chan accessToken, mediaId string, writer io.Writer) error {
return err
}
var result response
if err := xml.Unmarshal(reply, &result); err != nil {
if err := json.Unmarshal(reply, &result); err != nil {
return err
} else {
switch result.ErrorCode {
Expand All @@ -436,7 +521,7 @@ func downloadMedia(c chan accessToken, mediaId string, writer io.Writer) error {
case 42001: // access_token timeout and retry
continue
default:
return errors.New(fmt.Sprintf("WeiXin reply[%d]: %s", result.ErrorCode, result.ErrorMessage))
return errors.New(fmt.Sprintf("WeiXin download[%d]: %s", result.ErrorCode, result.ErrorMessage))
}
}
}
Expand Down Expand Up @@ -520,7 +605,22 @@ func (w responseWriter) PostNews(articles []Article) error {
return w.wx.PostNews(w.toUserName, articles)
}

// Download media file
// Upload media from local file
func (w responseWriter) UploadMediaFromFile(mediaType string, filepath string) (string, error) {
return w.wx.UploadMediaFromFile(mediaType, filepath)
}

// Download media and save to local file
func (w responseWriter) DownloadMediaToFile(mediaId string, filepath string) error {
return w.wx.DownloadMediaToFile(mediaId, filepath)
}

// Upload media with reader
func (w responseWriter) UploadMedia(mediaType string, filename string, reader io.Reader) (string, error) {
return w.wx.UploadMedia(mediaType, filename, reader)
}

// Download media with writer
func (w responseWriter) DownloadMedia(mediaId string, writer io.Writer) error {
return w.wx.DownloadMedia(mediaId, writer)
}

0 comments on commit 9cdff3b

Please sign in to comment.