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() } } } } }
权限配置步骤
- 在
AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
- 在代码中运行时请求权限:
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
相关产品推荐
相关产品推荐

