上次写了利用javacv从rtsp流抓图的过程,这次简单记录一下利用海康SDK从安防平台抓图的过程。
1. SDK
首先,在海康官网找到并下载OpenAPI安全认证库(JAVA):
海康开放平台
在我们的项目中,我把下载后得到的artemis-http-client-1.1.12.RELEASE-sources.jar
中的源代码解压到工程目录中,以便后面根据实际需求修改某些参数。如果不想直接用源码,也可以在pom.xml
中添加SDK引用:
<dependency>
<groupId>com.hikvision.ga</groupId>
<artifactId>artemis-http-client</artifactId>
<version>1.1.12.RELEASE</version>
</dependency>
需要说明的是,源码里有一个地方可能是需要修改的 —— 我们抓图或者是调用海康接口,肯定要用到ArtemisHttpUtil
这个类,它对认证信息和请求信息进行了封装,其中的请求部分封装成了Request
的实例,如:
Request request = new Request(Method.GET, httpSchema + artemisConfig.getHost(),
path.get(httpSchema), artemisConfig.getAppKey(), artemisConfig.getAppSecret(), Constants.DEFAULT_TIMEOUT);
然后,这个Request
的实例通过层层调用,最终来到了HttpUtil
,以其中的httpPost
方法为例,我们看一下具体实现:
public static Response httpPost(String host, String path, int connectTimeout, Map<String, String> headers, Map<String, String> querys, Map<String, String> bodys, List<String> signHeaderPrefixList, String appKey, String appSecret)
throws Exception {
if (headers == null) {
headers = new HashMap<String, String>();
}
headers.put(HttpHeader.HTTP_HEADER_CONTENT_TYPE, ContentType.CONTENT_TYPE_FORM);
headers = initialBasicHeader(HttpMethod.POST, path, headers, querys, bodys, signHeaderPrefixList, appKey, appSecret);
CloseableHttpClient httpClient = wrapClient(host);
HttpPost post = new HttpPost(initUrl(host, path, querys));
for (Map.Entry<String, String> e : headers.entrySet()) {
post.addHeader(e.getKey(), MessageDigestUtil.utf8ToIso88591(e.getValue()));
}
UrlEncodedFormEntity formEntity = buildFormEntity(bodys);
if (formEntity != null) {
post.setEntity(formEntity);
}
return convert(httpClient.execute(post));
}
这里有一个不起眼但是又很关键的问题,传进来的connectTimeout参数没用。在一切状况都非常好的情况下,所有一切都部署在同一网段的时候,everything is ok。但是如果通过外网访问,或者海康服务器在A城市,你的应用在B城市通过广域网访问,这时候网络原因可能造成你无法正确通过HTTP取得正确的结果。
这里简单写一下设置超时的方式:
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(connectTimeout)
.setConnectionRequestTimeout(connectTimeout)
.build();
post.setConfig(requestConfig);
抓图
实际上在海康开放平台上,对各种接口的描述已经写得非常清楚了,网上各种教程和资料也非常多。这里简单归纳总结一下,再放上一段代码给大家参考:
抓图流程
(1) 取得访问身份信息,可以找海康平台管理员分配appKey和appSecret;
(2) 取得要抓图的设备编码;
(3) 构造ArtemisConfig
对新,设置海康服务器IP、端口、appKey和appSecret;
(4) 调用抓图接口;
(5) 按照开放平台的描述,解析返回结果,正常情况下会取到一个图片的picUrl;
(6) 从picUrl下载图片。
示例代码
public class ArtemisPost {
/**
* API网关的后端服务上下文为:/artemis
*/
private static final String ARTEMIS_PATH = "/artemis";
private static final String MANUAL_CAPTURE = "/api/video/v1/manualCapture";
private String ip = "";
private String port = "";
private String appKey = "";
private String appSecret = "";
private String cameraIndexCode = "";
public void callManualCapture() {
ArtemisConfig config = new ArtemisConfig();
config.setHost(ip + ":" + port);
config.setAppKey(appKey);
config.setAppSecret(appSecret);
final String getCamsApi = ARTEMIS_PATH + MANUAL_CAPTURE;
Map<String, String> paramMap = new HashMap<>(); // post请求参数
paramMap.put("cameraIndexCode", cameraIndexCode);
String body = JSON.toJSON(paramMap).toString();
Map<String, String> path = new HashMap<String, String>(2) {
{
put("https://", getCamsApi);
}
};
try {
String result = ArtemisHttpUtil.doPostStringArtemis(config, path, body, null, null, "application/json", null);
if (!StringUtils.isEmpty(result)) {
// 在返回结果里,通过code判断操作是否成功
// 操作成功时,取data,再取picUrl
Map resultObj = JSON.parseObject(result, Map.class);
if (resultObj != null && "0".equals(resultObj.get("code").toString())) {
Map<String, String> resultData = (Map<String, String>) resultObj.get("data");
String picUrl = resultData.get("picUrl");
// 代码略有删减,这里将服务器返回的图片转成了base64使用
String imgBase64 = downloadPic(picUrl);
}
}
} catch (ConnectTimeoutException e) {
log.error("Connect to {} failed: connect timed out", ip);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return captureResult;
}
public String downloadPic(String picUrl) throws IOException {
HTTPSTrustManager.retrieveResponseFromServer(picUrl);
URL url = new URL(picUrl);
byte[] buf = new byte[4096];
HttpsURLConnection conn = null;
InputStream inStream = null;
ByteArrayOutputStream outStream = null;
String imgBase64 = null;
try {
//构造连接
conn = (HttpsURLConnection) url.openConnection();
//这个网站要模拟浏览器才行
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();
//用这个做中转站 ,把图片数据都放在了这里,再调用toByteArray()即可获得数据的byte数组
outStream = new ByteArrayOutputStream();
int len = 0;
//读取图片数据
while ((len = inStream.read(buf)) != -1) {
outStream.write(buf, 0, len);
}
outStream.flush();
// 图片转为base64
imgBase64 = Base64Utils.imageToBase64ByLocalByte(outStream.toByteArray());
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
if (inStream != null) {
inStream.close();
}
if (outStream != null) {
outStream.close();
}
}
return imgBase64;
}
}
服务器返回的图片是以https
开头的,所以下载图片之前需要搞一下认证。这东西是在网上找的,直接代码贴出来:
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class HTTPSTrustManager implements X509TrustManager {
static HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
System.out.println("Warning: URL Host: " + urlHostName + " vs. "
+ session.getPeerHost());
return true;
}
};
public final static String retrieveResponseFromServer(final String url) {
HttpURLConnection connection = null;
try {
URL validationUrl = new URL(url);
trustAllHttpsCertificates();
HttpsURLConnection.setDefaultHostnameVerifier(hv);
connection = (HttpURLConnection) validationUrl.openConnection();
final BufferedReader in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
final StringBuffer stringBuffer = new StringBuffer(255);
synchronized (stringBuffer) {
while ((line = in.readLine()) != null) {
stringBuffer.append(line);
stringBuffer.append("\n");
}
return stringBuffer.toString();
}
} catch (final IOException e) {
System.out.println(e.getMessage());
return null;
} catch (final Exception e1){
System.out.println(e1.getMessage());
return null;
}finally {
if (connection != null) {
connection.disconnect();
}
}
}
public static void trustAllHttpsCertificates() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[1];
TrustManager tm = new miTM();
trustAllCerts[0] = tm;
SSLContext sc = SSLContext
.getInstance("SSL");
sc.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc
.getSocketFactory());
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
static class miTM implements TrustManager,
X509TrustManager {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(
X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(
X509Certificate[] certs) {
return true;
}
public void checkServerTrusted(
X509Certificate[] certs, String authType)
throws CertificateException {
return;
}
public void checkClientTrusted(
X509Certificate[] certs, String authType)
throws CertificateException {
return;
}
}
}