問題背景:開發中,我們時常會遇到對文件進行存儲或傳輸的問題,但如果傳輸儲存的文件較大,浪費磁盤空間不說,還會大大影響程序運行效率。於是便引出了這篇文章的主題,關於文件打包與壓縮的問題。這裏“打包”是指,將多個文件合成一個文件;“壓縮”是指,把文件的二進制代碼壓縮,把相鄰的0,1代碼減少,比如有000000,可以把它變成6個0 的寫法60,來減少該文件的空間。
舉例:在PC端中,有很多壓縮軟件,如:WinRAR、2345好壓等。他們通常是將文件打包與壓縮和爲一體執行的。簡單來說,當我們要壓縮的文件夾裏包含多個文件時,壓縮軟件會先將文件夾打包成一個文件後再進行二進制代碼壓縮。就像Linux中 tar 與 gzip 命令一樣,一個負責打包,一個負責壓縮。
壓縮率: bz2 > gz > zip。看到這裏,可能有人會問,爲什麼沒有.rar格式的文件呢?那是因爲,.rar格式是一種文件壓縮與歸檔的私有專利壓縮格式。其作者,僅有條件的公開了解碼程序的源代碼,但是編碼程序仍然是私有的。(PS:由於zip文件壓縮率太低所以很少被使用,但zip格式文件也是有優點的,最明顯的是,.zip格式的文件可以在Windows和Linux中不安裝任何解壓軟件的情況下解壓。)
說了這麼多,接下來我們就開始動手吧!
(由於有同學反饋該篇文章的功能不夠全面,因此博主進行了功能升級建議查看 下篇文章 進行效仿,以節約您寶貴的時間)
依賴jar包:
commons-compress-1.12.jar
commons-io-2.4.jar
代碼:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.io.IOUtils;
/**
* 多文件壓縮與解壓
* @author Supreme_Sir
*/
public class Compress {
private final static int BUFFER = 1048576;
/**
* 解壓tar.gz文件
* @param tar_gz
* @param sourceFolder
*/
public void decompress(File tar_gz,String sourceFolder){
FileInputStream fis = null;
BufferedInputStream bis = null;
GZIPInputStream gzis = null;
TarArchiveInputStream tais = null;
OutputStream out = null;
try {
fis = new FileInputStream(tar_gz);
bis = new BufferedInputStream(fis);
gzis = new GZIPInputStream(bis);
tais = new TarArchiveInputStream(gzis);
TarArchiveEntry tae = null;
boolean flag = false;
while((tae = tais.getNextTarEntry()) != null ){
File tmpFile = new File(sourceFolder+tae.getName());
if(! flag){
//使用 mkdirs 可避免因文件路徑過多而導致的文件找不到的異常
new File(tmpFile.getParent()).mkdirs();
flag = true;
}
out = new FileOutputStream(tmpFile);
int length = 0;
byte[] b = new byte[BUFFER];
while((length = tais.read(b)) != -1){
out.write(b, 0, length);
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(tais != null) tais.close();
if(gzis != null) gzis.close();
if(bis != null) bis.close();
if(fis != null) fis.close();
if(out != null){
out.flush();
out.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 壓縮tar文件
* @param list
* @param outPutPath
* @param fileName
*/
public File compresser(ArrayList<File> list,String outPutPath,String fileName){
File outPutFile = null;
FileInputStream fis = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
GZIPOutputStream gzp = null;
File tar = new File("C:/temp.tar");
try {
fis = new FileInputStream(pack(list,tar));
bis = new BufferedInputStream(fis,BUFFER);
outPutFile = new File(outPutPath+"/"+fileName+".tar.gz");
fos = new FileOutputStream(outPutFile);
gzp = new GZIPOutputStream(fos);
int count;
byte data[] = new byte[BUFFER];
while ((count = bis.read(data, 0, BUFFER)) != -1) {
gzp.write(data, 0, count);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(gzp != null){
gzp.finish();
gzp.flush();
gzp.close();
}
if(fos != null) fos.close();
if(bis != null) bis.close();
if(fis != null) fis.close();
if(tar.exists()){
tar.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return outPutFile;
}
/**
* 私有函數將文件集合壓縮成tar包後返回
* @param files 要壓縮的文件集合
* @param target tar.輸出流的目標文件
* @return File 指定返回的目標文件
*/
private File pack(ArrayList<File> files, File target){
FileOutputStream fos = null;
BufferedOutputStream bos = null;
TarArchiveOutputStream taos = null;
FileInputStream fis = null;
try {
fos = new FileOutputStream(target);
bos = new BufferedOutputStream(fos,BUFFER);
taos = new TarArchiveOutputStream(bos);
//解決文件名過長問題
taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
for(File file : files){
taos.putArchiveEntry(new TarArchiveEntry(file));
fis = new FileInputStream(file);
IOUtils.copy(fis, taos);
taos.closeArchiveEntry();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(fis != null) fis.close();
if(taos != null){
taos.finish();
taos.flush();
taos.close();
}
if(bos != null){
bos.flush();
bos.close();
}
if(fos != null){
fos.flush();
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return target;
}
}
結語: 最後,細心的人可能會問,上文中說 .bz2 格式的文件爲壓縮率最高的文件,可爲什麼沒有選擇 .bz2 格式進行文件壓縮。這裏我想說明的是,可能是因爲兩種壓縮算法不同的原因, 當我對一個139M的文件夾進行打包壓縮時,使用.bz2算法壓縮需要35秒的時間,而當我使用.gz格式的文件只需要15秒的時間,且文件大小僅差3M。因此,綜合考慮後,我選擇了.gz格式的壓縮文件。