You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ZipOutputStream生成的DOCX文件损坏,macOS无法解压求助

解决ZipOutputStream压缩OfficeOpenXml格式文件夹后解压损坏的问题

你的代码存在几个关键问题,导致生成的Zip文件(DOCX本质是Zip格式)无法被macOS正常解压,尤其是OfficeOpenXml格式对Zip结构的合规性要求更严格:


问题根源

  • 未关闭ZipEntry:写入文件内容后没有调用zipOut.closeEntry()结束当前条目,Zip文件的条目结构不完整,Office工具会判定文件损坏
  • 未关闭FileInputStream:文件输入流使用后未关闭,可能导致数据未完全写入,同时造成资源泄漏
  • 目录条目未正确标记:处理目录时没有创建带斜杠结尾的ZipEntry,部分解压工具无法识别为目录结构

修复后的完整代码

private const val DEFAULT_BUFFER_SIZE = 1024 * 4

fun zip(source: String?, destination: String?, contentsOnly: Boolean, compression: Int) {
    try {
        FileOutputStream(destination).use { fileOut ->
            ZipOutputStream(fileOut).use { zipOut ->
                val srcFile = File(source ?: return)

                // 压缩级别处理
                val compressionLevel: Int = when(compression) {
                    0 -> Deflater.DEFAULT_COMPRESSION
                    1 -> Deflater.NO_COMPRESSION
                    2 -> Deflater.BEST_SPEED
                    3 -> Deflater.BEST_COMPRESSION
                    else -> Deflater.DEFAULT_COMPRESSION
                }

                zipOut.setLevel(compressionLevel)
                zipOut.setMethod(ZipOutputStream.DEFLATED)

                if (contentsOnly && srcFile.isDirectory) {
                    srcFile.list()?.forEach { fileName ->
                        addToZip("", srcFile.resolve(fileName).absolutePath, zipOut)
                    }
                } else {
                    addToZip("", srcFile.absolutePath, zipOut)
                }

                println("Successfully created $destination")
            }
        }
    } catch (error: Exception) {
        println("Failed: ${error.message}")
        error.printStackTrace()
    }
}

private fun addToZip(path: String, srcFile: String, zipOut: ZipOutputStream) {
    val file = File(srcFile)
    val entryPath = if (path.isEmpty()) {
        file.name
    } else {
        "$path${File.separator}${file.name}"
    }

    if (file.isDirectory) {
        // 创建目录条目,路径以斜杠结尾确保解压工具识别
        val dirEntry = ZipEntry("$entryPath${File.separator}")
        zipOut.putNextEntry(dirEntry)
        zipOut.closeEntry()

        file.list()?.forEach { fileName ->
            addToZip(entryPath, file.resolve(fileName).absolutePath, zipOut)
        }
    } else {
        ZipEntry(entryPath).let { entry ->
            zipOut.putNextEntry(entry)
            // 使用use自动关闭输入流
            FileInputStream(file).use { inputStream ->
                val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
                var len: Int
                while (inputStream.read(buffer).also { len = it } != -1) {
                    zipOut.write(buffer, 0, len)
                }
            }
            // 必须关闭当前条目,完成条目写入
            zipOut.closeEntry()
        }
    }
}

修复关键点说明

  1. 自动流管理:使用use函数自动关闭FileOutputStream、ZipOutputStream和FileInputStream,避免资源泄漏和数据写入不完整
  2. 关闭ZipEntry:写入文件内容后调用zipOut.closeEntry(),确保每个Zip条目结构完整
  3. 目录条目处理:为目录创建带斜杠结尾的ZipEntry,让解压工具正确识别并还原目录结构
  4. 安全路径拼接:使用File.resolve()拼接文件路径,避免手动拼接时的错误

内容的提问来源于stack exchange,提问作者Genealogy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 02:26:06