微信js_sdk开发

jar 下载路径:https://download.youkuaiyun.com/download/xsf1840/10318356

最近项目中需要用到手机H5页面中录制 音频 视频 的上传回显功能,在开发中发现ios系统对input标签存在兼容性问题导致在页面直接实现音频录制存在困难,包括网上的插件都是基于js实现,所以也都存在兼容性问题。

<!--调用手机的录像功能和直接打开系统文件目录。苹果手机调出来的是拍照和录像安卓手机调出来的录音功能-->

<input type="file" accept="audio/*" capture="microphone">

后初步准备使用微信提供的js_sdk 实现该功能

视频直接使用ajax FormDate对象实现

由于token每日有请求上限,建议放到memcache/redis 或者根据定义的全局时间判断  我根据全局时间判断

                   第一步:获得access_token代码

                   第二步:根据token获得js临时票据(有效时间7200秒,可以放到memcache)

                   第三步:对密钥进行sha1加密

                   第四步:进行配置(页面)

public class PastUtil {
    public static String  token = null;
    public static String time = null;
    public static String jsapi_ticket = null;
    private static final Logger logger = LoggerFactory.getLogger(PastUtil.class);
    /**
     *
     * @param appId   公账号appId
     * @param appSecret
     * @param     request     当前网页的URL不包含#及其后面部分
     * @return
     */
    public static  Map<String,String> getParam(HttpServletRequest request,String appId,String appSecret){
        if(token == null){
            token = getAccess_token(appId, appSecret);
            jsapi_ticket = getJsApiTicket(token);
            time = getTime();
        }else{
            if(!time.substring(0, 13).equals(getTime().substring(0, 13))){ //每小时刷新一次
                token = null;
                token = getAccess_token(appId, appSecret);
                jsapi_ticket = getJsApiTicket(token);
                time = getTime();
            }
        }
        String url = getUrl(request);
        //获取签名并SHA-1加密
        Map<String, String> params = sign(jsapi_ticket, url);
        params.put("appid", appId);
        System.out.println(params);
        return params;
    }

    /**
     * 获取接口访问凭证
     *
     * @param appid 凭证
     * @param appsecret 密钥
     * @return
     */
    public static String getAccess_token(String appid, String appsecret) {
        //凭证获取(GET)
        String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
        // 发起GET请求获取凭证
        Map<String,Object> jsonObject = HttpUtil.doGet(requestUrl,null);
        String access_token = null;
        if (null != jsonObject) {
            try {
                Object object= jsonObject.get("access_token");
                if(object !=null){
                    access_token = object.toString();
                }
            } catch (JSONException e) {
                // 获取token失败
                logger.error("获取token失败 errcode:{} errmsg:{}", jsonObject.get("errcode"), jsonObject.get("errmsg"));
            }
        }
        return access_token;
    }

    /**
     * 调用微信JS接口的临时票据
     *
     * @param access_token 接口访问凭证
     * @return
     */
    public static String getJsApiTicket(String access_token) {
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
        String requestUrl = url.replace("ACCESS_TOKEN", access_token);
        // 发起GET请求获取凭证
        Map<String,Object> jsonObject = HttpUtil.doGet(requestUrl,null);
        String ticket = null;
        if (null != jsonObject) {
            try {
                Object object = jsonObject.get("ticket");
                if(object !=null){
                    ticket = object.toString();
                }
            } catch (JSONException e) {
                // 获取token失败
                logger.error("获取票据失败 errcode:{} errmsg:{}", jsonObject.get("errcode"), jsonObject.get("errmsg"));
            }
        }
        return ticket;
    }

    private static String getUrl(HttpServletRequest request){
        StringBuffer requestUrl = request.getRequestURL();
        String queryString = request.getQueryString();
        String url = requestUrl +"?"+queryString;
        return url;
    }

    public static Map<String, String> sign(String jsapi_ticket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = create_nonce_str();
        String timestamp = create_timestamp();
        String str;
        String signature = "";

        //注意这里参数名必须全部小写,且必须有序
        str = "jsapi_ticket=" + jsapi_ticket +
                "&noncestr=" + nonce_str +
                "&timestamp=" + timestamp +
                "&url=" + url;

        try
        {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(str.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        }
        catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        return ret;
    }

    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    private static String create_nonce_str() {
        return UUID.randomUUID().toString();
    }

    private static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    //获取当前系统时间 用来判断access_token是否过期
    public static String getTime(){
        Date dt=new Date();
        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(dt);
    }
}

页面js 部分需要先到微信公众号注册接口回调域名

<script>
    /*
     * 注意:
     * 1. 所有的JS接口只能在公众号绑定的域名下调用,公众号开发者需要先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
     * 2. 如果发现在 Android 不能分享自定义内容,请到官网下载最新的包覆盖安装,Android 自定义分享接口需升级至 6.0.2.58 版本及以上。
     * 3. 完整 JS-SDK 文档地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
     *
     */
    wx.config({
        debug: true,
        appId: '${weixinMap.appid}',
        timestamp: '${weixinMap.timestamp}',
        nonceStr: '${weixinMap.nonceStr}',
        signature: '${weixinMap.signature}',
        jsApiList: [
            'checkJsApi',
            'startRecord',
            'stopRecord',
            'onVoiceRecordEnd',
            'playVoice',
            'pauseVoice',
            'stopVoice',
            'uploadVoice',
            'downloadVoice'
        ]
    });
</script>
<script src="/static/common/weixin_js_sdk/weixin_js_sdk.js?v=${v}"></script>
引入weixin_js_dk.js 是自己的 录音 语音识别等事件逻辑

需要注意的是,微信存储的音频格式amr格式(压缩比大) ,且只保留3天       H5也 无法播放 需要转码MP3

所以录制音频上传微信服务器,在根据微信提供的多媒体接口 下载到自己的服务器,转码保存即可(根据ffmpeg实现)

转码在后台Java实现

知道原因之后,解决思路有两种。

