开放失败:EACCES(权限被拒绝)

时间:2014-05-07 20:30:00

标签: android filenotfoundexception

我在某些设备上存储访问时遇到了一个非常奇怪的问题。该应用程序适用于我的测试设备(Nexus 4& 7,Samsung GS5)。我的所有设备都运行Android 4.4.2。但是我收到了很多用户的电子邮件,说应用程序无法写入存储(内部存储和SD卡)。从用户反馈收到的日志文件中,我可以看到问题是以下代码:

try {
    if (fStream == null) {
    fStream = new FileOutputStream(filename, true);
}
    fStream.write(data, 0, bytes);
    return;
} catch (IOException ex) {
    ex.printStackTrace();
}

它在fStream = new FileOutputStream(filename,true)行抛出异常;在创建FileOutputStream时。

堆栈日志是:

W/System.err( 8147): Caused by: java.io.FileNotFoundException: /storage/emulated/0/my_folder/test_file_name.png: open failed: EACCES (Permission denied)
w/System.err( 8147):    at libcore.io.IoBridge.open(IoBridge.java:409)
W/System.err( 8147):    at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
W/System.err( 8147):    at java.io.FileOutputStream.<init>(FileOutputStream.java:128)
W/System.err( 8147):    at myapp.save(SourceFile:515)
W/System.err( 8147):    ... 8 more
W/System.err( 8147): Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied)
W/System.err( 8147):    at libcore.io.Posix.open(Native Method)
W/System.err( 8147):    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
W/System.err( 8147):    at libcore.io.IoBridge.open(IoBridge.java:393)
W/System.err( 8147):    ... 11 more

在AndroidManifest.xml中,我声明了以下权限:

 <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19"/>
    <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

我已确认用户正在SD卡上使用正确的应用程序专用。而更奇怪的是,它也无法写入内部存储。如果我同时阅读和阅读,怎么会发生这种情况?写权限?用户表示他们当时没有将他们的设备连接到PC。

更新

事实证明我调用open并过于频繁地关闭FileOutputStream,这会在某些时候抛出FileNotFoundException。听起来更像是一个线程问题。

20 个答案:

答案 0 :(得分:121)

对于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" />

答案 1 :(得分:32)

我不久前遇到了类似的问题。

您的问题可能出在两个不同的领域。这是你要创建要写入的文件的方式,或者你的写作方法可能存在缺陷,因为它依赖于手机。

如果您要将文件写入SD卡上的特定位置,请尝试使用环境变量。他们应该总是指向一个有效的位置。这是写入下载文件夹的示例:

java.io.File xmlFile = new java.io.File(Environment
    .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
     + "/Filename.xml");

如果您正在将文件写入应用程序的内部存储。试试这个例子:

java.io.File xmlFile = new java.io.File((getActivity()
   .getApplicationContext().getFileStreamPath("FileName.xml")
   .getPath()));

我个人依赖外部库来处理流式文件。这个还没有让我失望。

org.apache.commons.io.FileUtils.copyInputStreamToFile(is, file);

我在失败的写入命令上丢失了太多次数据,所以我依靠着名且经过测试的库来解决我的IO繁重问题。

如果文件很大,您可能还想查看在后台运行IO,或使用回调。

如果您已经在使用环境变量,则可能是权限问题。查看Justin Fiedler的答案。

答案 2 :(得分:25)

默认情况下,

面向 Android Q-API 29 的应用会获得过滤到外部存储设备的视图。快速解决方案是在AndroidManifest.xml中添加以下代码:

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

在此处阅读有关此内容的更多信息: https://developer.android.com/preview/privacy/scoped-storage

答案 3 :(得分:6)

就我而言,我的错误案例

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

android.permission必须是小写的,并且不知何故整个字符串在我们的源代码中都是大写的。

答案 4 :(得分:5)

我也面临同样的问题。 经过大量的努力,我发现了我的错误。 我的设备通过USB线连接到计算机。 有大量存储类型,媒体设备(MTP),相机(PTP)等USB连接类型。 我的连接类型是 - &#39; Mass Storage&#39;,这导致了问题。 当我更改连接类型时,问题就解决了。

在Android设备上访问文件系统时始终记住: -

不要将大量存储设备连接到计算机/电脑上。

答案 5 :(得分:2)

我遇到了同样的问题,发现我必须在运行时请求权限,即使我已经在清单中声明了它。就像Justin Fiedler的回答一样。

有关此内容的官方文档如下:https://developer.android.com/training/permissions/requesting.html

我的实现与Justin Fiedler的回答略有不同,它还实现了v4片段的onRequestPermissionsResult方法来处理权限请求响应。

