项目里面使用的opencv测试可用,最后输出打包却不可用,另外为了不配置环境变量做了很多工作,
此文记录下全部过程发生的问题及解决方法.
一. maven依赖本地jar包
maven导入本地jar,基本有下面两种方法
1.1本地jar包安装到maven库
mvn install:install-file -Dfile=D:/opencv-341.jar -DgroupId=com.opencv -DartifactId=opencv-java -Dversion=3.4.1 -Dpackaging=jar
这样在pom里面正常引用就可以.
<dependency>
<groupId>com.opencv</groupId>
<artifactId>opencv-java</artifactId>
<version>3.4.1</version>
</dependency>
如果不想安装到本地,或者不想项目组每个人都这样安装一遍可以使用下面第二种
1.2本地jar包system引入
直接引入项目lib目录的文件,即插即用.
<dependency>
<groupId>com.opencv</groupId>
<artifactId>opencv-java</artifactId>
<version>3.4.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/opencv-341.jar</systemPath>
</dependency>
这样使用,本地eclipse里面运行是没有问题的.
二. opencv dll/so文件jar使用
2.1 正常环境变量使用
因为opencv最后需要dll或者so本地功能的支持。
代码里面一般是这样。不管是什么系统,配置上环境变量即可自动加载dll或者so
# System.loadLibrary("opencv_java341")
System.loadLibrary(Core.NATIVE_LIBRARY_NAME)
人懒没办法,为了方便部署,减少其他人的部署工作,生成的jar直接能用,折腾了下.
想来动态加载,这样不需要修改环境变量,也不需要添加jar至本地库
2.2 动态加载路径
先把dll和so都打包进项目生成的jar中,在系统启动时,根据系统类型将dll或者so读取释放到指定目录,
然后使用System.load(nativepath);动态加载释放出来的dll或者so
String osName = System.getProperty("os.name");
boolean iswindow = true;
String nativename = PropertiesUtil.getProperty("opencv.dll.path");
if (osName.toLowerCase(new Locale("", "", "")).startsWith("windows")) {
iswindow = true;
} else {
iswindow = false;
nativename = PropertiesUtil.getProperty("opencv.so.path");
}
//这里不能用File.separator ... windows下又坑了一回- -!
InputStream nativeStream = OpencvHelper.class.getClassLoader().getResourceAsStream("nativelib/"+nativename);
String path = System.getProperty("user.dir");
String nativepath =path + File.separator+ "lib" +File.separator+ nativename;
File f = new File(path +File.separator+ "lib");
if (!f.exists())
f.mkdirs();
BufferedInputStream reader = null;
FileOutputStream fo = null;
try {
File filenative = new File(nativepath);
fo = new FileOutputStream(filenative);
reader = new BufferedInputStream(nativeStream);
byte[] buffer = new byte[1024];
while (reader.read(buffer) > 0) {
fo.write(buffer);
buffer = new byte[1024];
}
filenative.deleteOnExit();
} catch (Exception e) {
System.err.println("opencv load failed!");
} finally {
try {
if (nativeStream != null)
nativeStream.close();
if (fo != null)
fo.close();
} catch (Exception e) {
}
}
System.load(nativepath);
2.3 将dll/so文件打包放入jar的相关问题
其中dll放在根目录的nativelib目录,
使用maven打包,将其打入jar包中.
其中filter不要使用,不然会出现问题,filter会将打包的dll文件改变.
开始使用时启用了filter,发现54M的jar最后解析出来变成了72M.导致系统无法加载.
fiterh还是比较好用的:
fiter使用时会方便配置文件的处理,比如配置文件jdbc.properties中使用jdbc.server=@jdbc.server@
在pom中可以配置不同的配置文件来动态替换 jdbc.server的值
<profiles> <!-- 初始化数据库 --> <profile> <id>dev</id> <!--可本地修改,避免影响其他开发--> <properties> <!--数据库连接--> <jdbc.server>192.168.100.134:3306/test</jdbc.server> <jdbc.username>test</jdbc.username>
如下在pom.xml中打包包含dll/so文件.
<build>
<resources>
<resource>
<targetPath>${project.build.directory}/classes/nativelib</targetPath>
<directory>nativelib</directory>
<!-- 这个filter不能使用,maven好像会导致dll或者so文件发生变化而不能使用 -->
<!-- <filtering>true</filtering> -->
<includes>
<!-- opencv native lib -->
<include>**/*.dll</include>
<include>**/*.so</include>
</includes>
</resource>
<resources>
...
</build>
三.maven项目打包-spring mvc,lib独立
默认会打包成一个jar包。
3.1 打包jar和依赖分开
将依赖jar包放入单独的lib目录,class独立打包.
<!-- 打包jar文件时,配置manifest文件,加入lib包的jar依赖 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<classesDirectory>${project.build.directory}/classes</classesDirectory>
<archive>
<manifest>
<mainClass>com.alibaba.dubbo.container.Main</mainClass>
<!-- 打包时 MANIFEST.MF文件不记录的时间戳版本 -->
<useUniqueVersions>false</useUniqueVersions>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<!-- 把依赖的jar包,打成一个lib文件夹 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<type>jar</type>
<includeTypes>jar</includeTypes>
<outputDirectory>
${project.build.directory}/lib
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
这样正常是没有问题的,但是我们懒啊。。前文使用了 system模式的引入。
这种系统引入在最后打包的时候是不会自动导出依赖的-_-!,及前面提到的opencv-java.jar包是不会导出到Lib目录的,
这回导致最后生成的jar找不到opencv相关class而无法运行
<scope>system</scope>
<systemPath>${project.basedir}/lib/opencv-341.jar</systemPath>
3.2 依赖的system包没有导出至lib目录
既然不能自动导出,那我们就手动来吧。。
手动复制至打包的lib目录.
<!-- 复制本地jar opencv 库 -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy-resources</id>
<!-- here the phase you need -->
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/lib</directory>
<include>**/*.jar</include>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
然而最后还是无法运行~~
3.3 依赖的system包写入MANIFEST.MF
这种打包分开的方式,最后也是使用 java -jar xx.jar的方式来运行。
那怎么找依赖jar的呢?
其实是通过目录下的META-INF/MANIFEST.MF文件来找依赖的
这个文件同上也会忽略system引入方式的jar,这就坑爹了。
Manifest-Version: 1.0
Built-By: admin
Class-Path: . lib/opencv-341.jar ...其他jar包,空格分开
netty-3.2.6.Final.jar lib/javassist-3.22.0-GA.jar
Build-Jdk: 1.8.0_161
Created-By: Maven Integration for Eclipse
Main-Class: com.alibaba.dubbo.container.Main
既然不引,我们自己来引好了,
修改打包插件配置增加jar包的MF文件引入
<!-- 打包jar文件时,配置manifest文件,加入lib包的jar依赖 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<classesDirectory>${project.build.directory}/classes</classesDirectory>
<archive>
<manifest>
<mainClass>com.alibaba.dubbo.container.Main</mainClass>
<!-- 打包时 MANIFEST.MF文件不记录的时间戳版本 -->
<useUniqueVersions>false</useUniqueVersions>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
<!-- 添加classpath缺少的内容-->
<manifestEntries>
<Class-Path>. lib/opencv-341.jar lib/xxx.jar</Class-Path>
</manifestEntries>
</archive>
<excludes>
<exclude>log4j2-test.xml</exclude>
</excludes>
</configuration>
</plugin>
这样就ok了!
四.springboot环境
上面说的是普通spring mvc,打包,同时lib作为独立目录的方法,
如果系统使用springboot集成打包,则如下处理。这个更简单一点
部分操作时一样的。
打包dll.so
<resource>
<targetPath>${project.build.directory}/classes/nativelib</targetPath>
<directory>nativelib</directory>
<!-- <filtering>true</filtering> -->
<includes>
<!-- opencv native lib -->
<include>**/*.dll</include>
<include>**/*.so</include>
<include>tess4j/**</include>
</includes>
</resource>
本地sys-lib打包入jar
类似spring mvc ,
springboot的jar包目录再 BOOT-INF/lib下,这里手动copy就好
<!-- spring boot-lib目录 -->
<resource>
<targetPath>BOOT-INF/lib/</targetPath>
<directory>${basedir}/lib</directory>
<!-- <filtering>true</filtering> -->
<includes>
<!-- opencv native lib -->
<include>**/*.jar</include>
</includes>
</resource>
五.linux环境GCC启动
都搞定了,要在各个系统跑一下呢.
在本地windos启动,妥妥的ok。
在ubuntu16.04上跑了下发现正常启动,ok~
然后再center os7 上发现运行不了!
报错:Caused by: java.lang.UnsatisfiedLinkError: /media/sf_vmshare/libopencv_java341.so: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /media/sf_vmshare/libopencv_java341.so
搜索一番,发现是GCC的问题。
这个使用的so是我在ubuntu上编译出来的,
ubuntu上的gcc版本是gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
而本地center os7上是gcc version 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC)
编译的高版本在低版本上无法运行。。。这就是编译的坑了。
本来想着怎么升级center os7上的gcc版本了,但是转念一想,为嘛不在低版本呢的gcc上编译一次呢。
点击opencv linux so编译相关教程查看如果编译
然后重新编译一次so,在center OS7上使用低版本gcc。
然后就ok了,这样编译出来的so在高版本gcc的ubuntu上也能正常运行。
这样运行过程会解压出dll或者so文件,动态加载然后运行!
-----------------
坑爹,最后还是出问题了。。妈蛋两个不同的so文件搞混了,外地同事svn更新下来跑不起来,还来了个加班。。
哎,还是不够仔细,后面得加强!
本文基于CC BY-NC-ND 4.0 许可协议发布,作者:野生的喵喵。 固定链接: 【maven项目中使用opencv打包jar及依赖jar为独立lib目录相关问题小记】 转载请注明
相关文章: