问题
最近项目中需要调用海康的摄像头然后进行抓拍图片,由于使用的是 spring-boot 框架,然后将 DLL 动态链接库打包到 jar包,导致 JNA 获取不到 DLL 的路径。
疑问
为什么在开发的时候可以加载到DLL,打包后就会获取不到?
经过检查,发现打包后,系统会将 jar 包当成一个文件,这时候我们使用 class 的 getResource() 就会获取不到DLL的绝对路径。这主要是因为jar包是一个单独的文件而非文件夹,绝对不可能通过 file:/e:/.../ResourceJar.jar/resource /res.txt 这种形式的文件URL来定位文件。所以即使是相对路径,也无法定位到jar文件内的文件。我们不能用常规操作文件的方法来读取jar中的资源文件。
可能这块的东西有些童鞋不太懂,可以去搜索相关的知识或者自己读取一个资源文件,打成 jar 包试试就明白了。
解决方法一
这时候就有办法了,那么获取不到怎么办,我们是不是可以将DLL放在 jar 包外面,然后给一个绝对路径,答案是可以的。
上图中 HCNetSDK 文件夹中放的是海康的DLL文件,hcnet-0.0.1-SNAPSHOT.jar 是项目的 jar 包。
还有一个问题,我们在部署的时候不知道运维人员到底会将项目部署到哪里,我们不能限定运维人员说必须部署到哪个盘哪个位置。这样的话,估计运维小伙伴会跟我们兵戎相见。那么我们可以在运行的时候指定 JNA 的运行路径。
@echo off
TITLE 拍照服务
set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_111
path path;C:\Program Files\Java\jdk1.8.0_111\bin
java -Dfile.encoding=UTF-8 -Djna.library.path=C:\cam -jar hcnet-0.0.1-SNAPSHOT.jar
pause
脚本中第二行是设置标题,第二行和第三行是设置运行的 JDK 环境。-Djna.library.path=C:\cam 是设置 JNA 的运行路径为 C:\cam 文件夹。这样我们在开发的时候 load 方法中直接写DLL的名称即可。
获取文件路径 DllLoad 类
import java.io.UnsupportedEncodingException;
public class DllLoad {
public static String DLL_PATH;
static {
String root = DllLoad.class.getResource("/").getPath();
if (root.startsWith("file:/")) { // jar包中获取到的路径
DLL_PATH = "";
} else { // 实际开发中获取到的路径不带 file:/
root = root.replaceAll("%20", " ").substring(1);
try {
DLL_PATH = java.net.URLDecoder.decode(root, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
在引用的地方使用:
HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary(DllLoad.DLL_PATH + "HCNetSDK\\Win32\\HCNetSDK",
HCNetSDK.class);
这样就解决了打 jar 后获取不到文件的尴尬情况。
解决方法二
这个方法很简单,直接打成 war 一了百了。spring-boot 如何打包成 war 网上有太多的文章,此处不在赘述。
结语
其实打成 war 包是我当初没研究成打成 jar 的方法,最后将项目上线后,自己也反思了,回去花了半晚上的时间研究了打成 jar 包的方法,如果哪位大神有更好的方法也可以借鉴。
最后说一些题外话吧,我觉得,干技术行业,没有钻研精神是不行的,而且当一个具有挑战性难题解决的时候,相信心中的成就感对于每一个干技术的都不言而喻。可能这个问题对于大神来说都是很简单的事情。说了这么多,还是希望大家读到我文章的兄弟们都能具有钻研解决问题的精神,人人都成为大佬。
Keep Calm And Make Epic Shit.