当前位置: 首页 > news >正文

Java集成腾讯云OCR身份证识别接口

一、背景

        项目用到身份证识别获取人员信息的功能,于是想到了腾讯云提供这样的API。在整合代码过程都很顺利,利用腾讯云官方SDK很快集成进来。但是在上测试环境部署时有了新的问题,通过Nginx代理后的环境无法访问到目标腾讯云接口,遂有了如下的改造过程。

二、SDK集成Demo

        首先是Maven依赖树:

        <dependency><groupId>com.tencentcloudapi</groupId><artifactId>tencentcloud-sdk-java</artifactId><version>4.0.11</version></dependency>

        然后是腾讯云提供的调试代码,改造了一部分:

import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.ocr.v20181119.OcrClient;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRRequest;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;import java.text.ParseException;@Component
@Slf4j
@PropertySource("classpath:/properties/tencentCard.properties")
public class TencentUtil {@Value("${secretId}")private String secretId;@Value("${secretKey}")private String secretKey;@Value("${tencentUrl}")private String tencentUrl;public RecognitionView recognition(String cBase64) {if (cBase64.length() > 10485760) {throw new BusinessException("证件识别失败:图片文件太大");}RecognitionView tRecognitionView = new RecognitionView();try{// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取Credential cred = new Credential(secretId, secretKey);// 实例化一个http选项,可选的,没有特殊需求可以跳过HttpProfile httpProfile = new HttpProfile();httpProfile.setEndpoint(tencentUrl);// 实例化一个client选项,可选的,没有特殊需求可以跳过ClientProfile clientProfile = new ClientProfile();clientProfile.setHttpProfile(httpProfile);// 实例化要请求产品的client对象,clientProfile是可选的OcrClient client = new OcrClient(cred, "ap-beijing", clientProfile);// 实例化一个请求对象,每个接口都会对应一个request对象IDCardOCRRequest req = new IDCardOCRRequest();req.setImageBase64(cBase64);// 返回的resp是一个IDCardOCRResponse的实例,与请求对象对应IDCardOCRResponse resp = client.IDCardOCR(req);tRecognitionView.setRecognitionView(resp);// 输出json格式的字符串回包log.info("证件识别返回参数:" + IDCardOCRResponse.toJsonString(resp));} catch (Exception e) {String tError = "证件识别失败:" + e.getMessage();log.error(tError);throw new BusinessException(tError);}return tRecognitionView;}}

        postman调试后可以正常获取到解析内容

三、Nginx调用失败及解决

        部署到测试环境后,由于内网服务器需要代理到外网服务器进行外网地址的访问,此时便提示证书找不到的错误。

        找问题的过程很坎坷,从证书的有效性、代理的连通性、SDK的限制性等等,研究了将近三天,就连做梦都在思考哪里有问题。最后实在没了方向,决定从根上入手,跳过证书验证。

