zoukankan      html  css  js  c++  java
  • java 调用腾讯身份OCR接口文档实例(绝对可用)

    1.情景展示

      通过读取身份证照片上的信息,实现自动填充功能。 

    2.原因分析

      想要解析照片上所携带的相关信息,就需要识别照片的功能,腾讯提供了免费的身份证OCR接口,可供大家使用。

      没有耐心的可以直接看接口调用(跳过接口规则介绍)

    3.接口规则

      接口地址:https://api.ai.qq.com/fcgi-bin/ocr/ocr_idcardocr

      API地址:https://ai.qq.com/doc/ocridcardocr.shtml

      规则

      注意:

      1.调取接口的请求方式必须使用form表单提交,JSON请求调取接口无效(我已经试过);

      2.返回的是JSON格式的字符串,解析具体数据时,并不能直接当做json对象取值,需要先将其转换成JSON对象。

      入参介绍

      app_id,注册账号后自动生成的唯一标识,配合密钥,才能拥有调取接口的权限;

      time_stamp,时间戳,获取系统当前时间戳(单位是秒,不是毫秒),用于超时校验的(5分钟);

      nonce_str,随机字符串,最大长度为32位,我们直接生成32位即可。

      sign,用于安全校验,主要是防止数据在传输过程中被篡改的;

      image,由图片转换成base64码,只支持jpg,png和bmp格式的图片并且图片大小不能够超过1MB。

      另外,不能包含图片类型的声明,即:需要将头部的,"data:image/jpeg;base64,","data:image/png;base64,","data:image/bmp;base64,"去掉。

      card_type,就很简单了,如果是正面用0,反面用1。

      需要说明的是:app_id,time_stamp和card_type这三个字段的值实际上是字符串,不用理会对于整数的说明,本身它对值的类型根本就限制不住,因为它要求的是必须是form表单提交,form表单提交的数据类型,永远只有一种,那就是字符串,这一点他们是自相矛盾的!

      sign的生成规则如下:


      意思是说:将这几个必要参数,排除sign后,按照字典升序排列(ASCII升序排列),组成get请求url参数拼接的形式,

      1.必要参数的升序拼接结果:app_id=value1&card_type=value2&image=value3&nonce_str=value4&sign=value5&time_stamp=value6

      2.image的值,即base64格式的数据需要使用URL进行编码,其余字段的值不需要使用URL编码。

      3.生成未加密前的字符串:将第一步的结果在其末尾拼接上密钥,即:app_id=value1&card_type=value2&image=value3&nonce_str=value4&sign=value5&time_stamp=value6&app_key=value7

      4.生成sign:对第三步的结果,使用MD5加密,加密结果转大写,得到sign的值。

      返回数据

      并发量限制(QPS-每秒可调用请求的次数) 

    4.具体调用

      前提:假设前端给我们传过来的是一个base64格式的图片。

      设置好常量

    // 接口分配的ID
    private final static int TX_APP_ID = 1111111111;
    // 接口对应的密钥
    private final static String TX_APP_KEY = "2222222222222222";
    // 接口调取地址
    private final static String TX_ORCURL = "https://api.ai.qq.com/fcgi-bin/ocr/ocr_idcardocr";  

      导入

    import java.util.Map;
    import java.util.TreeMap;
    import java.util.UUID;  

      具体代码

    String imgBase64 = request.getParameter("imgBase64");
    if (imgBase64.startsWith("data:image/jpeg;base64,")) {
        imgBase64 = imgBase64.replace("data:image/jpeg;base64,", "");
    } else if (imgBase64.startsWith("data:image/png;base64,")) {
        imgBase64 = imgBase64.replace("data:image/png;base64,", "");
    } else if (imgBase64.startsWith("data:image/bmp;base64,")) {
        imgBase64 = imgBase64.replace("data:image/bmp;base64,", "");
    }
    
    // 时间戳
    int time_stamp = (int) (System.currentTimeMillis()/1000);
    // 随机字符串
    String nonce_str = UUID.randomUUID().toString().replace("-", "");
    // 鉴权
    String sign = "";
    // 1.String image = URLEncoder.encode(imgBase64, "UTF-8");
    // base64图片
    String image = imgBase64;
    // 身份证正反面(0-正面;1-反面)
    int card_type = 0;
    // 必须使用TreeMap(会自动生成有序Map)
    Map<String, String> sortedMap = new TreeMap<>();
    sortedMap.put("app_id", String.valueOf(TX_APP_ID));
    sortedMap.put("time_stamp", String.valueOf(time_stamp));
    sortedMap.put("nonce_str", nonce_str);
    sortedMap.put("image", image);
    sortedMap.put("card_type", String.valueOf(card_type));
    // 生成鉴权
    sign = generateSign(sortedMap);
    sortedMap.put("sign", sign);
    // 调取腾讯接口
    String resultData = httpPostWithForm(TX_ORCURL,sortedMap);
    // TODO 对resultData进行进一步处理(可直接返给前端处理)  

      生成鉴权代码

      需导入

    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;
    import java.util.Iterator;
    import java.util.Map;
    import org.apache.commons.codec.digest.DigestUtils;
    /**
     * 生成腾讯身份证OCR鉴权
     * @explain key1=value1&key2=value2&...
     * @param sortedMap	key按照字典进行升序排列的Map
     * @return	sign(MD5加密并转大写)
     */
    private static String generateSign(Map<String, String> sortedMap) {
        // 鉴权
        String sign = "";
        Iterator<String> iter = sortedMap.keySet().iterator();
        StringBuilder sb = new StringBuilder();
        while(iter.hasNext()){ 
            String key =iter.next(); 
            Object value = sortedMap.get(key);
            // 2.base64需要使用URL进行编码
            if (key.equals("image")) {
                try {
                    value = URLEncoder.encode(sortedMap.get(key), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
            sb.append(key).append("=").append(value).append("&");
        }  
        sb.append("app_key").append("=").append(TX_APP_KEY);
        // 加密前
        sign = sb.toString();
        // 加密后(MD5加密并转大写)
        sign = DigestUtils.md5Hex(sign).toUpperCase();
        return sign;
    }
    

      发送post请求代码

      需导入

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import org.apache.commons.httpclient.HttpStatus;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.util.EntityUtils;
    
    /**
     * 以form表单形式提交数据,发送post请求
     * @explain 
     *   1.请求头:httppost.setHeader("Content-Type","application/x-www-form-urlencoded")
     *   2.提交的数据格式:key1=value1&key2=value2...
     * @param url 请求地址
     * @param paramsMap 具体数据
     * @return 服务器返回数据
     */
    public static String httpPostWithForm(String url,Map<String, String> paramsMap){
        // 用于接收返回的结果
        String resultData ="";
         try {
                HttpPost post = new HttpPost(url);
                List<BasicNameValuePair> pairList = new ArrayList<BasicNameValuePair>();
                // 迭代Map-->取出key,value放到BasicNameValuePair对象中-->添加到list中
                for (String key : paramsMap.keySet()) {
                    pairList.add(new BasicNameValuePair(key, paramsMap.get(key)));
                }
                UrlEncodedFormEntity uefe = new UrlEncodedFormEntity(pairList, "utf-8");
                post.setEntity(uefe); 
                // 创建一个http客户端
                CloseableHttpClient httpClient = HttpClientBuilder.create().build();
                // 发送post请求
                HttpResponse response = httpClient.execute(post);
                
                // 状态码为:200
                if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                    // 返回数据:
                    resultData = EntityUtils.toString(response.getEntity(),"UTF-8");
                }else{
                    throw new RuntimeException("接口连接失败!");
                }
            } catch (Exception e) {
                throw new RuntimeException("接口连接失败!");
            }
         return resultData;
    }
    

      说明:

      如果我在拼接sign钱前,对image进行url编码,即取消1.的注释,将2.代码注销,将会发生有趣的事情:

      调用接口会报错:16388,即鉴权失败,也就是咱们这边生成的sign和腾讯那边生成的sign不一致。

      但是,我已经测过了,两种方式,只是对image进行url编码的时间不同而已,并没有出现url编码后image的值不一致的情况,很是奇怪,感兴趣的可以自己试一下。

      我这里直接上的是正确的代码,会报异常的代码已经注销。

      刚开始的时候,一直卡在这里,错误代码是16388,即:请求签名无效,郁闷了很久,换了一种方式居然可以了,服气!

    5.错误代码

      具体错误,见:https://ai.qq.com/doc/returncode.shtml 

      需要注意的一个错误代码是:16432,其实际意思是,你上传的图片不是身份证

      但是,错误提示信息却是:

      驴头不对马嘴,真是服了!!!

      另外,这是图片识别技术,并没有对身份证的真实有效性进行校验,不要想太多!

      我们知道,现在手机拍照的照片大小一般在10MB左右,由于腾讯接口只允许接受1MB以内的图片,那我们就必须对图片进行压缩。

      以下两种方式任你选。 

    写在最后

      哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!

     相关推荐:

  • 相关阅读:
    SpringMVC学习笔记六:类型转换器及类型转换异常处理
    SpringMVC学习笔记五:HandlerExceptionResolver异常处理
    SpringMVC学习笔记四:SimpleMappingExceptionResolver异常处理
    SpringMVC学习笔记三:Controller的返回值
    SpringMVC学习笔记二:参数接受
    SSH+Ajax实现用户名重复检查(二)
    SSH+Ajax实现用户名重复检查(一)
    Java添加事件的四种方式
    用Java开发一个本地服务管理软件
    Java Web开发中的名词解释
  • 原文地址:https://www.cnblogs.com/Marydon20170307/p/11448975.html
Copyright © 2011-2022 走看看