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

Android压缩DIRECTORY_PICTURES内图片文件夹遇EPERM权限错误求助

问题描述

我需要将存储在DIRECTORY_PICTURES目录下、包含.jpg图片的文件夹压缩为ZIP文件,当前代码如下:

val picPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES  + "/" + PaUnitEquipmentFragment.equipComp + " " + PaUnitEquipmentFragment.report + " " + date)
var data = ByteArray(1024)
if(picPath.listFiles() != null ) {
    picPath.listFiles().forEach {
        var outDest = FileOutputStream(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES  + "/" + PaUnitEquipmentFragment.equipComp + " " + PaUnitEquipmentFragment.report + " " + date + ".zip"))
        var out = ZipOutputStream(outDest)
        var fi = FileInputStream(it)
        var origin = BufferedInputStream(fi)
        var entry = ZipEntry(it.toString())
        out.putNextEntry(entry)
        origin.copyTo(out, 1024)
    }
}

运行时出现EPERM(操作不允许)错误,报错信息:

Caused by: java.io.FileNotFoundException: /storage/emulated/0/Pictures/ PA External Visual Inspection 02-13-2024.zip: open failed: EPERM (Operation not permitted)

我的应用目标API为34,最低API为33——我理解在此API级别下无需申请WRITE权限,且通过MediaStore创建待压缩图片文件夹时未出现权限问题。想知道代码是否存在遗漏错误?是否需要在Manifest中添加权限?


代码层面的核心错误

  • 重复创建Zip流导致文件冲突:在forEach循环内每次遍历文件都新建ZipOutputStream和FileOutputStream,这会反复打开同一个ZIP文件,既会覆盖之前的写入内容,还会触发文件锁定,直接引发EPERM权限错误。必须把ZIP流的创建移到循环外部,写完所有文件后统一关闭。
  • ZipEntry路径错误:用it.toString()作为条目名称,会把文件的完整绝对路径写入ZIP,解压后会生成冗余的目录层级。应该使用文件的相对名称(比如仅文件名,或相对于压缩根目录的路径)。
  • 未关闭流导致资源泄漏:代码未关闭任何输入输出流,不仅浪费系统资源,还可能导致文件长时间被锁定,引发后续操作的权限问题。

权限相关说明

针对API 33+:

  • 确实无需申请WRITE_EXTERNAL_STORAGE权限,但读取公共Pictures目录下的图片文件,需要在Manifest中添加READ_MEDIA_IMAGES权限,并在运行时请求该权限——你用MediaStore创建文件夹没问题,但压缩时需要读取文件夹内的图片,这一步必须有读权限。

修正后的代码示例

val picFolder = Environment.getExternalStoragePublicDirectory(
    Environment.DIRECTORY_PICTURES + "/" + PaUnitEquipmentFragment.equipComp + " " + PaUnitEquipmentFragment.report + " " + date
)
val zipFile = File(
    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
    "${PaUnitEquipmentFragment.equipComp} ${PaUnitEquipmentFragment.report} $date.zip"
)

// 先校验文件夹状态
if (picFolder.exists() && picFolder.isDirectory && picFolder.listFiles()?.isNotEmpty() == true) {
    // 使用use语法自动关闭流
    FileOutputStream(zipFile).use { outDest ->
        ZipOutputStream(outDest).use { zipOut ->
            picFolder.listFiles()?.forEach { file ->
                // 仅处理JPG文件
                if (file.isFile && file.name.endsWith(".jpg", ignoreCase = true)) {
                    // 用文件名作为ZipEntry名称,避免冗余路径
                    val entry = ZipEntry(file.name)
                    zipOut.putNextEntry(entry)
                    
                    FileInputStream(file).use { fis ->
                        BufferedInputStream(fis).use { bis ->
                            bis.copyTo(zipOut, 1024)
                        }
                    }
                    zipOut.closeEntry()
                }
            }
        }
    }
}

权限配置步骤

  1. 在AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
  1. 在代码中运行时请求权限:
private const val REQUEST_CODE_READ_IMAGES = 1001

// 检查并请求权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
    requestPermissions(arrayOf(Manifest.permission.READ_MEDIA_IMAGES), REQUEST_CODE_READ_IMAGES)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 03:53:17