您当前的位置:首页 > 计算机 > 编程开发 > Java

通俗易懂地教你用Java实现文件压缩

时间:02-04来源:作者:点击数:

前言

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

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门