Java 实现文件【夹】压缩

JDK自带的压缩功能

JDK提供了 ZipOutputStreamZipInputStream 用于实现文件(夹)的压缩及解压缩功能。
压缩 将需要压缩的文件写入 ZipOutputStream
解压 将需要解压的文件用流 ZipInputStream 读取出来

文件压缩代码

public class FileZip {

	/**
	 * 生成压缩文件
	 * @param dir 文件夹
	 * @param path 生成的压缩文件的路径
	 */
	public static void generateZip(File dir, String path) {
		try(ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(path))){
			File[] files = dir.listFiles();
			for (File file : files) {
				if(file.isFile()){
					ZipEntry entry = new ZipEntry(file.getName());
					zipOutputStream.putNextEntry(entry);
					copy(file, zipOutputStream);
				}
			}
			// 完成压缩
			zipOutputStream.finish();
		} catch (IOException e) {
		}
	}

	/**
	 * 文件复制
	 * @param file 文件
	 * @param zipOutputStream 流
	 */
	private static void copy(File file, ZipOutputStream zipOutputStream) {
		try(FileInputStream fis = new FileInputStream(file)){
			byte[] buffer = new byte[fis.available()];
			fis.read(buffer);
			zipOutputStream.write(buffer);
		} catch (FileNotFoundException e) {
		} catch (IOException e) {
		}
	}
}

用到的方法

putNextEntry(ZipEntry e)
开始写一个新的压缩文件Entry, 并将流指向Entry数据的开始位置
closeEntry()
关闭当前压缩Entry, 并将流指向下一个Entry
finish()
完成压缩输出流内容的写入,不会关闭流

从上面的API可以看出:

  1. putNextEntry 以及 closeEntry 都具备改变流的指向的功能,所以closeEntry在demo中并没有使用到;
  2. 最后一定要调用 finish 方法将压缩文件流写入到文件中

使用STORED压缩算法

ZipOutputStream 默认使用的是 DEFLATED 算法对文件进行压缩,会造成压缩时间过长,而且相对有些资源压缩率也比较低(比如:图片),所以使用 STORED 压缩算法会更加好一些,STORED 采用的是不压缩的方式,直接将文件直接写入压缩文件。

public class FileZip {

	/**
	 * 生成压缩文件
	 * @param dir 待压缩的文件夹
	 * @param path 生成的压缩文件的路径
	 */
	public static void generateZip(File dir, String path) {
		try(ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(path))){
			zipOutputStream.setMethod(ZipOutputStream.STORED);
			File[] files = dir.listFiles();
			List<FileMessage> messages = toFileMessages(files);
			for (FileMessage message : messages) {
				ZipEntry entry = new ZipEntry(message.getName());
				entry.setMethod(ZipEntry.STORED);
				entry.setSize(message.getLength());
				entry.setCrc(message.getCrc());
				zipOutputStream.putNextEntry(entry);
				zipOutputStream.write(message.getBuffer());
			}
			zipOutputStream.finish();
		} catch (IOException e) {
		}
	}

	/**
	 * 转换成文件信息实体
	 * @param files 文件数组
	 * @return
	 */
	private static List<FileMessage> toFileMessages(File[] files) {
		List<FileMessage> messages = new ArrayList<FileMessage>(files.length);
		for (File file : files) {
			if(file.isFile()){
				FileMessage message = toFileMessage(file);
				if(null != message){				
					messages.add(message);
				}
			}
		}
		return messages;
	}

	/**
	 * 转换成文件信息实体
	 * @param file 文件
	 * @return
	 */
	private static FileMessage toFileMessage(File file) {
		try(FileInputStream fis = new FileInputStream(file);
				CheckedInputStream cis = new CheckedInputStream(fis, new CRC32());){
			byte[] buffer = new byte[fis.available()];
			cis.read(buffer);
			return new FileMessage(file.getName(), file.length(), buffer, cis.getChecksum().getValue());
		} catch (FileNotFoundException e) {
		} catch (IOException e) {
		}
		return null;
	}
	
	static class FileMessage {
		// 文件名称
		private String name;
		// 文件长度
		private long length;
		private long crc;
		// 文件字节数组
		private byte[] buffer;
		
		public FileMessage(String name, long length, byte[] buffer, long crc){
			this.name = name;
			this.length = length;
			this.buffer = buffer;
			this.crc = crc;
		}
		
		public FileMessage() {
		}

		public long getCrc() {
			return crc;
		}
		
		public void setCrc(long crc){
			this.crc = crc;
		}
		
		public byte[] getBuffer() {
			return buffer;
		}
		
		public void setBuffer(byte[] buffer){
			this.buffer = buffer;
		}
		
		public String getName() {
			return name;
		}
		
		public void setName(String name) {
			this.name = name;
		}
		
		public long getLength() {
			return length;
		}
		
		public void setLength(long length){
			this.length = length;
		}
	}
}

