Open 失败: EACCES (拒绝许可)

我有一个非常奇怪的问题存储访问一些设备。这个应用程序可以在我的测试设备上运行(Nexus4 & 7,Samsung GS5)。我所有的设备都运行安卓4.4。但是我收到了很多用户的电子邮件,他们说这个应用程序不能写入存储器(无论是内部存储器还是 sd 卡)。从用户反馈的日志文件中,我可以看出问题出在以下代码上:

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

在创建 FileOutputStream 时,它在 fStream = new FileOutputStream (filename,true)行引发异常。

堆栈日志是:

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 卡上使用了正确的应用程序。更奇怪的是,它也不能写入内部存储。如果我同时拥有读写权限,怎么会发生这种情况?这些用户说,他们当时并没有将自己的设备连接到电脑上。

更新

事实证明,我调用 open 和 close FileOutputStream 的次数太频繁了,这会在某个时候抛出 FileNotFoundException。听起来更像是线程问题。

183265 次浏览

I ran into a similar issue a while back.

Your problem could be in two different areas. It's either how you're creating the file to write to, or your method of writing could be flawed in that it is phone dependent.

If you're writing the file to a specific location on the SD card, try using Environment variables. They should always point to a valid location. Here's an example to write to the downloads folder:

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

If you're writing the file to the application's internal storage. Try this example:

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

Personally I rely on external libraries to handle the streaming to file. This one hasn't failed me yet.

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

I've lost data one too many times on a failed write command, so I rely on well-known and tested libraries for my IO heavy lifting.

If the files are large, you may also want to look into running the IO in the background, or use callbacks.

If you're already using environment variables, it could be a permissions issue. Check out Justin Fiedler's answer below.

I also faced the same issue. After lot of hard work, I found what was wrong in my case. My device was connected to computer via USB cable. There are types for USB connections like Mass Storage, Media Device(MTP), Camera(PTP) etc. My connection type was - 'Mass Storage', and this was causing the problems. When I changed the connection type, the issue was solved.

Always remember while accessing filesystem on android device :-

DON'T CONNECT AS MASS STORAGE to the computer/pc.

In my case I used the option android:isolatedProcess="true" for a service in the AndroidManifest.xml.

As soon as I removed it, the error disappeared...

Also I found solving for my way.

Before launch app i granted root to file-explorer and did not disable permission on write/read when exit from app.

My app can not use external memory while i did restrat device for resetting all permissions.

In my case I had the wrong case in

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

android.permission must be lowercase, and somehow the entire string was uppercase in our source.

For API 23+ you need to request the read/write permissions even if they are already in your manifest.

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

If the clients are using Android 6.0, Android added new permission model for (Marshmallow).

Trick: If you are targeting version 22 or below, your application will request all permissions at install time just as it would on any device running an OS below Marshmallow

In my case it was permissions issue. The catch is that on device with Android 4.0.4 I got access to file without any error or exception. And on device with Android 5.1 it failed with ACCESS exception (open failed: EACCES (Permission denied)). Handled it with adding follow permission to manifest file:

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

So I guess that it's the difference between permissions management in OS versions that causes to failures.

First give or check permissions like

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

If these two permissions are OK, then check your output streams are in correct format.

Example:

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

In my case the issue was the WIFI Configuration that was static had a conflict with another device using the same IP Address.

I ran into the same problem and found that I have to request the permissions at run time, even if I have declared it in the manifest. Just as stated as Justin Fiedler's answer.

The official documentation about this are here: https://developer.android.com/training/permissions/requesting.html

My implementation is slightly different from Justin Fiedler's answer that it also implement v4 fragment's onRequestPermissionsResult method to handle the permissions request response.

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
}
}
}
}

I got the same issue but Sometimes, the most dificult issue get simple answer.

I recheck the manifest permisions and there WAS_NOT write permision shame of me!!!

in my case i forgot to add / in front of file name after i added i got rid of from it

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

Apps targeting Android Q - API 29 by default are given a filtered view into external storage. A quick fix for that is to add this code in the AndroidManifest.xml:

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

Read more about it here: https://developer.android.com/training/data-storage/compatibility

@Uriel Frankel is correct that the Android 10 storage access has changed. But the right way is not to use legacy storage flag but to request the storage of your app like so:

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

getExternalFilesDir is what you need.

Add these both permission of read and write, to solve this issue

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

Add this below line in Application tag

android:requestLegacyExternalStorage="true"

This error was thrown by another app that I'm sharing my app's file to (using Intent.FLAG_GRANT_READ_URI_PERMISSION)

Turns out, the File Uri has to be provided via FileProvider class as shown here.

I solved this by using another path:

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

For those of you who tried all the above solutions and still doesn't solve your error on API 30. Change targetSdkVersion 30 to 29 in defaultConfig in your build.gradle(app)

Code will look like:-

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

I was able to solve this problem by removing

  tools:ignore="ScopedStorage" />

this line from

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


   

and also you don't need read permission anymore

I had the same problem. My app worked on Android 11, but the error showed up in Android 10. Upgrading to Android 11 solved the problem.

Using Android 12 (API 31), I read in OS docs that we must use Manifest main property :

example:

 uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"

The issue has been solved.