        跳过验证分为两步,1、放弃SDK请求方式,需要手写Connection;2、增加跳过证书的代码逻辑。于是便有了如下代码:

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.JsonResponseModel;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.ocr.v20181119.OcrClient;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRRequest;
import com.tencentcloudapi.ocr.v20181119.models.IDCardOCRResponse;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;@Component
@Slf4j
@PropertySource("classpath:/properties/tencentCard.properties")
public class TencentUtil {@Value("${secretId}")private String secretId;@Value("${secretKey}")private String secretKey;@Value("${tencentUrl}")private String tencentUrl;private final static String CT_JSON = "application/json; charset=utf-8"; // 请求头内容类型private final static Charset UTF8 = StandardCharsets.UTF_8; // 编码格式static final OkHttpClient HTTP_CLIENT = new BaiduUtil().getUnsafeOkHttpClient();public RecognitionView recognitionPassSSL(byte[] cbyte) {String ImageBase64 = Base64.encodeBase64String(cbyte);Map<String, Object> tRequest = new HashMap<>();tRequest.put("ImageBase64", ImageBase64);String requestData = JSON.toJSONString(tRequest);RecognitionView tRecognitionView = new RecognitionView();try {MediaType mediaType = MediaType.parse(CT_JSON);RequestBody body = RequestBody.create(mediaType, requestData);Request.Builder tBuilder = new Request.Builder().url("https://" + tencentUrl).method("POST", body);this.assembleHeader(tBuilder,this.sign(requestData));Request request = tBuilder.build();Response response = HTTP_CLIENT.newCall(request).execute();String tResult = response.body().string();Gson gson = new Gson();log.info("证件识别返回参数:" + tResult);if (tResult.contains("Error")) {//{"Response":{"RequestId":"4dd26ba4-3e0e-412b-b5a3-047829d5541f","Error":{"Code":"FailedOperation.ImageNoIdCard","Message":"照片未检测到身份证"}}}JsonResponseModel resp = JSON.parseObject(tResult,JsonResponseModel.class);TencentErrorView tTencentErrorView = JSON.parseObject(JSON.toJSONString(resp.response),TencentErrorView.class);throw new BusinessException(tTencentErrorView.getError().getMessage());} else {//{"name": "吕能仕","id": "362323194911046513","nation": "汉","sex": "男","birthDay": "1949-11-04","address": "江西省上饶市玉山县四股桥乡丁村村喻村4号","age_unit": "岁","age_value": "73"}Type type = new TypeToken<JsonResponseModel<IDCardOCRResponse>>() {}.getType();JsonResponseModel<IDCardOCRResponse> resp = gson.fromJson(tResult, type);tRecognitionView.setRecognitionView(resp.response);}} catch (Exception e) {String tError = "证件识别失败:" + e.getMessage();log.error(tError);throw new BusinessException(tError);}return tRecognitionView;}private void assembleHeader(Request.Builder tBuilder, Map<String, String> sign) {Set<String> strings = sign.keySet();for (String tName : strings) {tBuilder.addHeader(tName, sign.get(tName));}}public RecognitionView recognition(byte[] cbyte) {String tBase64 = Base64.encodeBase64String(cbyte);RecognitionView tRecognitionView = new RecognitionView();try {// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取Credential cred = new Credential(secretId, secretKey);// 实例化一个http选项,可选的,没有特殊需求可以跳过HttpProfile httpProfile = new HttpProfile();httpProfile.setEndpoint(tencentUrl);// 实例化一个client选项,可选的,没有特殊需求可以跳过ClientProfile clientProfile = new ClientProfile();clientProfile.setHttpProfile(httpProfile);// 实例化要请求产品的client对象,clientProfile是可选的OcrClient client = new OcrClient(cred, "ap-beijing", clientProfile);// 实例化一个请求对象,每个接口都会对应一个request对象IDCardOCRRequest req = new IDCardOCRRequest();req.setImageBase64(tBase64);// 返回的resp是一个IDCardOCRResponse的实例,与请求对象对应IDCardOCRResponse resp = client.IDCardOCR(req);tRecognitionView.setRecognitionView(resp);// 输出json格式的字符串回包log.info("证件识别返回参数:" + IDCardOCRResponse.toJsonString(resp));} catch (Exception e) {String tError = "证件识别失败:" + e.getMessage();log.error(tError);throw new BusinessException(tError);}return tRecognitionView;}/*** API签名方法* @param data 发送的json串数据* @return 请求头map* @throws Exception 异常*/@SuppressWarnings({"JsonStandardCompliance", "DuplicatedCode"})private Map<String,String> sign(String data) throws Exception {String service = "ocr"; // 腾讯云服务器String host = "ocr.tencentcloudapi.com"; // 服务器地址String region = "ap-beijing"; // 服务器区域String action = "IDCardOCR"; // api接口名称String version = "2018-11-19"; // 接口版本号String algorithm = "TC3-HMAC-SHA256";// String timestamp = "1551113065";String timestamp = String.valueOf(System.currentTimeMillis() / 1000); // 时间戳SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");// 注意时区,否则容易出错sdf.setTimeZone(TimeZone.getTimeZone("UTC"));String date = sdf.format(new Date(Long.valueOf(timestamp + "000")));// ************* 步骤 1:拼接规范请求串 *************String httpRequestMethod = "POST"; // 请求方法String canonicalUri = "/";String canonicalQueryString = "";String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n"; // 请求头信息String signedHeaders = "content-type;host"; // 签名头包含内容String payload = data; // 请求内容String hashedRequestPayload = sha256Hex(payload);String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"+ canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;// ************* 步骤 2:拼接待签名字符串 *************String credentialScope = date + "/" + service + "/" + "tc3_request";String hashedCanonicalRequest = sha256Hex(canonicalRequest);String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;// ************* 步骤 3:计算签名 *************byte[] secretDate = hmac256(("TC3" + secretKey).getBytes(UTF8), date);byte[] secretService = hmac256(secretDate, service);byte[] secretSigning = hmac256(secretService, "tc3_request");String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();// ************* 步骤 4:拼接 Authorization *************String authorization = algorithm + " " + "Credential=" + secretId + "/" + credentialScope + ", "+ "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;TreeMap<String, String> headers = new TreeMap<String, String>();headers.put("Authorization", authorization);headers.put("Content-Type", CT_JSON);headers.put("Host", host);headers.put("X-TC-Action", action);headers.put("X-TC-Timestamp", timestamp);headers.put("X-TC-Version", version);headers.put("X-TC-Region", region);return headers;}private static byte[] hmac256(byte[] key, String msg) throws Exception {Mac mac = Mac.getInstance("HmacSHA256");SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());mac.init(secretKeySpec);return mac.doFinal(msg.getBytes(UTF8));}private static String sha256Hex(String s) throws Exception {MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] d = md.digest(s.getBytes(UTF8));return DatatypeConverter.printHexBinary(d).toLowerCase();}
}

四、总结

