通俗易懂地教你用Java實現文件壓縮

前言

當前項目中,有一個需求,App中會產生一些日誌文件,需要上傳到服務器,而上傳之前需要把文件壓縮之後再上傳,這樣上傳時就會比較快,因爲文件變小了嘛!那麼怎麼實現壓縮呢,百度,結果那些文章都是一堆堆的代碼,雖然實現了功能,但是並沒有講清楚邏輯,所以在這裏我用自己的方式記錄一下文件壓縮的實現。

最簡單的文件壓縮實現

JDK中自帶壓縮實現類:ZipOutputStream

示例代碼如下:

fun main() {
    val rawfile = File("C:\\Users\\even\\Desktop\\logs\\壓縮示例.txt")
    val zipfile = File("C:\\Users\\even\\Desktop\\logs\\壓縮示例.zip")

    ZipOutputStream(zipfile.outputStream()).use { zos ->
        val zipEntry = ZipEntry(rawfile.name)
        zos.putNextEntry(zipEntry)
        rawfile.inputStream().use { ins ->
            val array = ByteArray(1024)
            var len: Int
            while (ins.read(array).also { len = it } != -1) {
                zos.write(array, 0, len)
            }
        }
    }
}

這裏採用的是Kotlin語言,如果你用的是Java,應該也能看得懂,代碼不多,最核心的代碼都在這裏,然後我們再來一行一行理解:

ZipOutputStream(zipfile.outputStream()).use { zos ->

這裏就相當於在電腦上創建了一個文件“壓縮示例.zip”,此時這是一個空文件,裏面什麼東西都沒有。

val zipEntry = ZipEntry(rawfile.name)
zos.putNextEntry(zipEntry)

這相當於往“壓縮示例.zip”裏面創建了一個“壓縮示例.txt”文件,這也是一個空文件,圖形顯示如下:
在這裏插入圖片描述
這裏是我憑空捏造的一個圖形界面,以方便大家理解代碼的含義,此時有了壓縮文件(壓縮示例.zip),壓縮文件裏面也有了我們的文本文件(壓縮示例.txt),但是文件是空的,真正的文本文件內容還沒存進去呢。

rawfile.inputStream().use { ins ->
	val array = ByteArray(8192)
	var len: Int
	while (ins.read(array).also { len = it } != -1) {
		zos.write(array, 0, len)
	}
}

如上代碼,從ins(InputStream)中讀取文件內容,並使用zos(ZipOutputStream)寫到文件中,寫到文件中的內容就是壓縮之後的內容,代碼執行完成後,圖形化界面如下:
在這裏插入圖片描述
可以看到,上圖壓縮文件中的“壓縮示例.txt”已經有了數據,壓縮前大小爲301.7KB,壓縮後的大小爲1.2KB,壓縮率驚人啊!

就是這麼簡單,文件壓縮我們就講完了,接下來再來學習一些細節就很容易了

設置文件的修改時間

在這裏插入圖片描述
壓縮的時候如果沒有指定文件的修改時間,則會以系統當前的時間做爲修改時間,我們希望和原文件的修改時間保持一致。

ZipEntry就代表了壓縮文件裏面的文件,所以要修改壓縮文件裏面的文件的信息時就可以找ZipEntry就行了。如下:

zipEntry.time = rawfile.lastModified()

當然,文件還有創建時間和訪問時間,在文件上右擊並選擇屬性即可查看,如下:
在這裏插入圖片描述
百度了一下,這些屬性是可以獲取到的,JDK本身有提供這樣的方法,如下:

BasicFileAttributes att = Files.readAttributes(p, BasicFileAttributes.class);
att.creationTime(); 
att.lastAccessTime(); 
att.lastModifiedTime();

但是這些方法在Android中被閹割掉了,也無所謂了,而且在Windows電腦上顯示的時間也是修改時間,如下:
在這裏插入圖片描述
就算不閹割這些方法也沒有用,據說在lunix系統下創建的文件只有最後修改時間,是沒有創建時間這個屬性的(不知道是不是真的)。如果說有公司有要求一定要知道文件的創建時間,解決方案也很簡單,在創建文件的時候,把當前時間加到文件名上即可。

設置壓縮級別

在這裏插入圖片描述
如上圖,壓縮配置有速度最快、體積最小,如果選擇自定義還會看到有存儲、最快、較快、標準、較好、最好等級別,最好的意思是壓縮的最厲害,壓縮的文件最小,但是需要的壓縮時間也是最長的,在代碼中,寫入壓縮的數據是通過ZipOutputStream流來寫的,所以這個類要知道壓縮級別,找這個類上的方法即可,如下:

zipOutputStream.setLevel(Deflater.BEST_SPEED)       // 無壓縮
zipOutputStream.setLevel(Deflater.BEST_SPEED)       // 最快的壓縮
zipOutputStream.setLevel(Deflater.BEST_COMPRESSION) // 最好的壓縮

setLevel接收的參數是int類型,取值範圍是0 ~ 9,0是無壓縮,就是可以把1個或幾個文件打包到一個zip文件中,但是文件數據沒有進行壓縮,1 ~ 9就是壓縮等級,9代表最好的壓縮等級,需要的壓縮時間也就最長。這個一般不用設置,用默認的就挺好了。我項目中使用了最好的壓縮,因爲我壓縮的是一些日記文件,內容都不算大,雖然選了最了最好的壓縮,但是時間也很快,壓縮的小一些,時間是長了一點,但是上傳的時間也快了一點。

設置壓縮文件的註釋

zipOutputStream.setComment(String comment) 

效果如下:
在這裏插入圖片描述
爲什麼不寫中文?因爲會有中文亂碼,怎麼解決中文亂碼呢,這個我懶得管了,公司也沒要求要加這個,所以真實開發就是這樣的,用到什麼學什麼,用不到的,不學也罷!

把多個文件壓縮到一個文件中

zipOutputStream.putNextEntry(zipEntry)
zipOutputStream.write(bytes)
zipOutputStream.closeEntry()

關鍵就是putNextEntry就代表往壓縮文件裏放一個文件,然後write開始寫入這個文件的內容,寫完之後調用closeEntry,再來第二個文件時就重複一樣的流程即可,完整示例如下:

fun main() {
    val rawfile_1 = File("C:\\Users\\even\\Desktop\\logs\\壓縮示例.txt")
    val rawfile_2 = File("C:\\Users\\even\\Desktop\\logs\\Hello.txt")
    val zipfile = File("C:\\Users\\even\\Desktop\\logs\\壓縮示例.zip")
    val rawFileList = listOf(rawfile_1, rawfile_2)

    ZipOutputStream(zipfile.outputStream()).use { zos ->
        rawFileList.forEach { rawFile ->
            zos.putNextEntry(ZipEntry(rawFile.name))
            rawFile.inputStream().use { ins ->
                val array = ByteArray(8192)
                var len: Int
                while (ins.read(array).also { len = it } != -1) {
                    zos.write(array, 0, len)
                }
            }
            zos.closeEntry()
        }
    }
}

效果如下:
在這裏插入圖片描述

遞歸壓縮一個目錄下的所有內容

相信有了上面的基礎知識,你要完成遞歸壓縮一個目錄下的所有內容,應該也是簡簡單單了,公司裏沒這需求,我也懶得寫代碼去實現了。

OK,就講到這了,雖然類上面還有很多其他方法,但是對於我目前項目的需求來說已經夠用了,我也懶得去研究那些方法幹嘛用的,人生苦短,該懶的時候就要懶!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章