安卓系统出现异常:'open failed: EACCES (Permission denied)'

414

我遇到了以下问题:

打开失败:EACCES(Permission denied)

出错在这一行:OutputStream myOutput = new FileOutputStream(outFileName);

我已经检查了root权限,并尝试添加了 android.permission.WRITE_EXTERNAL_STORAGE 权限,但仍然无法解决问题。

请问该如何修复这个问题?

try {
    InputStream myInput;

    myInput = getAssets().open("XXX.db");

    // Path to the just created empty db
    String outFileName = "/data/data/XX/databases/"
            + "XXX.db";

    // Open the empty db as the output stream
    OutputStream myOutput = new FileOutputStream(outFileName);

    // Transfer bytes from the inputfile to the outputfile
    byte[] buffer = new byte[1024];
    int length;
    while ((length = myInput.read(buffer)) > 0) {
        myOutput.write(buffer, 0, length);
    }

    // Close the streams
    myOutput.flush();
    myOutput.close();
    myInput.close();
    buffer = null;
    outFileName = null;
}
catch (IOException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}

我在一台Android平板电脑上遇到了同样的问题,但我的情况可能是独特的,所以我在评论中报告了我的解决方案。当应用程序尝试访问包含50多个XML文件的目录/data/data/myapp/foo时,出现了错误。由于一个漏洞,该应用程序没有清理文件夹。删除一些旧文件后,错误消失了。我不知道为什么。这可能是通用Android设备的问题。 - Hong
这个帖子解决了我的问题:https://dev59.com/B1wX5IYBdhLWcg3wjwJZ - partho
36个回答

417

谷歌在Android Q中推出了一个新功能:外部存储过滤视图。一个快速的解决方法是在AndroidManifest.xml文件中添加以下代码:

<manifest ... >
    <!-- This attribute is "false" by default on apps targeting Android Q. -->
    <application android:requestLegacyExternalStorage="true" ... >
     ...
    </application>
</manifest>

您可以在此处阅读更多相关信息:https://developer.android.com/training/data-storage/use-cases

编辑:我开始遭到投票下降,因为这个回答已经过时了对于Android 11。因此,看到这个答案的任何人请前往上面的链接并阅读说明。


31
先生,您为我节省了大量时间。在Q中,许多事情对开发人员来说已经变得非常麻烦。我认为这个答案应该作为一个单独的问题发布,并且值得更多的赞同。 - sanjeev
1
哇,你救了我。我一直在尝试各种方法来解决它。我猜测问题是由于 Android 10,而你只用了一行代码就解决了它,谢谢你!(y) - Mister JJ
13
从安卓11开始,这不再有效。 - Roshana Pitigala
5
@RoshanaPitigala,针对安卓11需要做什么?有任何链接吗? - Siddarth G
2
权威机构做出了荒谬的决定!这给开发者带来了额外的痛苦,为什么?难道我们选择安卓就是我们的错吗? - Noor Hossain
显示剩余15条评论

370

对于API 23及以上版本,即使权限已在清单文件中声明,您仍需要请求读写权限。

// Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
};

/**
 * Checks if the app has permission to write to device storage
 *
 * If the app does not has permission then the user will be prompted to grant permissions
 *
 * @param activity
 */
public static void verifyStoragePermissions(Activity activity) {
    // Check if we have write permission
    int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        // We don't have permission so prompt the user
        ActivityCompat.requestPermissions(
                activity,
                PERMISSIONS_STORAGE,
                REQUEST_EXTERNAL_STORAGE
        );
    }
}

AndroidManifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

如果需要请求API 23+的权限的官方文档,请查看https://developer.android.com/training/permissions/requesting.html


13
为了使其正常工作,该操作必须处理活动权限请求响应。请参阅http://developer.android.com/training/permissions/requesting.html#handle-response获取更多详细信息。 - kldavis4
这里有一个方便的链接,提供Android M权限开发者版的解释和示例:http://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en - Paul Alexander
1
这难道不是对用户体验来说一件坏事吗? - M. Usman Khan
5
但是我在哪里使用它?例如,如果我想要拍照片,我是在startActivityForResult之前运行verifyStoragePermissions,还是在onActivityResult中运行它?这真的让我很困惑。 - Kiwi Lee
5
谢谢,这解决了我的问题。 顺便提一句: 如果有人无法解决“Manifest.permission”的问题,只需导入“import android.Manifest”即可。 - Oubaida AlQuraan
显示剩余4条评论

253

