【微信公众号】如何上传客服头像

1,526 阅读3分钟

【微信公众号】如何上传客服头像

1. 前言

哎呦,刚刚被恶心坏了,用了一个上午+一个下午的时间来找问题。卡了我一天的时间。看看下面的文档吧。

image-20230316150139119.png

只能说这个文档去除大部分的示例,留下一点点curl请求。不能说有用吧,只能说有一点点指导作用。

2. 构建请求

从刚开看这文档来气,到最后麻木,只用了一天。行吧,我们来回顾整个过程。因为我用的技术栈是EggJs,所以顺理成章的用了curl这个方法,把我给坑坏了。来看看v1版本的代码:

 // project_path/app/service/customer.js
 const Service = require('egg').Service;
 const fse = require('fs-extra');
 const path = require('path');
 class CustomerService extends Service {
      /**
      * 上传客服头像
      * @param {string} account 客服账号
      * @returns 成功或失败原因
      */
      async uploadAvatarAccount(account) {
         const {ctx} = this;
         const {ERR_MAP} = ctx.app.config;
         try {
             const token_result = await ctx.service.wx.getAssessToken();
             if (token_result.errCode) return token_result;            
             const avatar = await fse.readFile(path.resolve(__dirname,'./../static/avatar-2.jpg'));
              //看这个请求方式
              const result = await ctx.app.curl(`https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=${token_result}&kf_account=${account}@wx_official_id`,{
                 method:'post',
                 contentType:'form',
                 dataType:'json',
                 data:{
                     media:file
                 }
             });
             if (result.status !== 200) {
                 ctx.logger.warn(`【客服账号上传头像错误】${JSON.stringify(result,null,2)}`);
                 return ERR_MAP.REQUEST;
             }
             const {errcode} = result.data;
             if (errcode !== 0) {
                 ctx.logger.warn(`【客服账号上传头像错误】错误码:${JSON.stringify(result.data,null,2)}`);
                 return ERR_MAP.REQUEST;
             }
             return 'success';
         } catch(e) {
             ctx.logger.warn(`【客服账号上传头像错误】${e}`);
             return ERR_MAP.REQUEST;
         }
     }
 }
 module.exports = CustomerService;

正如上面代码所示,出来的请求结果是这个:

 {
     errcode: 41005,
     errmsg: 'media data missing hint: [ehHb1Aore-a6KNRA] rid: 6412c0b6-391d3558-15b56dc7'
 }

没有上传成功,看了下Eggjs的HttpClient的文档,原来是因为data不能作为文件上传的参数,而是要使用files参数,行吧……

3. 上传文件的尝试

来看看v2的版本吧……

 // project_path/app/service/customer.js
 const Service = require('egg').Service;
 const fse = require('fs-extra');
 const path = require('path');
 class CustomerService extends Service {
      /**
      * 上传客服头像
      * @param {string} account 客服账号
      * @returns 成功或失败原因
      */
      async uploadAvatarAccount(account) {
         const {ctx} = this;
         const {ERR_MAP} = ctx.app.config;
         try {
             const token_result = await ctx.service.wx.getAssessToken();
             if (token_result.errCode) return token_result;            
             const avatar = await fse.readFile(path.resolve(__dirname,'./../static/avatar-2.jpg'));
              //看这个请求方式
              const result = await ctx.app.curl(`https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=${token_result}&kf_account=${account}@wx_official_id`,{
                 method:'post',
                 contentType:'form',
                 dataType:'json',
 +               files:file,
 -               data:{
 -                   media:file
 -               }
             });
             if (result.status !== 200) {
                 ctx.logger.warn(`【客服账号上传头像错误】${JSON.stringify(result,null,2)}`);
                 return ERR_MAP.REQUEST;
             }
             const {errcode} = result.data;
             if (errcode !== 0) {
                 ctx.logger.warn(`【客服账号上传头像错误】错误码:${JSON.stringify(result.data,null,2)}`);
                 return ERR_MAP.REQUEST;
             }
             return 'success';
         } catch(e) {
             ctx.logger.warn(`【客服账号上传头像错误】${e}`);
             return ERR_MAP.REQUEST;
         }
     }
 }
 module.exports = CustomerService;

