我正在尝试在 Kotlin 中创建一个 zip 文件。 这是代码:
fun main(args: Array<String>) {
var files: Array<String> = arrayOf("/home/matte/theres_no_place.png", "/home/matte/vladstudio_the_moon_and_the_ocean_1920x1440_signed.jpg")
var out = ZipOutputStream(BufferedOutputStream(FileOutputStream("/home/matte/Desktop/test.zip")))
var data = ByteArray(1024)
for (file in files) {
var fi = FileInputStream(file)
var origin = BufferedInputStream(fi)
var entry = ZipEntry(file.substring(file.lastIndexOf("/")))
out.putNextEntry(entry)
origin.buffered(1024).reader().forEachLine {
out.write(data)
}
origin.close()
}
out.close()}
zip 文件已创建,但里面的文件已损坏!
如果你使用 Kotlin 的
IOStreams.copyTo()
扩展,它会为你完成复制工作,这最终对我有用。
所以替换这个:
origin.buffered(1024).reader().forEachLine {
out.write(data)
}
有了这个:
origin.copyTo(out, 1024)
我也遇到了
ZipEntry
有一个前导斜杠的问题,但这可能只是因为我使用的是 Windows。
注意:我最终不需要致电
closeEntry()
才能使其正常工作,但建议这样做。
我做了一个混合:
fun main(args: Array<String>) {
val files: Array<String> = arrayOf("/home/matte/theres_no_place.png", "/home/matte/vladstudio_the_moon_and_the_ocean_1920x1440_signed.jpg")
ZipOutputStream(BufferedOutputStream(FileOutputStream("/home/matte/Desktop/test.zip"))).use { out ->
for (file in files) {
FileInputStream(file).use { fi ->
BufferedInputStream(fi).use { origin ->
val entry = ZipEntry(file.substring(file.lastIndexOf("/")))
out.putNextEntry(entry)
origin.copyTo(out, 1024)
}
}
}
}
}
效果完美!
这是一个使用子文件夹的解决方案:
fun addFolderToZip(
folder: String,
destination: String,
zipFileName: String = folder.substring(folder.lastIndexOf("/"))
) {
val folderToZip = File(folder)
var out: ZipOutputStream? = null
try {
out = ZipOutputStream(
BufferedOutputStream(FileOutputStream("$destination/$zipFileName"))
)
recursivelyAddZipEntries(folderToZip, folderToZip.absolutePath, out)
} catch (e: Exception) {
Log.e("ZIP Err", e.message)
} finally {
out?.close()
}
}
private fun recursivelyAddZipEntries(
folder: File,
basePath: String,
out: ZipOutputStream
) {
val files = folder.listFiles() ?: return
for (file in files) {
if (file.isDirectory) {
recursivelyAddZipEntries(file, basePath, out)
} else {
val origin = BufferedInputStream(FileInputStream(file))
origin.use {
val entryName = file.path.substring(basePath.length)
out.putNextEntry(ZipEntry(entryName))
origin.copyTo(out, 1024)
}
}
}
}
我不确定你是否想手动执行此操作,但我发现这个很好的库可以完美运行:
https://github.com/zeroturnaround/zt-zip
这个库是 Java Zip Utils 库的一个很好的包装,其中包括使用单个函数压缩/解压缩文件和目录的方法。
要压缩单个文件,您只需要使用
packEntry
方法:
ZipUtil.packEntry(File("/tmp/demo.txt"), File("/tmp/demo.zip"))
对于压缩目录及其子目录的情况,您可以使用
pack
方法:
val dirToCompress = Paths.get("/path/to/my/dir").toFile()
val targetOutput = Paths.get("/output/path/dir.zip").toFile()
ZipUtil.pack(dirToCompress, targetOutput)
zip 文件应该已在指定的目标输出中创建。
您可以在库的文档中找到更多详细信息和示例。
希望这有帮助=)
1) 您正在将一个空字节数组写入输入文件每一行的
out
。
2)
BufferedReader
中不需要,因为读写字节而不是行就足够了(这会导致解压后的内容与原始内容不匹配)。
3)出现异常时应关闭所有流。使用方法
use
就像 Java 中的 try-with-resources 一样。
4)
val
改为 var
有可能
5)除了快速测试片段之外,不要使用绝对路径。
6)这个片段对于 Kotlin 来说不是惯用的方式(参见 Todd 的回答)
所以这就是它应该如何工作(尽管以 Java 方式):
fun main(args: Array<String>) {
val files: Array<String> = arrayOf("/home/matte/theres_no_place.png", "/home/matte/vladstudio_the_moon_and_the_ocean_1920x1440_signed.jpg")
ZipOutputStream(BufferedOutputStream(FileOutputStream("/home/matte/Desktop/test.zip"))).use { out ->
val data = ByteArray(1024)
for (file in files) {
FileInputStream(file).use { fi ->
BufferedInputStream(fi).use { origin ->
val entry = ZipEntry(file)
out.putNextEntry(entry)
while (true) {
val readBytes = origin.read(data)
if (readBytes == -1) {
break
}
out.write(data, 0, readBytes)
}
}
}
}
}
}
编辑:我已经用我的文件运行了这个片段,并且运行正常。
代码可以稍微清理一下,以分离关注点并更好地利用
use
:
fun File.bufferedOutputStream(size: Int = 8192) = BufferedOutputStream(this.outputStream(), size)
fun File.zipOutputStream(size: Int = 8192) = ZipOutputStream(this.bufferedOutputStream(size))
fun File.bufferedInputStream(size: Int = 8192) = BufferedInputStream(this.inputStream(), size)
fun File.asZipEntry() = ZipEntry(this.name)
fun archive(files: List<File>, destination: File) =
destination.zipOutputStream().use { zipOs ->
files.forEach { file ->
zipOs.putNextEntry(file.asZipEntry())
file.bufferedInputStream().use { bis -> bis.copyTo(zipOs) }
}
}
fun main() {
val files = listOf(
File("/Users/xor/Downloads/Ghibli/kaguyahime006.jpg"),
File("/Users/xor/Downloads/Ghibli/kaguyahime035.jpg")
)
val destination = File("/Users/xor/work/kotlin/scratchpad-kotlin-java/src/main/kotlin/main/archive.zip")
archive(files, destination)
}
这是一个更简单的解决方案,也由 https://stackoverflow.com/a/63828765/3792198
提供fun test() {
val fullPath: String = tempFolder.absolutePath // Folder to be zipped
val zipFilePath = File(baseDirectory, "newTest.zip")// new zip file
zipAll(fullPath, zipFilePath.absolutePath)
}
private fun zipAll(directory: String, zipFile: String) {
val sourceFile = File(directory)
println("directory: $directory")
println("zipFile: $zipFile")
val inputDirectory = sourceFile
val outputZipFile = File(zipFile)
ZipOutputStream(BufferedOutputStream(FileOutputStream(outputZipFile))).use { zos ->
inputDirectory.walkTopDown().forEach { file ->
val zipFileName = file.absolutePath.removePrefix(inputDirectory.absolutePath).removePrefix("/")
val entry = ZipEntry( "$zipFileName${(if (file.isDirectory) "/" else "" )}")
zos.putNextEntry(entry)
if (file.isFile) {
file.inputStream().copyTo(zos)
}
}
}
}
将之前提出的两个选项组合成一个更通用的解决方案,允许您在源文件的存档中设置任意名称
private const val DEFAULT_BUFFER_SIZE: Int = 8 * 1024
fun File.bufferedOutputStream(size: Int = DEFAULT_BUFFER_SIZE) = BufferedOutputStream(this.outputStream(), size)
fun File.zipOutputStream(size: Int = DEFAULT_BUFFER_SIZE) = ZipOutputStream(this.bufferedOutputStream(size))
fun File.bufferedInputStream(size: Int = DEFAULT_BUFFER_SIZE) = BufferedInputStream(this.inputStream(), size)
fun archive(files: Map<String, File>, destination: File) {
destination.zipOutputStream().use { zipStream ->
files.forEach { entry ->
val file = entry.value
val name = if (file.isDirectory) "${entry.key}/" else entry.key
zipStream.putNextEntry(ZipEntry(name))
if (file.isFile) {
file.bufferedInputStream().use { bis -> bis.copyTo(zipStream) }
}
}
}
}
fun use(){
val zip = mutableMapOf<String, File>()
zip.put("internal/3.jpg", File("/home/qixi/3.jpg"))
zip.put("4.jpg", File("/home/qixi/4.jpg"))
archive(zip, File("/home/qixi/file.zip"))
}