通过代码实现gz压缩,并保持原来的文件名
写这篇博客主要是为了记录一下这两天来的研究成果-gz带原有文件名压缩。首先要说的是这个解决方案不是通过调用命令gzip来做的,而是通过java代码来实现的,其中用到了apache的common compress类库。
首先来了解一下问题:
对于gz文件,我想对于使用Linux的同学们来说应该非常的熟悉了。那好,现在有一个文件叫a.txt, 我现在要把它压缩成b.gz。可以猜想一下,解压后的文件是
1. a.txt
2. b.txt
3. b
猜到了没,答案是3。为什么?为什么用gzip -c压缩a.txt,解压出来还是a.txt。而使用代码(通常是GZIPOutputStream)压缩后,解压的却是b,而且连个扩展名也没有。
这个问题还得从gz的设计目的说起。对于gz这种压缩格式,设计当初的目的是用来压缩单个平面文件的。也就是说如果想压缩多个文件,就要用到tar命令。如果想保留原文件名就要用文件名+.gz来做,如a.txt -> a.txt.gz。默认情况下,gz并不负责对文件名的处理。那么gzip -c又是如何做到保留原文件名这点的呢?
这里就要说一下gz的文件格式标准了,参见gz文件格式RFC。里面明确提到了在gz文件头里有一个标志位用于控制gz文件的一下扩展属性。
+---+---+---+---+---+---+---+---+---+---+ |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->) +---+---+---+---+---+---+---+---+---+---+ (if FLG.FEXTRA set) +---+---+=================================+ | XLEN |...XLEN bytes of "extra field"...| (more-->) +---+---+=================================+ (if FLG.FNAME set) +=========================================+ |...original file name, zero-terminated...| (more-->) +=========================================+ (if FLG.FCOMMENT set) +===================================+ |...file comment, zero-terminated...| (more-->) +===================================+ (if FLG.FHCRC set) +---+---+ | CRC16 | +---+---+ +=======================+ |...compressed blocks...| (more-->) +=======================+ 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | CRC32 | ISIZE | +---+---+---+---+---+---+---+---+
一个实际的gz文件样子
红色部分的是FNAME标志位,以及附带的文件名。一旦设置了这两个属性,gz在解压缩时候就会自动使用附带的文件名来命名解压文件了。那么写到这里,gzip命令的实现原理就已经清楚了。接下来就要分析一下为什么常规的代码无法实现这一功能。
将想法付诸于实践,借此来影响他人是一个人存在的真正价值