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

Android 11 Base64转PDF时出现open failed: EACCES权限错误如何解决?

Android 11写入公共目录PDF权限错误解决方案

问题根因

Android 11及以上版本采用分区存储机制,原有READ_EXTERNAL_STORAGE、WRITE_EXTERNAL_STORAGE权限无法直接对公共Download目录执行写入操作,即使动态申请了权限也会被系统拦截,触发EACCES (Permission denied)错误。

可选解决方案

方案1:使用应用专属外部存储(最推荐,无需额外权限)

直接将PDF写入应用专属的外部私有目录,该目录默认授予应用完整读写权限,且你已配置的FileProvider已经适配该路径,修改getFilePath方法即可:

private fun getFilePath(filename: String): String {
    // 应用专属外部下载目录,无需申请存储权限即可读写
    val file = File(requireContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "")
    if (!file.exists()) {
        file.mkdirs()
    }
    return file.absolutePath + "/" + filename + ".pdf"
}

如果需要让用户可在系统下载列表中查看该PDF,后续可再通过MediaStore将文件同步插入公共下载目录。

方案2:使用MediaStore写入公共Download目录(无需敏感权限)

如果必须将文件存储到公共Download目录,可通过MediaStoreAPI写入,适配分区存储规则,无需申请特殊权限:

// 替换原有的FileOutputStream写入逻辑
private fun savePdfToPublicDownload(decodedBytes: ByteArray, fileName: String) {
    val contentValues = ContentValues().apply {
        put(MediaStore.MediaColumns.DISPLAY_NAME, "$fileName.pdf")
        put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
        put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
    }
    val uri = requireContext().contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
    uri?.let {
        requireContext().contentResolver.openOutputStream(it)?.use { outputStream ->
            outputStream.write(decodedBytes)
        }
    }
}

方案3:申请所有文件访问权限(不推荐,仅适用于文件管理类应用)

如果你的应用有合理的全存储访问需求,可在AndroidManifest.xml中补充权限配置:

<!-- Android 11-12 全存储访问权限 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />
<!-- Android 13+ 媒体文件读取权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"
    android:minSdkVersion="33" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />

运行时需要引导用户跳转到系统设置页开启「所有文件访问权限」,权限通过后即可正常写入公共目录。

代码优化点

原有generatePDFFromBase64方法未做空参数校验,建议补充校验逻辑避免空指针异常:

private fun generatePDFFromBase64(base64: String?, fileName: String?) {
    // 提前校验空参数
    if (base64.isNullOrEmpty() || fileName.isNullOrEmpty()) {
        return
    }
    try {
        val decodedBytes: ByteArray = Base64.decode(base64, Base64.DEFAULT)
        // 后续写入逻辑
    } catch (e: IOException) {
        Log.e("TAG", "Faild to generate pdf from base64: ${e.localizedMessage}")
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 18:45:03