请求一下,发现出现下面的问题:

 {
     errcode: 40005,
     errmsg: 'invalid file type hint: [IhHb2uHae-bexQta] rid: 6412c2b8-0b7ecfa4-2ac30cab'
   },

非法的文件类型?搜索了下微信公众号官方的社区,可能是文件名和上传的文件名不同的原因。我在想,有没有一种可能:上传的文件就没有文件名。这个curl就没有地方给我放文件名的地方,就算如下添加了文件名:

 /**
 * 省略以上代码
 * more code....
 */
 const result = await ctx.app.curl(`https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=${token_result}&kf_account=${account}@wx_official_id`,{
                 method:'post',
                 contentType:'form',
                 dataType:'json',
 +               files:file,
 +               dataAsQueryString:true,
 +               data:{
 +                   media:'avatar.jpg'
 +               }
             });
 /**
 * 省略以上代码
 * more code....
 */

还是非法文件类型。我想想是不是请求头有问题啊?然后添加了beforeRequest方法看看

 /**
 * 省略以上代码
 * more code....
 */
 const result = await ctx.app.curl(`https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=${token_result}&kf_account=${account}@wx_official_id`,{
                 method:'post',
                 contentType:'form',
                 dataType:'json',
 +               files:file,
 +               dataAsQueryString:true,
 +               data:{
 +                   media:'avatar.jpg'
 +               },
 +               beforeRequest:(options) => {
 +                   console.log(options.headers);
 +               }
             });
 /**
 * 省略以上代码
 * more code....
 */

看看请求头,发现还不如不看呢……

  {
   'content-type': 'multipart/form-data; boundary=--------------------------101357776524923229718969',
   'content-length': '3608',
   accept: 'application/json'
 }

emmmm,后来查了别人关于form-data上传的前端示例。如果上传多个file的话,每个file都有其自己的filename和name,那么后端eggjs怎么做呢?于是我试了试axiosjs。

5. 还是Axios好用

最终版的代码……

 // project_path/app/service/customer.js
 const Service = require('egg').Service;
 const fse = require('fs-extra');
 const path = require('path');
 + const axios = require('axios');
 + const FormData = require('form-data');
 class CustomerService extends Service {
      /**
      * 上传客服头像
      * @param {string} account 客服账号
      * @returns 成功或失败原因
      */
      async uploadAvatarAccount(account) {
         const {ctx} = this;
         const {ERR_MAP} = ctx.app.config;
         try {
             const token_result = await ctx.service.wx.getAssessToken();
             if (token_result.errCode) return token_result;            
 -           const avatar = await fse.readFile(path.resolve(__dirname,'./../static/avatar-2.jpg'));
 +            const file = await fse.readFile(path.resolve(__dirname,'./../static/avatar-2.jpg'));
 +           const form = new FormData();
 +           form.append('media',file,'avatar-2.jpg');
              //看这个请求方式
 -              const result = await - -ctx.app.curl(`https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?-access_token=${token_result}&kf_account=${account}@wx_official_id`,{
 -                method:'post',
 -                contentType:'form',
 -                dataType:'json',
 -                files:file,
 -                data:{
 -                   media:file
 -               }
             });
 +           const result = await axios({
                 url:`https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=${token_result}&kf_account=${account}@wx_official_id`,
 +               method:'post',
 +               data:form,
 +           });
             
             if (result.status !== 200) {
                 ctx.logger.warn(`【客服账号上传头像错误】${JSON.stringify(result,null,2)}`);
                 return ERR_MAP.REQUEST;
             }
             const {errcode} = result.data;
             if (errcode !== 0) {
                 ctx.logger.warn(`【客服账号上传头像错误】错误码:${JSON.stringify(result.data,null,2)}`);
                 return ERR_MAP.REQUEST;
             }
             return 'success';
         } catch(e) {
             ctx.logger.warn(`【客服账号上传头像错误】${e}`);
             return ERR_MAP.REQUEST;
         }
     }
 }
 module.exports = CustomerService;

于是,就成功了。我的感受就是,这微信公众号文档写的什么啊,什么示例都没有,都这么久了,写了一点点,不能写多了。我佛了……

R-C.jpg