  1. 不使用jave ,将jave一些核心的代码抽取出来,自己调用系统外部。
  2. 下载最新的ffmpeg,替换掉原先的ffmpeg。

第二种比较简单

网上一般都是利用引入jave-1.0.2集成了ffmpeg的jar包实现该功能,避免了在linux上搭建程序的麻烦

jave的能转换的原理其实就是调用外部的二进制可执行文件 ffmpeg,打开它的jar包就可以发现,它里面内置了ffmpeg.exe(window)或ffmpeg二进制可执行文件(linux)
所以实际上,jave就是封装了一层对外部ffmpeg的调用。

而windows上能转换是因为:ffmpeg.exe 这个程序没问题。再linux环境只需要到网站下载相应二进制可执行文件替换相应的。exe即可

代码如下

private Object o = new Object();//全局锁对象
**
 * 获取上传音频并转码保存
 * @param request
 * @param serverId
 * @return
 */
@RequestMapping("/mobile/add/audio")
@ResponseBody
public Map<String,Object> addAudio(HttpServletRequest request,@RequestParam("serverId")String serverId){
    Map<String,Object> json =null;
    try {
        String path = saveImageToDisk(serverId,request);
        json = getJsonMap(true,path,"");
    }catch (Exception e){
        logger.error("addAudio",e);
        json = getJsonMap(false,"","");
        return json;
    }
    return  json;
}



/**
 * 获取临时素材
 */
private InputStream getMedia(String mediaId) {
    String url = "https://api.weixin.qq.com/cgi-bin/media/get";
    String access_token =  PastUtil.token;
    String params = "access_token=" + access_token + "&media_id=" + mediaId;
    InputStream is = null;
    try {
        String urlNameString = url + "?" + params;
        URL urlGet = new URL(urlNameString);
        HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
        http.setRequestMethod("GET"); // 必须是get方式请求
        //文本 application/x-www-form-urlencoded
        //            视频:video/mpeg4
        //            音频:audio/mp3
        http.setRequestProperty("Content-Type","audio/mp3");
        http.setDoOutput(true);
        http.setDoInput(true);
        http.connect();
        // 获取文件转化为byte流
        is = http.getInputStream();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return is;
}

/**
 * 保存图片至服务器
 * @param mediaId
 * @return 文件名
 */
public String saveImageToDisk(String mediaId,HttpServletRequest request){

    String filename = "";
    String newFileName = "";
    //从微信服务器 下载流文件
    InputStream inputStream = getMedia(mediaId);
    //将流文件写入本地服务器
    byte[] data = new byte[1024];
    int len = 0;
    FileOutputStream fileOutputStream = null;
    try {
        //服务器保存路径
        filename = path+System.currentTimeMillis()+mediaId  + ".amr";
        newFileName = path+System.currentTimeMillis()+mediaId+"1"+".mp3";
        String allAmrUrl = s+filename;  //下载amr到自己服务器的文件全路径
        String allMap3Url = s+newFileName; //转码后全路径
        String savePath = s+url+dateStr; //保存路径
        //目录
        File isD = new File(savePath);
        // 校验如果目录不存在,则创建目录
        if (!isD.isDirectory()) {
            isD.mkdirs();
        }
        if (!isD.exists()) {
            synchronized (o) {
                isD.mkdirs();
            }
        }
        fileOutputStream = new FileOutputStream(allAmrUrl);
        while ((len = inputStream.read(data)) != -1) {
            fileOutputStream.write(data, 0, len);
        }
        //转码
        changeToMp3(allAmrUrl,allMap3Url);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fileOutputStream != null) {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return newFileName;
}

/**
 * 把amr格式的语音转换成MP3
 * @Title: changeToMp3
 * @Description: (把amr格式的语音转换成MP3)
 * @author  pll
 * @param @param sourcePath amr格式文件路径
 * @param @param targetPath 存放mp3格式文件路径
 * @return void 返回类型
 * @throws
 */
public static void changeToMp3(String sourcePath, String targetPath) {
    File source = new File(sourcePath);
    File target = new File(targetPath);
    AudioAttributes audio = new AudioAttributes();
    Encoder encoder = new Encoder();

    audio.setCodec("libmp3lame");
    EncodingAttributes attrs = new EncodingAttributes();
    attrs.setFormat("mp3");
    attrs.setAudioAttributes(audio);

    try {
        encoder.encode(source, target, attrs);
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InputFormatException e) {
        e.printStackTrace();
    } catch (EncoderException e) {
        e.printStackTrace();
    }
}

不得不吐槽:微信开发文档写的很恶心!说明说一半,注意事项还与接口说明不放一起,得自己到犄角旮旯的地方去找

例如:这个js_sdk 需要服务器ip白名单设置  微信转账银行卡获取公钥加密方式,参数回调 甚是烦人

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值