关于Java解压文件的一些坑及经验分享(MALFORMED异常)
文章也已经同步到我的csdn博客: http://blog.csdn.net/u012881584/article/details/72615481
关于Java解压文件的一些坑及经验分享
就在本周, 测试人员找到我说现上的需求文档(zip格式的)无法预览了, 让我帮忙看看怎么回事。
这个功能也并不是我做的, 于是我便先看看线上日志有没有什么错误,果不其然, 后台果然报错了。
java.lang.IllegalArgumentException:MALFORMED
at java.util.zip.ZipCoder.toString(ZipCoder.toString:58)
...
异常大致是这样,前台无法预览需求文档的原因是该zip文件解压失败了。
首先网上查了下这个异常的原因, 都说是因为编码的问题, 要求将UTF-8改成GBK就可以了。
然后定位代码, 看到有一个方法:unzip()
public static void unzip(File zipFile, String descDir) {
try {
File pathFile = new File(descDir);
if (!pathFile.exists()) {
pathFile.mkdirs();
}
ZipFile zip = getZipFile(zipFile);
for (Enumeration entries = zip.entries(); entries.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) entries.nextElement();
String zipEntryName = entry.getName();
if (StringUtils.isNotBlank(pre)) {
zipEntryName = zipEntryName.substring(pre.length());
}
InputStream in = zip.getInputStream(entry);
String outPath = (descDir + "/" + zipEntryName).replaceAll("\\*", "/");
;
//判断路径是否存在,不存在则创建文件路径
File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
if (!file.exists()) {
file.mkdirs();
}
//判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
if (new File(outPath).isDirectory()) {
continue;
}
//输出文件路径信息
LOG.info("解压文件的当前路径为:{}", outPath);
OutputStream out = new FileOutputStream(outPath);
IOUtils.copy(in, out);
in.close();
out.close();
}
zip.close();
LOG.info("******************解压完毕********************");
} catch (Exception e) {
LOG.error("[unzip] 解压zip文件出错", e);
}
}
private static ZipFile getZipFile(File zipFile) throws Exception {
ZipFile zip = new ZipFile(zipFile, Charset.forName("UTF-8"));
Enumeration entries = zip.entries();
while (entries.hasMoreElements()) {
try {
entries.nextElement();
zip.close();
zip = new ZipFile(zipFile, Charset.forName("UTF-8"));
return zip;
} catch (Exception e) {
zip = new ZipFile(zipFile, Charset.forName("GBK"));
return zip;
}
}
return zip;
}
于是便将线上的zip文件down下来 然后本地调试下, 发现在第9行中抛出了异常, 如下代码:
ZipEntry entry = (ZipEntry) entries.nextElement();
再由最开始的异常日志找到ZipCoder中的58行:
String toString(byte[] ba, int length) {
CharsetDecoder cd = decoder().reset();
int len = (int)(length * cd.maxCharsPerByte());
char[] ca = new char[len];
if (len == 0)
return new String(ca);
// UTF-8 only for now. Other ArrayDeocder only handles
// CodingErrorAction.REPLACE mode. ZipCoder uses
// REPORT mode.
if (isUTF8 && cd instanceof ArrayDecoder) {
int clen = ((ArrayDecoder)cd).decode(ba, 0, length, ca);
if (clen == -1) // malformed
throw new IllegalArgumentException("MALFORMED");
return new String(ca, 0, clen);
}
ByteBuffer bb = ByteBuffer.wrap(ba, 0, length);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
cr = cd.flush(cb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
return new String(ca, 0, cb.position());
}
这里只有UTF-8才会进入if逻辑才会抛错?果然如网上所说, 将编码格式改为GBK即可。
ZipCoder这个类似src.zip包中的, 既然这里做了check当然会有它的道理, 单纯的改为GBK来解决这个bug显然是不合理的。
于是便要换种思路了, 线上有些zip是仍然可以预览的。 我将线上的zip文件解压后, 在自己电脑重新打个包(我用的是好压), 然后又运行了上述代码, 竟然解压成功?? 这是为什么? 于是上网上找了一下, 果然找到了答案:
Windows 压缩的时候使用的是系统的编码 GB2312,而 Mac 系统默认的编码是 UTF-8,于是出现了乱码。
最后去问了上传的同事, 他是在Windows下用的winRar上传的(看来不同的解压工具还不同)。
好了, 问题基本定位到了, 这里就要想着怎么解决了。
又是一通找, 终于:
Apache commons-compress 解压 zip 文件是件很幸福的事,可以解决 zip 包中文件名有中文时跨平台的乱码问题,不管文件是在 Windows 压缩的还是在 Mac,Linux 压缩的,解压后都没有再出现乱码问题了。
看到这里基本上问题就要解决了, 于是开始使用apache的commons-compress了, 下面直接上代码, 代码是基于上面代码进行改造的:
首先引入pom文件:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.8.1</version>
</dependency>
public static void main(String[] args) throws Exception{
String path = "C:\\Users\\Isuzu\\Desktop\\test.zip";
unzip(new File(path), "D:\\data",);
}
public static void unzip(File zipFile, String descDir) {
try (ZipArchiveInputStream inputStream = getZipFile(zipFile)) {
File pathFile = new File(descDir);
if (!pathFile.exists()) {
pathFile.mkdirs();
}
ZipArchiveEntry entry = null;
while ((entry = inputStream.getNextZipEntry()) != null) {
if (entry.isDirectory()) {
File directory = new File(descDir, entry.getName());
directory.mkdirs();
} else {
OutputStream os = null;
try {
os = new BufferedOutputStream(new FileOutputStream(new File(descDir, entry.getName())));
//输出文件路径信息
LOG.info("解压文件的当前路径为:{}", descDir + entry.getName());
IOUtils.copy(inputStream, os);
} finally {
IOUtils.closeQuietly(os);
}
}
}
final File[] files = pathFile.listFiles();
if (files != null && files.length == 1 && files[0].isDirectory()) {
// 说明只有一个文件夹
FileUtils.copyDirectory(files[0], pathFile);
//免得删除错误, 删除的文件必须在/data/demand/目录下。
boolean isValid = files[0].getPath().contains("/data/www/");
if (isValid) {
FileUtils.forceDelete(files[0]);
}
}
LOG.info("******************解压完毕********************");
} catch (Exception e) {
LOG.error("[unzip] 解压zip文件出错", e);
}
}
private static ZipArchiveInputStream getZipFile(File zipFile) throws Exception {
return new ZipArchiveInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
}
到了这里就大功告成了, 原先自己遇到这个问题时百度了一圈, 解决方案大都是改编码格式为GBK, 但那也只是治标不治本的方法, 解压的坑就讲这么多, 后续有新的坑还会继续总结出来的。
关于Java解压文件的一些坑及经验分享(MALFORMED异常)的更多相关文章
- JAVA解压文件
package com.chauvet.utils; import java.io.File; import java.io.FileOutputStream; import java.io.IOEx ...
- Java解压上传zip或rar文件,并解压遍历文件中的html的路径
1.本文只提供了一个功能的代码 public String addFreeMarker() throws Exception { HttpSession session = request.getSe ...
- java解压多层目录中多个压缩文件和处理压缩文件中有内层目录的情况
代码: package com.xiaobai; import java.io.File; import java.io.FileOutputStream; import java.io.IOExce ...
- java批量解压文件夹下的所有压缩文件(.rar、.zip、.gz、.tar.gz)
// java批量解压文件夹下的所有压缩文件(.rar..zip..gz..tar.gz) 新建工具类: package com.mobile.utils; import com.github.jun ...
- Java解压和压缩带密码的zip或rar文件(下载压缩文件中的选中文件、向压缩文件中新增、删除文件)
JAVA 实现在线浏览管理zip和rar的工具类 (有密码及无密码的)以及下载压缩文件中的选中文件(向压缩文件中新增.删除文件) 这是之前的版本 JAVA 解压压缩包中指定文件或实现压缩文件的预览及下 ...
- JAVA解压ZIP文件
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.Inp ...
- 开发掉坑(一)tar命令解压文件覆盖源文件
今天在编译机上编译前端代码,报了找不到依赖的异常.检查后发现是node_modules/.bin下少了一些文件. 一开始疑惑为什么本地能成功生成软链在node_modules/.bin,服务器上面却不 ...
- JAVA解压.Z及.ZIP文件
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-compress --> <dependency ...
- Unity3D研究院之LZMA压缩文件与解压文件
原地址:http://www.xuanyusong.com/archives/3095 前两天有朋友告诉我Unity的Assetbundle是LZMA压缩的,刚好今天有时间那么就研究研究LZMA.它是 ...
随机推荐
- 【Egret】里使用audio标签达到默认播放背景音乐
方法一 <audio id="bgmMusic" style="position:absolute;" src="resource/assets ...
- user-modify属性,让html标签可以编辑
其实这只是一个很小的需求,但是写着写着发现干货越来越多,所以特意给大家分享一下. 项目需要做一个类似QQ聊天输入的效果 有的同学说,这不是很简单吗?一开始我也这么感觉 :) 观察需求 1.整体固定在底 ...
- MYSQL中 ENUM 类型的详细解释
ENUM 是一个字符串对象,其值通常选自一个允许值列表中,该列表在表创建时的列规格说明中被明确地列举. 在下列某些情况下,值也可以是空串("") 或 NULL: 如果将一个无效值插 ...
- [UWP]了解模板化控件(5):VisualState
1. 功能需求 使用TemplatePart实现上篇文章的两个需求(Header为空时隐藏HeaderContentPresenter,鼠标没有放在控件上时HeaderContentPresent半透 ...
- Java转型(向上转型和向下转型)
在Java编程中经常碰到类型转换,对象类型转换主要包括向上转型和向下转型. 5.13.1 向上转型 我们在现实中常常这样说:这个人会唱歌.在这里,我们并不关心这个人是黑人还是白人,是成人还是小孩,也就 ...
- 【mysql】关于InnoDB表text blob大字段的优化
最近在数据库优化的时候,看到一些表在设计上使用了text或者blob的字段,单表的存储空间已经达到了近100G,这种情况再去改变和优化就非常难了 一.简介 为了清楚大字段对性能的影响,我们必须要知道i ...
- ESLint系列:ESLint入门安装及简单配置
1.eslint需要依赖node.js环境,在配置之前需要安装好node.js; 2.npm install eslint --save-dev 或 npm install eslint --save ...
- angular中路由的实现(针对新手)
最近搜了一下网上的教程,看完总感觉有点糊涂,对于新手来说可能云里雾里,所以写一个最简单的路由来给大家做个指引. 首先当然需要准备angular,下载地址:https://angular.io/ 现在a ...
- 鸟哥linux私房菜学习笔记,U盘安装centos5.3不能正常引导的问题
前言: 一直都想学习linux,毕竟是做测试的标配.听过鸟哥的linux私房菜大名,作为新手我淘来了第三版,到手看到书的厚度,心都凉了半截,本着不能浪费的原则,还是学吧! 过程: 开始看 ...
- java复习(6)---异常处理
JAVA异常处理知识点及可运行实例 接着复习java知识点,异常处理是工程中非常重要的. 1.处理异常语句: try{ .... }catch(Exception e){ ..... } finall ...