        经过验证,该方式可以访问经过Nginx代理的腾讯云接口。整个解决过程缺少对问题现状的分析,并没有制定切入点,而是想到哪里改哪里,所以修改的过程异常煎熬。

        后续对于问题的挖掘及解决要整体分析然后列出各个怀疑的情况和解决方案,然后对照着清单逐一排查,如此条理清晰的处理过程才会更有效的解决问题。

相关文章:

Java集成腾讯云OCR身份证识别接口

一、背景 项目用到身份证识别获取人员信息的功能&#xff0c;于是想到了腾讯云提供这样的API。在整合代码过程都很顺利&#xff0c;利用腾讯云官方SDK很快集成进来。但是在上测试环境部署时有了新的问题&#xff0c;通过Nginx代理后的环境无法访问到目标腾讯云接口&#xff0c;…...

C++之C++11引入enum class与传统enum关键字总结(二百五十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

如何将word格式的文档转换成markdown格式的文档

如何将word格式的文档转换成markdown格式的文档 前言 A. 介绍Markdown和Word格式文档 什么是Markdown&#xff1f; Markdown是一种轻量级标记语言&#xff0c;旨在简化文本格式化和排版的过程。它以纯文本形式编写&#xff0c;通过使用简单的标记语法&#xff0c;使文档更具…...

Leetcode—2558.从数量最多的堆取走礼物【简单】

2023每日刷题&#xff08;十二&#xff09; Leetcode—2558.从数量最多的堆取走礼物 大顶堆实现代码 void swap(int *a, int *b) {int tmp *a;*a *b;*b tmp; }void downAdjustHeap(int *heap, int low, int high) {int i low;int j 2 * i 1;while(j < high) {if(j …...

【如何写论文】硕博学位论文的结构框架、过程与大纲分析

硕士论文可以说是毕业前最重要的一部分&#xff0c;也可以说是展示和检验你3年研究生学习的成果的一个考试。硕士论文答辩和检验合格&#xff0c;才能够顺利拿到毕业生和学位证&#xff0c;可见其重要性。 目录 一、基础框架1.1、摘要&#xff08;Abstract&#xff09;1.2、绪论…...

砷化镓(GaAs)纳米线 砷化镓纳米线 GaAs纳米线 瑞禧

砷化镓&#xff08;GaAs)纳米线 名称&#xff1a;砷化镓(GaAs&#xff09;纳米线 直径&#xff1a;50-400 nm 长度&#xff1a;10-80μm 纳米线是一种新型的材料结构&#xff0c;具有较小的直径和高的长度比&#xff0c;因此在纳米电子学、光电器件等领域有着广泛的应用前景…...

PostGreSQL:JSON|JSONB数据类型

JSON JSON 指的是 JavaScript 对象表示法&#xff08;JavaScript Object Notation&#xff09;JSON 是轻量级的文本数据交换格式JSON 独立于语言&#xff1a;JSON 使用 Javascript语法来描述数据对象&#xff0c;但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许…...

树----数据结构

树的概念 树是一种非线性的数据结构&#xff0c;它是由 n (n>1) 个有限结点组成一个具有层次关系的集合&#xff0c;它看起来就像一颗倒挂的树&#xff0c;根朝上&#xff0c;叶朝下。由 0 个节点构成的树&#xff0c;叫做空树。 树的特点&#xff1a;每个结点有 0 个或多…...

GitLab定时备份

GitLab定时备份 文章目录 GitLab定时备份GitLab基础环境备份命令自动清理备份上传命令设置定时任务参考链接 GitLab基础环境 部署方式&#xff1a;Docker 版本&#xff1a;16.2.2 备份命令 Notes&#xff1a; 编写sh脚本时&#xff0c;不要使用Windows上的Notepad类似编辑…...

SQL IN 运算符

SQL IN 运算符 IN 运算符允许您在 WHERE 子句中指定多个值。 IN 运算符是多个 OR 条件的简写。 SQL IN 语法 SELECT column_name(s) FROM table_name WHERE column_name IN (value1, value2, ...); 或者 SELECT column_name(s) FROM table_name WHERE column_name IN (SELE…...

虚拟机构建单体项目及前后端分离项目

引言 在现代化办公环境中&#xff0c;会议是组织沟通、决策和合作的重要方式之一。为了提高会议的效率和质量&#xff0c;许多企业选择部署会议OA系统来实现会议管理的自动化和数字化。本博客将介绍如何部署和优化会议OA系统&#xff0c;并探讨前后端分离的SPA项目在此过程中的…...

代码浅析DLIO(一)---整体框架梳理

0. 简介 我们刚刚了解过DLIO的整个流程&#xff0c;我们发现相比于Point-LIO而言&#xff0c;这个方法更适合我们去学习理解&#xff0c;同时官方给出的结果来看DLIO的结果明显好于现在的主流方法&#xff0c;当然指的一提的是&#xff0c;这个DLIO是必须需要六轴IMU的&#x…...

Springboot的Container Images,docker加springboot

Spring Boot应用程序可以使用Dockerfiles容器化&#xff0c;或者使用Cloud Native Buildpacks来创建优化的docker兼容的容器映像&#xff0c;您可以在任何地方运行。 1. Efficient Container Images 很容易将Spring Boot fat jar打包为docker映像。然而&#xff0c;像在docke…...

c 从avi 视频中提取图片

avi 视频的视频流编码必须是jpeg&#xff0c;或者mjpg 直接用摄像头录取的视频都是这两种格式&#xff0c;不能用ffmpeg转成avi的视频。 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.…...

Jtti:Apache服务的反向代理及负载均衡怎么配置

配置Apache服务的反向代理和负载均衡可以帮助您分散负载并提高应用程序的可用性和性能。下面是一些通用的步骤&#xff0c;以配置Apache反向代理和负载均衡。 1. 安装和配置Apache&#xff1a; 确保您已经安装了Apache HTTP服务器。通常&#xff0c;Apache的配置文件位于/etc…...

82.二分查找

目录 什么是二分查找 一、左闭右闭写法[left,right] 代码演示&#xff1a; 二、左闭右开写法[left,right] 代码演示&#xff1a; 今天进行了二分查找的学习。 什么是二分查找 二分查找&#xff08;Binary Search&#xff09;是一种常用的搜索算法&#xff0c;也被称为折…...

线程是如何创建的

线程不是一个完全由内核实现的机制&#xff0c;它是由内核态和用户态合作完成的。pthread_create 不是一个系统调用&#xff0c;是 Glibc 库的一个函数&#xff0c;所以我们还要去 Glibc 里面去找线索。 首先处理的是线程的属性参数。例如前面写程序的时候&#xff0c;我们设置…...

owl_vit安装步骤

owl项目的clip目录与openai的clip重名了&#xff0c;import时容易找不到文件simple_tokenizer。 from clip import simple_tokenizer解决办法: 把clip项目下的simple_tokenizer.py拷贝到owl项目下的clip文件夹 cp simple_tokenizer.py /{project_dir}/scenic/scenic/projects…...

运行real.exe时出现NUM_METGRID_SOIL_LEVELS=0

本人在运行real.exe时&#xff0c;发现出现这样的报错&#xff1a; d01 2020-01-01_00:00:00 ---- ERROR: Mismatch between namelist and global attribute NUM_METGRID_SOIL_LEVELS NOTE: 2 namelist vs input data inconsistencies found. -------------- FATAL CALL…...

【数值计算方法】Gauss消元法及其Python/C实现

文章目录 一、基础理论1. 线性方程组2. Gauss消元法的详细步骤3. 注意事项 二、具体计算过程1. 用Gauss 消元法求A的LU分解&#xff0c;并由此求解方程组 Ax ba. 将A进行LU分解。b. 使用LU分解求解方程组Axb 三、代码实现1. Python代码实现2. C语言代码实现 Gauss消元法&#x…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...