Apache 提供的API

引入

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.18</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

代码

public class Scatter {

	private String path;
	private File root;
	private Integer method = ZipEntry.DEFLATED;
	private ParallelScatterZipCreator scatterZipCreator = new ParallelScatterZipCreator();
	private ScatterZipOutputStream dirs = ScatterZipOutputStream
			.fileBased(File.createTempFile("whatever-preffix", ".whatever"));

	public Scatter(String path) throws IOException {
		this(path, ZipEntry.DEFLATED);
	}

	public Scatter(String path, Integer method) throws IOException {
		this.path = path;
		root = new File(path);
		this.method = method;
	}

	public void generate(OutputStream os) {
		try (final ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(os)) {
			compressCurrentDirectory(root);
			writeTo(zipArchiveOutputStream);
		} catch (IOException | ExecutionException | InterruptedException e) {
		}
	}

	public void generate(String targetPath) {
		try {
			generate(new FileOutputStream(targetPath));
		} catch (FileNotFoundException e) {
		}
	}

	public byte[] generate() {
		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
		generate(buffer);
		return buffer.toByteArray();
	}

	/**
	 * 压缩当前文件夹
	 * 
	 * @param dir
	 *            文件夹
	 * @throws IOException
	 */
	private void compressCurrentDirectory(File dir) throws IOException {
		// 如果是文件, 直接将文件写入
		if (dir.isFile()) {
			addEntry(dir.getName(), dir);
			return;
		}

		// 获取所有的文件及文件夹
		File[] files = dir.listFiles();
		if (files.length == 0) { // 文件夹为空
			String relativePath = getRelativePath(dir.getAbsolutePath()); // 获取相对路径
			addEntry(relativePath, dir);
			return;
		}
		// 遍历文件及文件夹
		for (File f : dir.listFiles()) {
			// 如果是文件夹,则遍历压缩文件夹
			if (f.isDirectory()) {
				compressCurrentDirectory(f);
			} else {
				// 获取相对路径
				String relativePath = getRelativePath(f.getParent());
				addEntry(relativePath + f.getName(), f);
			}
		}
	}

	/**
	 * 获取相对路径
	 * 
	 * @param absolutePath
	 *            绝对路径
	 * @return
	 */
	private String getRelativePath(String absolutePath) {
		return absolutePath.replace(path, "");
	}

	/**
	 * 添加实体
	 * 
	 * @param entryName
	 *            实体名称
	 * @param currentFile
	 *            当前文件
	 * @throws IOException
	 */
	private void addEntry(String entryName, File file) throws IOException {
		ZipArchiveEntry archiveEntry = new ZipArchiveEntry(entryName);
		archiveEntry.setMethod(method);
		final InputStreamSupplier supplier = new DefaultInputStreamSupplier(file);

		if (archiveEntry.isDirectory() && !archiveEntry.isUnixSymlink()) {
			dirs.addArchiveEntry(ZipArchiveEntryRequest.createZipArchiveEntryRequest(archiveEntry, supplier));
		} else {
			scatterZipCreator.addArchiveEntry(archiveEntry, supplier);
		}
	}

	/**
	 * 写出
	 * 
	 * @param zipArchiveOutputStream
	 *            压缩文件输出流
	 * @throws IOException
	 * @throws ExecutionException
	 * @throws InterruptedException
	 */
	private void writeTo(final ZipArchiveOutputStream zipArchiveOutputStream)
			throws IOException, ExecutionException, InterruptedException {
		dirs.writeTo(zipArchiveOutputStream);
		dirs.close();
		scatterZipCreator.writeTo(zipArchiveOutputStream);
	}

	static class DefaultInputStreamSupplier implements InputStreamSupplier {
		private File file;

		public DefaultInputStreamSupplier(File file) {
			this.file = file;
		}

		@Override
		public InputStream get() {
			try {
				return file.isDirectory() ? new NullInputStream(0) : new FileInputStream(file);
			} catch (FileNotFoundException e) {
			}
			return new NullInputStream(0);
		}
	}
}

总结

JDK提供的压缩方式,没有提供多线程压缩的方案,使用多线程压缩需要自己来完成;而Apache提供的API支持多线程压缩,在压缩速度上使用Apache提供的API进行压缩要比JDK提供的方式进行压缩要快很多,建议使用Apache提供的压缩方式进行文件压缩。

参考

  1. https://blog.csdn.net/ahau10/article/details/52550430/
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值