大家好,我又来水文了,说句题外话,代码生成器前后端分离响应式主题已经支持权限管理模块生成了,感兴兴趣的朋友可以去尝试下。好了,让我们进入本次文章的主题,文件的压缩和解压在项目中还是比较常见的,比如常用的导出和导入功能,有时候就需要导出或导入为zip格式,对于压缩这类的需求我一直是使用zip4j来处理,简单实用,java中虽然有自带的处理压缩的包,但中文会乱码,apache的ant包可以解决乱码问题,但使用起来较为繁琐,zip4j则要简单很多,下面一起看一下zip4j的使用方法以及我前阵子在项目中遇到的乱码问题以及解决方法吧!
zip4j的压缩文件的方法十分简单粗暴,首先引入pom依赖:
<!-- https://mvnrepository.com/artifact/net.lingala.zip4j/zip4j -->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>1.3.2</version>
</dependency>
下面是压缩文件的代码:
/**
* 压缩带根目录
*/
public static void zipFile(String dir, String filePath) {
try {
ZipFile zip = new ZipFile(filePath);
zip.setFileNameCharset("UTF-8");
File file = zip.getFile();
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
//为了不被原有文件干扰,保证每次重新生成
if (file.exists()) {
file.delete();
}
ZipParameters parameters = new ZipParameters();
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
// 要打包的文件夹
File currentFile = new File(dir);
if (currentFile.isDirectory()) {
zip.addFolder(currentFile, parameters);
} else {
zip.addFile(currentFile, parameters);
}
} catch (Exception e) {
throw new ForbiddenException("压缩出错!!");
}
}
/**
* 压缩根目录内的文件,不带根目录
*/
public static void zipFileList(String dir, String filePath) {
try {
ZipFile zip = new ZipFile(filePath);
zip.setFileNameCharset("UTF-8");
File file = zip.getFile();
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
//为了不被原有文件干扰,保证每次重新生成
if (file.exists()) {
file.delete();
}
ZipParameters parameters = new ZipParameters();
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
// 要打包的文件夹
File zipfloder = new File(dir);
File[] files = zipfloder.listFiles();
assert files != null;
if (files.length == 0) {
zip.addFolder(dir, parameters);
return;
}
for (File theFile : files) {
if (theFile.isDirectory()) {
zip.addFolder(theFile, parameters);
}
if (theFile.isFile()) {
zip.addFile(theFile, parameters);
}
}
} catch (Exception e) {
throw new ForbiddenException("压缩出错!!");
}
}
如上压缩代码所示,zip4j在压缩文件的时候可以指定编码类型,那么解压的时候也必须使用同样的编码类型进行解压,否则就会出现乱码现象,但用户上传的时候,如果是使用window系统压缩,编码类型为gbk,如果是使用linux或者mac压缩,那么编码类型为utf-8,这点我们是不可控的,如果想要防止解压乱码,需要得知压缩包的编码类型才可以!
我最近做的一个项目就遇到了这个问题,本来以为判断压缩包的编码类型是件简单的事情,结果网上一搜,五花八门,什么方法都有,包括读取二进制流,判断字节的值等对于压缩文件来说都是不可用的,最后到了想要修改设计的地步,让用户自行选择压缩平台,然后根据用户的选择使用对应的编码解压,这也不失为一种好的方法,只是对于用户来说多了一步选择的操作。
后来我又仔细寻找了一番,终于发现了较为靠谱的方法:使用cpdetecpor第三方工具包来判断,因为只需要判断gbk或utf-8,准确率目前可以达到百分之百,这个工具包使用了内置的探测器去探测编码,按照“谁最先返回非空的探测结果,就以该结果为准”的原则返回探测到的字符集编码,因此不保证完全正确,但经过项目的实践,可以认为完全正确。(maven仓库里没有收录这个工具包,可以在文末得到获取方式,下载安装到本地maven仓库即可)
相关的解压以及判断编码类型的代码如下:
// 解压加密的压缩文件
public static void unZip(File zipFile, String dest, String pwd) {
try {
// 首先创建ZipFile指向磁盘上的.zip文件
ZipFile zFile = new ZipFile(zipFile);
String charset = getCharset(zipFile);
if (charset == null) {
throw new ForbiddenException("文件不合法,可能已经被损坏!请重新打包尝试!");
}
zFile.setFileNameCharset(charset);
// 验证.zip文件是否合法,包括文件是否存在、是否为zip文件、是否被损坏等
if (!zFile.isValidZipFile()) {
throw new ForbiddenException("文件不合法,可能已经被损坏!请重新打包尝试!");
}
// 解压目录
File destDir = new File(dest);
if (destDir.isDirectory() && !destDir.exists()) {
destDir.mkdir();
}
if (zFile.isEncrypted()) {
// 设置密码
zFile.setPassword(pwd.toCharArray());
}
zFile.extractAll(dest);
} catch (ZipException e) {
throw new ForbiddenException("解析压缩文件过程中出现错误!!");
}
}
private static String getCharset(File file) {
/*
* detector是探测器,它把探测任务交给具体的探测实现类的实例完成。
* cpDetector内置了一些常用的探测实现类,这些探测实现类的实例可以通过add方法 加进来,如ParsingDetector、
* JChardetFacade、ASCIIDetector、UnicodeDetector。
* detector按照“谁最先返回非空的探测结果,就以该结果为准”的原则返回探测到的
* 字符集编码。使用需要用到三个第三方JAR包:antlr.jar、chardet.jar和cpdetector.jar
* cpDetector是基于统计学原理的,不保证完全正确。
*/
CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
/*
* ParsingDetector可用于检查HTML、XML等文件或字符流的编码,构造方法中的参数用于
* 指示是否显示探测过程的详细信息,为false不显示。
*/
detector.add(new ParsingDetector(false));
/*
* JChardetFacade封装了由Mozilla组织提供的JChardet,它可以完成大多数文件的编码
* 测定。所以,一般有了这个探测器就可满足大多数项目的要求,如果你还不放心,可以
* 再多加几个探测器,比如下面的ASCIIDetector、UnicodeDetector等。
*/
detector.add(JChardetFacade.getInstance());
// ASCIIDetector用于ASCII编码测定
detector.add(ASCIIDetector.getInstance());
// UnicodeDetector用于Unicode家族编码的测定
detector.add(UnicodeDetector.getInstance());
java.nio.charset.Charset charset = null;
try {
charset = detector.detectCodepage(file.toURI().toURL());
} catch (Exception ex) {
ex.printStackTrace();
}
if (charset != null) {
String name = charset.name();
if (name.startsWith("UTF")) {
return "UTF-8";
} else {
return "GBK";
}
}
return null;
}
通过此方法可以比较有效的解决解压不同平台压缩的压缩包产生的乱码问题。
文章到这里就结束了,大家可以收藏一下,说不定哪天能用上,或者你有什么更好的方法也欢迎告知我哦,毕竟这个方案并不是完全准确的。今天上班摸鱼更新了一版代码生成器,前后端分离响应式主题现在也支持权限管理模块生成了,同时顺手修复了一些明显的bug,今年已经接近尾声了,大家是不是都在等着年终奖呢,不知道今年发几个月。。。人艰不拆,努力奋斗吧!
关注公众号 螺旋编程极客 发送 zip4j 可获取相关工具包,感兴趣的朋友也可以下载代码生成器使用,期待您的关注!