当前项目中,有一个需求,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.NO_COMPRESSION) // 无压缩
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,就讲到这了,虽然类上面还有很多其他方法,但是对于我目前项目的需求来说已经够用了,我也懒得去研究那些方法干嘛用的,人生苦短,该懒的时候就要懒!