public static final int REQUEST_EXTERNAL_PERMISSION_CODE = 666;

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public static final String[] PERMISSIONS_EXTERNAL_STORAGE = {
        READ_EXTERNAL_STORAGE,
        WRITE_EXTERNAL_STORAGE
};

public boolean checkExternalStoragePermission(Activity activity) {
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
        return true;
    }

    int readStoragePermissionState = ContextCompat.checkSelfPermission(activity, READ_EXTERNAL_STORAGE);
    int writeStoragePermissionState = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE);
    boolean externalStoragePermissionGranted = readStoragePermissionState == PackageManager.PERMISSION_GRANTED &&
            writeStoragePermissionState == PackageManager.PERMISSION_GRANTED;
    if (!externalStoragePermissionGranted) {
        requestPermissions(PERMISSIONS_EXTERNAL_STORAGE, REQUEST_EXTERNAL_PERMISSION_CODE);
    }

    return externalStoragePermissionGranted;
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        if (requestCode == REQUEST_EXTERNAL_PERMISSION_CODE) {
            if (checkExternalStoragePermission(getActivity())) {
                // Continue with your action after permission request succeed
            }
        }
    }
}

答案 6 :(得分:2)

首先提供或检查权限,如

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

如果这两个权限都正常,请检查输出流的格式是否正确。

示例:

FileOutputStream fos=new FileOutputStream(Environment.getExternalStorageDirectory()+"/rahul1.jpg");

答案 7 :(得分:2)

就我而言,这是权限问题。 问题是,在使用Android 4.0.4的设备上,我可以访问文件,没有任何错误或异常。在使用Android 5.1的设备上,它因ACCESS异常而失败(打开失败:EACCES(Permission denied))。 通过向清单文件添加以下权限来处理它:

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

所以我猜这是OS版本中权限管理导致失败的区别。

答案 8 :(得分:1)

我遇到了同样的问题但是 有时,最困难的问题得到简单的答案。

我重新检查明显的permisions并且WAS_NOT写了permision 我的耻辱!!!

答案 9 :(得分:1)

就我而言,我在android:isolatedProcess="true"中使用service选项作为AndroidManifest.xml

我一删除它,错误就消失了......

答案 10 :(得分:1)

我能够通过删除来解决这个问题

  android:maxSdkVersion="28"
  tools:ignore="ScopedStorage" />

这一行来自

uses-permission 
   android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28" tools:ignore="ScopedStorage" 

   

而且您不再需要读取权限

答案 11 :(得分:0)

如果客户使用的是Android 6.0,Android会为(Marshmallow)添加新的permission model

诀窍:如果您的目标是22或更低版本,您的应用程序将在安装时请求所有权限,就像在Marshmallow下运行操作系统的任何设备上一样

答案 12 :(得分:0)

在我的情况下,问题是静态的WIFI配置与使用相同IP地址的其他设备发生冲突。

答案 13 :(得分:0)

我也找到了解决方法。

在启动应用程序之前,我将root授予了文件浏览器,并且在退出应用程序时未禁用写入/读取权限。

当我使用restrat设备重置所有权限时,我的应用程序无法使用外部存储器。

答案 14 :(得分:0)

@Uriel Frankel是正确的,Android 10存储访问权限已更改。但是正确的方法不是使用旧存储标志,而是请求存储应用,如下所示:

val screenShotDirPath = getApplication<Application>().getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.path

getExternalFilesDir 是您所需要的。

答案 15 :(得分:0)

添加这两个读写权限,以解决此问题

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

将此行添加到“应用程序”标记中

android:requestLegacyExternalStorage="true"

答案 16 :(得分:0)

我使用另一条路径解决了这个问题:

data/data/[packagename]/cache/...

答案 17 :(得分:0)

对于那些尝试了上述所有解决方案但仍然无法解决 API 30 错误的人。 在您的 targetSdkVersion

中的 30 中将 29 defaultConfig 更改为 build.gradle(app)

代码看起来像:-

defaultConfig {
    applicationId "com.example.company"
    minSdkVersion 16
    targetSdkVersion 29
   .
   .
   .
}

答案 18 :(得分:0)

我遇到了同样的问题。我的应用在 Android 11 上运行,但在 Android 10 中出现错误。升级到 Android 11 解决了问题。

答案 19 :(得分:-2)

在我的情况下,我忘了添加/在文件名前添加我摆脱它

bitmap.compress(Bitmap.CompressFormat.PNG,100,new FileOutputStream(Environment.getExternalStorageDirectory()+"/arjunreddy.png"));