Java-压缩还能这么优化~喜大普奔(1)

zipOut.putNextEntry(new ZipEntry(inputFile.getName()));

//写入文件的方法,同上

int size = 0;

//设置读取数据缓存大小

while ((size = bis.read(buffer)) >= 0) {

zipOut.write(buffer, 0, size);

}

//关闭输入输出流

zipOut.closeEntry();

bis.close();

}

}

} catch (Exception e) {

log.error(“batchZipFiles error:sourceFileNames:” + JSONObject.toJSONString(fileNames), e);

} finally {

if (null != zipOut) {

try {

zipOut.close();

} catch (Exception e) {

log.error(“batchZipFiles error:sourceFileNames:” + JSONObject.toJSONString(fileNames), e);

}

}

}

}

复制代码

首先利用BufferedInputStream读取文件内容,ZipOutputStream的putNextEntry方法对每一个文件进行压缩写入。最后将所有压缩后的文件写入到最终的zipOutName文件中。由于用了BufferedInputStream缓冲输入流,文件的读取和写入都是从缓存区(内存)中也就是代码里面对应的byte数组获取,相比较普通的FileInputStream提升了较大的效率。但是不够!耗时如下:

image.png


压缩三个大小为3.5GB的文件

第二天

想到了NIO,传统的IO叫BIO(上面代码)是同步阻塞的,读写都在一个线程中。NIO则是同步非阻塞的,核心是channel(通道),buffer(缓冲区),Selector(选择器)。其实说的通俗易懂点就是NIO在密集型计算下效率之所以比BIO高的原因是NIO是多路复用,用更少的线程最更多的事情,相对于BIO大大减少了线程切换,竞争带来的资源损耗。不多BB了,上代码:

/**

  • 批量压缩文件 v2.0

  • @param fileNames 需要压缩的文件名称列表(包含相对路径)

  • @param zipOutName 压缩后的文件名称

**/

public static void batchZipFiles(List fileNames, String zipOutName) throws Exception {

ZipOutputStream zipOutputStream = null;

WritableByteChannel writableByteChannel = null;

ByteBuffer buffer = ByteBuffer.allocate(2048);

try {

zipOutputStream = new ZipOutputStream(new FileOutputStream(zipOutName));

writableByteChannel = Channels.newChannel(zipOutputStream);

for (String sourceFile : fileNames) {

File source = new File(sourceFile);

zipOutputStream.putNextEntry(new ZipEntry(source.getName()));

FileChannel fileChannel = new FileInputStream(sourceFile).getChannel();

while (fileChannel.read(buffer) != -1) {

//更新缓存区位置

buffer.flip();

while (buffer.hasRemaining()) {

writableByteChannel.write(buffer);

}

buffer.rewind();

}

fileChannel.close();

}

} catch (Exception e) {

log.error(“batchZipFiles error fileNames:” + JSONObject.toJSONString(fileNames), e);

} finally {

zipOutputStream.close();

writableByteChannel.close();

buffer.clear();

}

}

复制代码

还是利用java.nio包下面的api,首先用Channels.newChannel()方法将zipOutputStream输出流创建一个写的通道通道,在读取文件内容的时候直接用FileInputStream.getChannel(),获取当前文件读的通道,然后从读的通道中通过ByteBuffer(缓冲区)读取文件内容写入writableByteChannel写通道中,一定记得反转缓冲区buffer.flip(),否则读取的内容就是文件最后的内容byte=0时的。这种方法相较于上面的速度如下图所示:

image.png


压缩三个大小为3.5GB的文件

第三天

继续优化,听说用上内存映射文件的方式更快!那还等什么,让我来try一try!撸代码:

/**

  • 批量压缩文件 v3.0

  • @param fileNames 需要压缩的文件名称列表(包含相对路径)

  • @param zipOutName 压缩后的文件名称

**/

public static void batchZipFiles(List fileNames, String zipOutName) {

ZipOutputStream zipOutputStream = null;

WritableByteChannel writableByteChannel = null;

MappedByteBuffer mappedByteBuffer = null;

try {

zipOutputStream = new ZipOutputStream(new FileOutputStream(zipOutName));

writableByteChannel = Channels.newChannel(zipOutputStream);

for (String sourceFile : fileNames) {

File source = new File(sourceFile);

long fileSize = source.length();

zipOutputStream.putNextEntry(new ZipEntry(source.getName()));

int count = (int) Math.ceil((double) fileSize / Integer.MAX_VALUE);

long pre = 0;

long read = Integer.MAX_VALUE;

//由于一次映射的文件大小不能超过2GB,所以分次映射

for (int i = 0; i < count; i++) {

if (fileSize - pre < Integer.MAX_VALUE) {

read = fileSize - pre;

}

mappedByteBuffer = new RandomAccessFile(source, “r”).getChannel()

.map(FileChannel.MapMode.READ_ONLY, pre, read);

writableByteChannel.write(mappedByteBuffer);

pre += read;

}

//释放资源

Method m = FileChannelImpl.class.getDeclaredMethod(“unmap”, MappedByteBuffer.class);

m.setAccessible(true);

m.invoke(FileChannelImpl.class, mappedByteBuffer);

mappedByteBuffer.clear();

}

} catch (Exception e) {

log.error(“zipMoreFile error fileNames:” + JSONObject.toJSONString(fileNames), e);

} finally {

try {

if (null != zipOutputStream) {

zipOutputStream.close();

}

if (null != writableByteChannel) {

writableByteChannel.close();

}

if (null != mappedByteBuffer) {

mappedByteBuffer.clear();

}

} catch (Exception e) {

log.error(“zipMoreFile error fileNames:” + JSONObject.toJSONString(fileNames), e);

}

}

}

复制代码

这里有两个坑的地方是:

1.利用MappedByteBuffer.map文件时如果文件太大超过了Integer.MAX时(大约是2GB)就会报错:

image.png

所以这里需要分次将要写入的文件映射为内存文件。

2.这里有个bug,就是将文件映射到内存后,在写完就算clear了mappedByteBuffer,也不会释放内存,这时候就需要手动去释放,详细见上代码。

看速度!

image.png


压缩三个大小为3.5GB的文件

肯定是我的打开方式有问题,为什么反而是最慢的。。难道是文件太大了吗?我的机器内存太小了?还是我用的有问题,让我思考一下。。希望留言区讨论一下。

第四天

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值