微信公眾號之臨時素材管理

前言:公眾號在發(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 官方是這么描述的:


微信公眾號上傳接口參數(shù)其中 media_id 官方是這么描述的

我們就可以理解為上傳的 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-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)異步寫入流示例:

如何從異步迭代器中利用pipe管道傳入可寫流

下面有段說明:

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)下載下來:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市癞蚕,隨后出現(xiàn)的幾起案子蕊爵,更是在濱河造成了極大的恐慌,老刑警劉巖桦山,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件攒射,死亡現(xiàn)場離奇詭異,居然都是意外死亡恒水,警方通過查閱死者的電腦和手機会放,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寇窑,“玉大人鸦概,你說我怎么就攤上這事∷ィ” “怎么了窗市?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長饮笛。 經(jīng)常有香客問我咨察,道長,這世上最難降的妖魔是什么福青? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任摄狱,我火速辦了婚禮,結(jié)果婚禮上无午,老公的妹妹穿的比我還像新娘媒役。我一直安慰自己,他們只是感情好宪迟,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布酣衷。 她就那樣靜靜地躺著,像睡著了一般次泽。 火紅的嫁衣襯著肌膚如雪穿仪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天意荤,我揣著相機與錄音啊片,去河邊找鬼。 笑死玖像,一個胖子當著我的面吹牛紫谷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼碴里,長吁一口氣:“原來是場噩夢啊……” “哼沈矿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咬腋,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤羹膳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后根竿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陵像,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年寇壳,在試婚紗的時候發(fā)現(xiàn)自己被綠了醒颖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡壳炎,死狀恐怖泞歉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匿辩,我是刑警寧澤腰耙,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站铲球,受9級特大地震影響挺庞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜稼病,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一选侨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧然走,春花似錦援制、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至啄巧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掌栅,已是汗流浹背秩仆。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留猾封,地道東北人澄耍。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親齐莲。 傳聞我的和親對象是個殘疾皇子痢站,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容