我曾经遇到过同样的问题... <uses-permission 放错了位置。这是正确的位置:

 <manifest>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        ...
        <application>
            ...
            <activity> 
                ...
            </activity>
        </application>
    </manifest> 

uses-permission标签需要放在application标签之外。


2
使用权限需要放在应用程序之外。 - user462990
3
我收到了一个警告:“<uses-permission>标签出现在<application>标签之后”。 - mr5
12
警告:在安卓4.4.4中不要使用参数 android:maxSdkVersion="18"。它会导致出现异常。 - guisantogui
1
我正在使用API级别15,尝试将文件移动到OTG USB存储设备时出现错误。 - Ravi Mehta
2
在API 29之后,您需要使用以下内容:https://medium.com/@sriramaripirala/android-10-open-failed-eacces-permission-denied-da8b630a89df - Carlos Barcellos
显示剩余5条评论

74

在 Android Manifest 中添加 android:requestLegacyExternalStorage="true"。 它适用于 Android 10 (Q) 和 SDK 版本 29+
或者在迁移至 Android X 后使用。

 <application
    android:name=".MyApplication"
    android:allowBackup="true"
    android:hardwareAccelerated="true"
    android:icon=""
    android:label=""
    android:largeHeap="true"
    android:supportsRtl=""
    android:theme=""
    android:requestLegacyExternalStorage="true">

太棒了,完美地解决了问题,这正是我一直在寻找的答案。非常感谢@rhaldar,你救了我的一天。 - Ayush Katuwal
5
无法在Android 11上工作 - famfamfam
@famfamfam 您可以参考 https://developer.android.com/about/versions/11/privacy/storage - rhaldar

60
我曾在模拟器中运行应用程序时观察到这种情况。在模拟器设置中,您需要正确指定外部存储("SD卡")的大小。默认情况下,“外部存储”字段为空,这可能意味着没有这样的设备,即使在清单中授予权限,仍会抛出EACCES错误。

53

除了所有的答案之外,确保您不要将手机用作USB存储设备。

我在启用USB存储模式的HTC Sensation上遇到了同样的问题。我仍然可以调试/运行应用程序,但无法保存到外部存储器。


谢谢,即使我在运行时请求了用户权限,这个错误仍然发生在模拟器上。 - Sharp Edge
1
当我连接三星Galaxy S6设备时,它首先会给出一个选项“允许访问设备数据”,并提供“拒绝”或“允许”的选项。同时,从通知栏中我也会得到一个选项:使用USB进行1. MTP 2. PTP 3. MIDI设备 4. 充电。应该选择哪一个? - Testing Singh

34

我的问题出在"TargetApi(23)",如果你的minSdkVersion低于23,则需要它。

因此,我使用以下代码请求权限

protected boolean shouldAskPermissions() {
    return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
}

@TargetApi(23)
protected void askPermissions() {
    String[] permissions = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE"
    };
    int requestCode = 200;
    requestPermissions(permissions, requestCode);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
// ...
    if (shouldAskPermissions()) {
        askPermissions();
    }
}

34

请注意该解决方案:

<application ...
    android:requestLegacyExternalStorage="true" ... >

暂时的,早晚您的应用程序都应该迁移到使用 Scoped Storage

在Android 10中,您可以使用建议的解决方案来绕过系统限制,但是在Android 11 (R)中必须使用作用域存储,如果继续使用旧逻辑,您的应用程序可能会出现问题!

这个视频 可能会对你有所帮助。


27

Android 10(API 29)引入了Scoped Storage。将您的清单更改为请求传统存储不是长期解决方案。

当我用context.getExternalFilesDir(null)替换了以前的Environment.getExternalStorageDirectory()(在API 29中已弃用)时,我解决了这个问题。

请注意,如果存储位置不可用,则context.getExternalFilesDir(type)可能返回null,因此请确保在检查是否具有外部权限时检查该位置。

在此处阅读更多


1
新的错误出现了,ENOENT没有文件或字典。 - reza rahmad
@reza rahmad 我也是。 - CodeToLife
我在日志中输出了它的名称。现在很清楚 Environment.getExternalStorageDirectory(Environment.***)context.getExternalFilesDir(Environment.***) 是不同的路径。就是这样。点赞。 - CodeToLife

20

我也遇到了同样的问题。我发现,如果你进入设置 -> 应用管理器 -> 你的应用 -> 权限 -> 启用存储,就可以解决这个问题。


3
我找不到这个部分? - Evan Sevy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接