前言:公眾號在發(fā)送消息的時候可能會使用本地的多媒體文件芬迄,例如圖片、視頻等昂秃,而這些素材微信公眾號是不允許我們直接發(fā)送給用戶的禀梳,只能上傳到微信服務(wù)器上,得到 media_id 通過 media_id 去微信服務(wù)器查找素材發(fā)送給用戶肠骆,這些上傳到微信服務(wù)器的素材又分為臨時素材和永久素材算途,本次我們來介紹如何上傳臨時素材和獲取臨時素材。
書接前文:
先大致過一下官網(wǎng):新增臨時素材
一蚀腿、media_id 的特點:
- 可復(fù)用
- 媒體文件在微信后臺保存時間為 3 天嘴瓤,即 3 天后 media_id 失效。
- 需使用 https 調(diào)用接口
- 上傳臨時素材的格式莉钙、大小需要和公眾平臺官網(wǎng)要求一致
二廓脆、接口調(diào)用說明:
- 請求方式:使用 https 進行 POST 請求,提交方式為 FORM 表單方式磁玉。
- 請求接口
https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
需要三個參數(shù):
- access_token 調(diào)用接口憑證
- type 媒體文件類型例如圖片(image)
- media_id form 表單提交時候攜帶的參數(shù)
三停忿、返回說明
正確情況下的返回 JSON數(shù) 據(jù)包結(jié)果如下:
{"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789}
- type 媒體文件類型
- media_id 媒體文件上傳后,獲取標識(有用)
- created_at 媒體文件上傳時間戳
四蚊伞、封裝上傳臨時素材
首先我們請求的接口里面有用到 access_token 那么我們最好把這個函數(shù)放到之前封裝的 WeChat 這個類里面席赂,方便得到 access_token吮铭。
觀察請求 URL,我們發(fā)現(xiàn)大多數(shù)請求的 URL 前綴都是相同的颅停,為了便于管理沐兵,比如萬一微信改域名了,我們方便改 URL 便监,需要把前綴單獨提出來扎谎,在 utils 文件下的 api.js 接口文件新增上傳素材接口和得到素材接口代碼如下:
//地址前綴
const prefix = 'https://api.weixin.qq.com/cgi-bin/';
module.exports = {
accessToken: `${prefix}token?grant_type=client_credential`,
ticket: `${prefix}ticket/getticket?type=jsapi`,
temporary: {
upload:`${prefix}media/upload?`,
get:`${prefix}media/get?`
}
}
在 WeChat.js 文件新增上傳接口:
//引入路徑
const { resolve } = require("path");
//引入fs模塊
const { createReadStream , createWriteStream } = require("fs");
//引入request模塊
const request = require("request");
//以上幾個是多引用的模塊
//上傳臨時素材
uploadTemporaryMaterial(type, fileName) {
//獲取文件的絕對路徑
const filePath = resolve(__dirname, '../media', fileName);
return new Promise(async (resolve, reject) => {
try { //放置可能出錯的代碼
//獲取access_token
const data = await this.fetchAccessToken();
//定義請求地址
const url = `${api.temporary.upload}access_token=${data.access_token}&type=${type}`;
const formData = {
media: createReadStream(filePath)
}
//以form表單的方式發(fā)送請求
const result = rp({ method: 'POST', url, json: true, formData })
//將數(shù)據(jù)返回給用戶
resolve(result);
} catch (e) {
//一旦try中的代碼出了問題,就會走catch邏輯烧董,處理錯誤
reject('uploadTemporaryMaterial方法出了問題:' + e);
}
})
}
uploadTemporaryMaterial 函數(shù)講解:
- 函數(shù)接收兩個參數(shù)毁靶,type 上傳文件類型,fileName 上傳文件名逊移。
- 根據(jù) fileName 去 media 文件夾找到上傳文件的絕對路徑预吆。
- 難點之一就是使用 request-promise-native 攜帶 media_id 發(fā)送 form 請求,一起研究下胳泉。
首先我們?nèi)?npm 查看官方文檔:
翻譯紅框的內(nèi)容:
這個包和另一個 npm 包 request-promise 很像拐叉,不同的是 request-promise-native 使用的是原生 ES6 語法構(gòu)建, request-promise 使用的是 ES6 的 polyfill 來構(gòu)建的扇商。使用請去參考 request-promise 的說明文檔凤瘦, request-promise 的使用方法一切適用于 request-promise-native。
很明顯 request-promise 和 request-promise-native 功能都是一樣的案铺,就是構(gòu)建各自的語法稍有不同蔬芥,我們就聽官方的去看 request-promise 的文檔去找 form 表單提交方式。
來到 request-promise 的官網(wǎng)控汉,首先我們的知道能夠上傳文件的只有表單笔诵。無論是法form 表單還是虛擬表單都可以。接下來看官網(wǎng)這句話:
If you want to include a file upload then use options.formData
如果你想上傳文件請使用 formData姑子,其實就是表單上傳乎婿。
我們只要看下 formData 如何配置的,主要字段的 name 和 file 其實對應(yīng)的是 input 標簽里面的 name 的屬性值街佑。第一個是上傳文本的格式谢翎,第二個才是表單上傳文件。文件的值比較特殊使用的是 NodeJS 的創(chuàng)建文件可讀流
createReadStream
舆乔。options 里面配置文件名和文件格式岳服,可省略,上傳多媒體文件希俩,很明顯我們用第二個吊宋。
formData: {
// Like <input type="text" name="name">
name: 'Jenn',
// Like <input type="file" name="file">
file: {
value: fs.createReadStream('test/test.jpg'),
options: {
filename: 'test.jpg',
contentType: 'image/jpg'
}
}
}
微信公眾號上傳接口參數(shù)其中 media 官方是這么描述的:
我們就可以理解為上傳的 input 標簽的 name 值為 media。所以上傳代碼封裝如上面匯總案例區(qū)。
然后進行測試璃搜,在 media 文件夾里放入需要上傳的文件拖吼,我們測試上傳一個 test.png 圖片,接下來去運行程序这吻,在 Wechat.js 里面:
//先實例化
const wx = new Wechat();
//調(diào)用臨時素材上傳函數(shù)
wx.uploadTemporaryMaterial('image',"test.png").then(res=>console.log(res));
node 運行程序:
C:\Users\lenovo\Desktop\微信公眾號開發(fā)\WeChat\wechat>node Wechat.js
文件讀取成功~
文件保存成功~
{
type: 'image',
media_id: 'm6pUnsPg6aIMmlRNSuWvWa_Gtthg0fl2oiU1B5Eqp3amPA2fe21iGWluwU0Tu0o_',
created_at: 1578315227,
item: []
}
看到 media_id 了表示成功上傳了吊档。如何把這個上傳的文件保存下來就是下來我們要做的事情。
五唾糯、封裝獲取臨時素材接口
這個接口需要注意:視頻文件不支持 https 下載怠硼,調(diào)用該接口需 http 協(xié)議。
接口請求方式:GET,https
調(diào)用移怯。
請求接口:
https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
請求接口攜帶的兩個參數(shù):
- access_token 調(diào)用接口憑證香璃。
- media_id 上傳素材時得到的媒體文件 ID。
在 Wxchat.js 里面新增:
//獲取臨時素材
getTemporaryMaterial(type, mediaId, fileName) {
//獲取文件的絕對路徑
const filePath = resolve(__dirname, '../media', fileName);
return new Promise(async (resolve, reject) => {
//獲取access_token
const data = await this.fetchAccessToken();
//定義請求地址
let url = `${api.temporary.get}access_token=${data.access_token}&media_id=${mediaId}`;
//判斷是否是視頻文件
if (type === 'video') {
//視頻文件只支持http協(xié)議
url = url.replace('https://', 'http://');
//發(fā)送請求
const data = await rp({ method: 'GET', url, json: true });
//返回出去
resolve(data);
} else {
//其他類型文件
request(url)
.pipe(createWriteStream(filePath))
.once('close', resolve) //當文件讀取完畢時舟误,可讀流會自動關(guān)閉葡秒,一旦關(guān)閉觸發(fā)close事件,從而調(diào)用resolve方法通知外部文件讀取完畢了
}
});
}
這個函數(shù)需要注意兩點:
- 保存文件嵌溢,視頻文件使用的是 http 請求眯牧,所以條件判斷,再利用正則替換下 URL 就可以了赖草。
-
第一個難點是使用 request-promise-native 發(fā)送 formData 学少,現(xiàn)在第二個難點就是使用 request-promise-native 來接收 stream 流式文件。接收流式文件我們在 request-promise-native 官網(wǎng)查到下面這段話:
大致意思就是講:request-promise-native 和 request API 是一樣的疚顷,只不過 request-promise-native 實現(xiàn)了使用 ES6 的 promise 來調(diào)取接口旱易。另外,不建議使用流式響應(yīng)(例如.pipe(...))腿堤,因為對于大請求,請求承諾會增加不必要的內(nèi)存占用如暖。為此使用原始的請求庫來接收流式文件笆檀。
然后為了接收流式文件,同時為了不增加不必要的內(nèi)存占用盒至,我們?nèi)?request 官網(wǎng)去查看流式文件如何接收的酗洒,走起。
官網(wǎng)上找到的使用示例為:
request('http://google.com/doodle.png').pipe(fs.createWriteStream('ddle.png'))
直接請求鏈接創(chuàng)建可寫流放到本地枷遂,還有一句話引起了我的注意:
And since pipe() returns the destination stream in ≥ Node 0.5.x you can do one line proxying.
pipe()
方法返回的目標流符合 NodeJs(符合大于 0.5.x 的版本)的 pipe()
函數(shù)所以樱衷,可以使用 NodeJS 里面 pipe 的方法。接下來就去 NodeJS 官網(wǎng)查看 stream 如何進行異步流的寫入了酒唉,找到官網(wǎng)異步寫入流示例:
下面有段說明:
To ensure completion of the write stream without errors, it is safer to use the finished() method as above, instead of using the once() listener for the 'finish' event.
為了確保流完全無錯誤寫入矩桂,請使用 finished() 方法來終止程序,而不是使用 once 來替代 finish 事件痪伦。
pipe()
方法執(zhí)行完可以返回侄榴。
接下來去試著運行程序雹锣,首先復(fù)制我們上面得到的 media_id 在下面函數(shù)里面使用用來得到素材:
wx.getTemporaryMaterial('image',"m6pUnsPg6aIMmlRNSuWvWa_Gtthg0fl2oiU1B5Eqp3amPA2fe21iGWluwU0Tu0o_","gettest.png").then(()=>console.log("成功下載圖片"));
最后 node 運行程序:
C:\Users\lenovo\Desktop\微信公眾號開發(fā)\WeChat\wechat>node Wechat.js
文件讀取成功~
成功下載圖片
這時候去 media 文件夾里面看看圖片是否已經(jīng)下載下來: