大华ICC远程抓图示例

1. 准备工作

(1)找大华ICC系统管理员申请访问凭证,一个clientId和一个clientSecret。
(2)除访问凭证外,还需要申请一个OpenAPI用户账号和密码。
(2)阅读ICC开放平台API,查询接口地址。
在这里插入图片描述

2. 开发

(1)引入大华ICC的鉴权SDK

大华ICC平台利用oauth实现第三方身份认证及接口鉴权。根据ICC开放平台文档,我们可以自行构建HTTP请求进行身份认证,也可以直接利用大华提供的SDK开发工具包进行登录认证操作。

在pom.xml文件中引入java-sdk-oauth包:

<dependency>
	<groupId>com.dahuatech.icc</groupId>
    <artifactId>java-sdk-oauth</artifactId>
    <version>1.0.9</version>
</dependency>

(2)API接口和相关模型类

public class DahuaAPI {

	public static final String GET_PUBLIC_KEY = "/evo-apigw/evo-oauth/1.0.0/oauth/public-key";

    public static final String USER_LOGIN = "/evo-apigw/evo-oauth/1.0.0/oauth/extend/token";

    public static final String REFRESH_TOKEN = "/evo-apigw/evo-oauth/1.0.0/oauth/extend/refresh/token";

    public static final String CAPTURE_PIC = "/evo-apigw/admin/API/EVO/invoke/DMS";

    public static final String OSS = "/evo-apigw/evo-oss/";
}

ICC平台令牌包装类

@Data
public class Token {

    private String token;

    private String refreshToken;

    private long getTokenTime;

    private long expireTime;

    private String tokenType;

    public Token(IccTokenResponse.IccToken iccToken){
        this.token = iccToken.getAccess_token();
        this.refreshToken = iccToken.getRefresh_token();
        this.getTokenTime = System.currentTimeMillis();
        // 把过期时间设置成真实过期时间 - 10分钟
        this.expireTime = this.getTokenTime + iccToken.getExpires_in() * 1000;
        this.expireTime = this.expireTime - 60 * 10 * 1000;
        this.tokenType = iccToken.getToken_type();
    }
}

传递大华相关配置的模型类

@Data
public class DahuaConfig {

    private String serverIp;

	/**
	* 大华ICC平台提供一个HTTP端口和一个HTTPS端口
	*/
	private String httpPort;

    private String httpsPort;

    private String clientId;

    private String clientSecret;

    private String username;

    private String password;
}

(3)身份认证

新建AuthService.java

public class AuthService {
	
	/**
	* 登录
	*/
	public Token doLogin(DahuaConfig config) throws ClientException {
        String url = "https://" + config.getServerIp() + ":" + config.getHttpsPort() + DahuaAPI.USER_LOGIN;
        String publicKey = this.getPublicKey(config);
        Map<String, Object> map = new HashMap();
        // 使用密码模式进行认证
        map.put("grant_type", "password");
        map.put("username", config.getUsername());
        // 用户密码需要加密
        map.put("password", SignUtil.encryptRSA(config.getPassword(), publicKey));
        map.put("client_id", config.getClientId());
        map.put("client_secret", config.getClientSecret());
        map.put("public_key", publicKey);
        IccHttpHttpRequest pr = new IccHttpHttpRequest(url, Method.POST, JSONUtil.toJsonStr(map));
        String prBody = pr.execute();
        IccTokenResponse keyResp = BeanUtil.toBean(prBody, IccTokenResponse.class);
        if (keyResp.isSuccess()) {
            IccTokenResponse.IccToken iccToken = keyResp.getData();
            return new Token(iccToken);
        }
        throw new RuntimeException(keyResp.getErrMsg());
    }

	/**
	* 刷新令牌
	*/
	public Token refreshToken(Token token, DahuaConfig config) throws ClientException {
        String url = "https://" + config.getServerIp() + ":" + config.getHttpsPort() + DahuaAPI.REFRESH_TOKEN;

        log.info("refreshing token");

        Map<String, Object> map = new HashMap();
        map.put("grant_type", GrantType.refresh_token.name());
        map.put("client_id", config.getClientId());
        map.put("client_secret", config.getClientSecret());
        map.put("refresh_token", token.getRefreshToken());
        IccHttpHttpRequest pr = new IccHttpHttpRequest(url, Method.POST, JSONUtil.toJsonStr(map));
        String prBody = pr.execute();
        IccTokenResponse keyResp = BeanUtil.toBean(prBody, IccTokenResponse.class);
        if (keyResp.isSuccess()) {
            IccTokenResponse.IccToken iccToken = keyResp.getData();
			return new Token(iccToken, config);
        }
        throw new RuntimeException(keyResp.getErrMsg());
    }

	/**
	* 登录前需要获取publicKey
	*/
	private String getPublicKey(DahuaConfig config) throws ClientException {
        String url = "https://" + config.getServerIp() + ":" + config.getHttpsPort() + DahuaAPI.GET_PUBLIC_KEY;
        IccHttpHttpRequest pubRequest = new IccHttpHttpRequest(url, Method.GET);
        log.info("getting public key");
        String pubBody = pubRequest.execute();
        OauthPublicKeyResponse keyResp = BeanUtil.toBean(pubBody, OauthPublicKeyResponse.class);
        if (keyResp.isSuccess()) {
            return keyResp.getData().getPublicKey();
        }
        throw new RuntimeException(keyResp.getErrMsg());
    }
}

在AuthServer.java类中,提供了一个登录方法,同时提供了一个刷新令牌方法。
这里需要小伙伴们自己写一个TokenManager,将登录获取到的token缓存起来,避免每次使用时都申请新令牌。同时定期查看缓存令牌的有效期,过期前调用refreshToken方法获取新令牌。

(4)抓图

新建PictureService.java
我的例子中将取到的图片转成了base64,直接用了。如果需要保存到本地,调用IO写一下就好了。

@Slf4j
public class PictureService {

	private ObjectMapper objectMapper = new ObjectMapper();

	/**
	* 手动抓图
	*/
	public String capturePicture(String pointCode, DahuaConfig config, Token token) {

        if (token == null) {
        	throw new RuntimeException("未取得令牌");
        }

        String imgBase64 = null;
        try {
            String url = "http://" + dahuaConfig.getServerIp() + ":" + dahuaConfig.getHttpPort() + DahuaBaseAPI.CAPTURE_PIC;
            String[] pointCodeDetails = pointCode.split("\\$");
            // pointCode前面一部分作为设备位号
            String devicePointCode = pointCodeDetails[0];
            // pointCode最后部分作为通道号
            String devChannel = pointCodeDetails[pointCodeDetails.length - 1];

            Map<String, Object> deviceMap = new HashMap<>();
            deviceMap.put("CmdSrc", 0);
            deviceMap.put("DevChannel", devChannel);
            deviceMap.put("SnapType", 1);
            deviceMap.put("DevID", devicePointCode);

            Map<String, Object> operationMap = new HashMap<>();
            operationMap.put("method", "dev.snap");
            operationMap.put("id", System.currentTimeMillis());
            operationMap.put("params", deviceMap);
            String operationJson = objectMapper.writeValueAsString(operationMap);

            Map<String, String> uriVariables = new LinkedHashMap<>(3);
            uriVariables.put("deviceCode", devicePointCode);
            uriVariables.put("operation", "generalJsonTransport");
            uriVariables.put("params", operationJson);
            String uriVariablesJson = objectMapper.writeValueAsString(uriVariables);

            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization", token.getTokenType() + " " + token.getToken());
            headers.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<String> httpEntity = new HttpEntity<>(uriVariablesJson, headers);
            ResponseEntity<Map> responseEntity = new RestTemplate().exchange(url, HttpMethod.POST, httpEntity, Map.class);
            Map<String, Object> body = responseEntity.getBody();

            Object code = body.get("code");
            Object desc = body.get("desc");
            Object data = body.get("data");
            // 抓图成功时,服务器会返回图片地址
            if (code != null && code.toString().equals("1000")) {
                data = data.toString().replaceAll("\n", "").replaceAll(" ", "");
                Map<String, Object> dataMap = objectMapper.readValue(data.toString(), Map.class);
                Map<String, Object> picInfo = (Map<String, Object>) dataMap.get("params");
                String picPath = (String) picInfo.get("PicInfo");

                // OSS图像地址拼接
                picPath = "http://" + dahuaConfig.getServerIp() + ":" + dahuaConfig.getHttpPort() + DahuaAPI.OSS + picPath + "?token=" + token.getToken();
                imgBase64 = this.downloadPic(picPath);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return imgBase64;
    }

	public String downloadPic(String picUrl) {
        URL url = null;
        try {
            url = new URL(picUrl);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }

        byte[] buf = new byte[4096];
        HttpURLConnection conn = null;
        InputStream inStream = null;
        String imgBase64 = null;
        try {
            //构造连接
            conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            //这个网站要模拟浏览器才行
            conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko");
            //打开连接
            conn.connect();
            //打开这个网站的输入流
            inStream = conn.getInputStream();

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int len = 0;
            while ((len = inStream.read(buf)) != -1) {
                byteArrayOutputStream.write(buf, 0, len);
            }
            byteArrayOutputStream.flush();
            byte[] imgContent = byteArrayOutputStream.toByteArray();

            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imgContent);
            BufferedImage image = ImageIO.read(byteArrayInputStream);
            if (image != null) {
                imgBase64 = Base64Utils.imageToBase64ByLocalByte(imgContent);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            if (inStream != null) {
                try {
                    inStream.close();
                } catch (IOException e) {
                }
            }
        }
        return imgBase64;
    }
}

3. 说明

(1)都是忙里偷闲写的这些东西,写的过程中删除了小部分与实际项目相关的代码。如果copy出去发现编译不过,留个言研究研究。
(2)大华ICC平台不支持高频抓图,高频抓图时返回的图片会与传入的设备位号不同。如,对100个设备同时抓图,可能抓1000001设备时,返回了1000009设备的图片。这个问题也没什么好办法去解决,我们直接用限流工具控制抓图速度了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值