前言
當前項目中,有一個需求,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,就講到這了,雖然類上面還有很多其他方法,但是對於我目前項目的需求來說已經夠用了,我也懶得去研究那些方法幹嘛用的,人生苦短,該懶的時候就要懶!