0
点赞
收藏
分享

微信扫一扫

微信公众号之临时素材管理

前言:公众号在发送消息的时候可能会使用本地的多媒体文件,例如图片、视频等,而这些素材微信公众号是不允许我们直接发送给用户的,只能上传到微信服务器上,得到 media_id 通过 media_id 去微信服务器查找素材发送给用户,这些上传到微信服务器的素材又分为临时素材和永久素材,本次我们来介绍如何上传临时素材和获取临时素材。

书接前文:

先大致过一下官网:新增临时素材

一、media_id 的特点:
  • 可复用
  • 媒体文件在微信后台保存时间为 3 天,即 3 天后 media_id 失效。
  • 需使用 https 调用接口
  • 上传临时素材的格式、大小需要和公众平台官网要求一致
二、接口调用说明:
  • 请求方式:使用 https 进行 POST 请求,提交方式为 FORM 表单方式。
  • 请求接口
https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE

需要三个参数:

  • access_token 调用接口凭证
  • type 媒体文件类型例如图片(image)
  • media_id form 表单提交时候携带的参数
三、返回说明

正确情况下的返回 JSON数 据包结果如下:

{"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789}
  • type 媒体文件类型
  • media_id 媒体文件上传后,获取标识(有用)
  • created_at 媒体文件上传时间戳
四、封装上传临时素材

首先我们请求的接口里面有用到 access_token 那么我们最好把这个函数放到之前封装的 WeChat 这个类里面,方便得到 access_token。

观察请求 URL,我们发现大多数请求的 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表单的方式发送请求
            const result = rp({ method: 'POST', url, json: true, formData })
            //将数据返回给用户
            resolve(result);
        } catch (e) {
            //一旦try中的代码出了问题,就会走catch逻辑,处理错误
            reject('uploadTemporaryMaterial方法出了问题:' + e);
        }

    })
}

uploadTemporaryMaterial 函数讲解:

  • 函数接收两个参数,type 上传文件类型,fileName 上传文件名。
  • 根据 fileName 去 media 文件夹找到上传文件的绝对路径。
  • 难点之一就是使用 request-promise-native 携带 media_id 发送 form 请求,一起研究下。
    首先我们去 npm 查看官方文档:


    翻译红框的内容:

很明显 request-promise 和 request-promise-native 功能都是一样的,就是构建各自的语法稍有不同,我们就听官方的去看 request-promise 的文档去找 form 表单提交方式。

来到 request-promise 的官网,首先我们的知道能够上传文件的只有表单。无论是法form 表单还是虚拟表单都可以。接下来看官网这句话:


我们只要看下 formData 如何配置的,主要字段的 name 和 file 其实对应的是 input 标签里面的 name 的属性值。第一个是上传文本的格式,第二个才是表单上传文件。文件的值比较特殊使用的是 NodeJS 的创建文件可读流 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'
        }
    }
}

微信公众号上传接口参数其中 media 官方是这么描述的:



我们就可以理解为上传的 input 标签的 name 值为 media。所以上传代码封装如上面汇总案例区。

然后进行测试,在 media 文件夹里放入需要上传的文件,我们测试上传一个 test.png 图片,接下来去运行程序,在 Wechat.js 里面:

//先实例化
const wx = new Wechat();
//调用临时素材上传函数
wx.uploadTemporaryMaterial('image',"test.png").then(res=>console.log(res));

node 运行程序:

C:\Users\lenovo\Desktop\微信公众号开发\WeChat\wechat>node Wechat.js
文件读取成功~
文件保存成功~
{
  type: 'image',
  media_id: 'm6pUnsPg6aIMmlRNSuWvWa_Gtthg0fl2oiU1B5Eqp3amPA2fe21iGWluwU0Tu0o_',
  created_at: 1578315227,
  item: []
}

看到 media_id 了表示成功上传了。如何把这个上传的文件保存下来就是下来我们要做的事情。

五、封装获取临时素材接口

这个接口需要注意:视频文件不支持 https 下载,调用该接口需 http 协议。

接口请求方式:GET,https 调用。
请求接口:

https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID

请求接口携带的两个参数:

  • access_token 调用接口凭证。
  • 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协议
            url = url.replace('https://', 'http://');
            //发送请求
            const data = await rp({ method: 'GET', url, json: true });
            //返回出去
            resolve(data);
        } else {
            //其他类型文件
            request(url)
                .pipe(createWriteStream(filePath))
                .once('close', resolve) //当文件读取完毕时,可读流会自动关闭,一旦关闭触发close事件,从而调用resolve方法通知外部文件读取完毕了
        }
    });
}

这个函数需要注意两点:

  • 保存文件,视频文件使用的是 http 请求,所以条件判断,再利用正则替换下 URL 就可以了。
  • 第一个难点是使用 request-promise-native 发送 formData ,现在第二个难点就是使用 request-promise-native 来接收 stream 流式文件。接收流式文件我们在 request-promise-native 官网查到下面这段话:


然后为了接收流式文件,同时为了不增加不必要的内存占用,我们去 request 官网去查看流式文件如何接收的,走起。

官网上找到的使用示例为:

request('http://google.com/doodle.png').pipe(fs.createWriteStream('ddle.png'))

直接请求链接创建可写流放到本地,还有一句话引起了我的注意:

And since pipe() returns the destination stream in ≥ Node 0.5.x you can do one line proxying. 

pipe() 方法返回的目标流符合 NodeJs(符合大于 0.5.x 的版本)的 pipe()函数所以,可以使用 NodeJS 里面 pipe 的方法。接下来就去 NodeJS 官网查看 stream 如何进行异步流的写入了,找到官网异步写入流示例:


下面有段说明:

pipe() 方法执行完可以返回。

接下来去试着运行程序,首先复制我们上面得到的 media_id 在下面函数里面使用用来得到素材:

wx.getTemporaryMaterial('image',"m6pUnsPg6aIMmlRNSuWvWa_Gtthg0fl2oiU1B5Eqp3amPA2fe21iGWluwU0Tu0o_","gettest.png").then(()=>console.log("成功下载图片"));

最后 node 运行程序:

C:\Users\lenovo\Desktop\微信公众号开发\WeChat\wechat>node Wechat.js
文件读取成功~
成功下载图片

这时候去 media 文件夹里面看看图片是否已经下载下来:


举报

相关推荐

0 条评论