open failed: EACCES (Permission denied)'在Android上

我得到了

打开失败:EACCES (Permission denied)

OutputStream myOutput = new FileOutputStream(outFileName);

我检查了根,并尝试了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();
}
562785 次浏览

我希望/data以下的所有内容都属于“内部存储”。然而,你应该能够写/sdcard

我也有同样的问题……<uses-permission在错误的地方。这是正确的:

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

uses-permission标记需要在application标记之外。

除了所有的答案,确保你没有把你的手机用作USB存储器。

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

当您的应用程序属于系统应用程序时,它不能访问SD卡。

事实证明,这是一个愚蠢的错误,因为我的手机仍然连接着台式电脑,并且没有意识到这一点。

所以我不得不关掉USB连接,一切都很好。

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

/system/etc/permission/platform.xml
中更改一个权限属性 和组需要提到如下。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
<group android:gid="sdcard_rw" />
<group android:gid="media_rw" />
</uses-permission>

我也有同样的问题,但没有一个建议有用。但我发现了一个有趣的原因,在物理设备上,Galaxy Tab

当USB存储打开时,外部存储的读写权限没有任何影响。 只要关闭USB存储,并使用正确的权限,你就会解决问题

我在init中/data/下创建了一个文件夹。rc(在Nexus 7上摆弄aosp),就遇到了这个问题。

结果证明,给文件夹rw(666)权限是不够的,它必须是rwx(777),然后它就工作了!

我在运行CM 12.1的三星Galaxy Note 3上也遇到了同样的问题。我的问题是我有

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

而且必须用它来拍摄和存储用户照片。当我试图在ImageLoader中加载这些相同的照片时,我得到了(Permission denied)错误。解决方案是显式添加

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

因为上面的权限只限制写权限到API版本18,以及它的读权限。

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

除了所有答案,如果客户端使用Android 6.0, Android为(Marshmallow)添加了新的权限模型

技巧:如果你的目标版本是22或以下,你的应用程序将在安装时请求所有权限,就像它在任何运行棉花糖以下操作系统的设备上一样。如果你在模拟器上尝试,那么从android 6.0开始,你需要显式地去设置->应用程序-> YOURAPP ->权限,如果你已经给了任何权限,改变权限。

如果你有一个根设备,可以通过以下adb命令绕过6.0后的存储权限强制执行:

root@msm8996:/ # getenforce
getenforce
Enforcing
root@msm8996:/ # setenforce 0
setenforce 0
root@msm8996:/ # getenforce
getenforce
Permissive
我在Galaxy S5 (android 6.0.1)上试图在DCIM/camera文件夹中写入图像时遇到了同样的错误,我发现只有这个文件夹是受限制的。我可以简单地写入DCIM/任何文件夹,但不是在相机。 这应该是基于品牌的限制/定制

在manifest中添加权限。

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

我的问题是“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();
}
}

奇怪的是,在我的newFile中之前放了一个斜杠“/”;之后,我的问题就解决了。我改了这个:

File myFile= new File(Environment.getExternalStorageDirectory() + "newFile");

:

File myFile= new File(Environment.getExternalStorageDirectory() + "/newFile");
< p > 更新: 正如评论中提到的,正确的方法是:

File myFile= new File(Environment.getExternalStorageDirectory(), "newFile");

也许答案是这样的:

在API >= 23个设备上,如果你安装了app (app不是系统app),你应该在“设置-应用程序”中检查存储权限,每个应用程序都有权限列表,你应该检查它!试一试

我有同样的问题(API >= 23)。

解决方案https://stackoverflow.com/a/13569364/1729501为我工作,但它是不实际的断开应用程序调试。

我的解决方案是在Windows上安装合适的adb设备驱动程序。谷歌USB驱动程序不适用于我的设备。

步骤1:下载适合您的设备品牌的adb驱动程序。

步骤2:进入设备管理器->其他设备->查找带有“adb”字样的条目->选择更新驱动程序->在步骤1中给出位置

在我的情况下,我使用文件选择器库返回外部存储的路径,但它从/root/开始。即使在运行时授予了WRITE_EXTERNAL_STORAGE权限,我仍然得到了错误EACCES(拒绝许可)。< br > 因此,使用Environment.getExternalStorageDirectory()来获取外部存储的正确路径 < p >例子:< br > 不能写: /root/storage/emulated/0/newfile.txt < br > 可以写: /storage/emulated/0/newfile.txt

boolean externalStorageWritable = isExternalStorageWritable();
File file = new File(filePath);
boolean canWrite = file.canWrite();
boolean isFile = file.isFile();
long usableSpace = file.getUsableSpace();


Log.d(TAG, "externalStorageWritable: " + externalStorageWritable);
Log.d(TAG, "filePath: " + filePath);
Log.d(TAG, "canWrite: " + canWrite);
Log.d(TAG, "isFile: " + isFile);
Log.d(TAG, "usableSpace: " + usableSpace);


/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}

输出1:

externalStorageWritable: true
filePath: /root/storage/emulated/0/newfile.txt
isFile: false
usableSpace: 0

输出2:

externalStorageWritable: true
filePath: /storage/emulated/0/newfile.txt
isFile: true
usableSpace: 1331007488

我也有同样的经历。我发现,如果你去设置—>应用程序管理器—>您的应用程序—>权限—>启用存储,它解决了这个问题。

添加gradle依赖项

implementation 'com.karumi:dexter:4.2.0'

在主活动中添加以下代码。

import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);


new Handler().postDelayed(new Runnable() {
@Override
public void run() {




checkMermission();
}
}, 4000);
}


private void checkMermission(){
Dexter.withActivity(this)
.withPermissions(
android.Manifest.permission.READ_EXTERNAL_STORAGE,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.INTERNET
).withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.isAnyPermissionPermanentlyDenied()){
checkMermission();
} else if (report.areAllPermissionsGranted()){
// copy some things
} else {
checkMermission();
}


}
@Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
token.continuePermissionRequest();
}
}).check();
}

基于user462990的回答

为了在用户响应权限请求对话框时得到通知,使用下面的代码:(kotlin代码)

override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>,
grantResults: IntArray) {
when (requestCode) {
MY_PERMISSIONS_REQUEST_READ_CONTACTS -> {
// If request is cancelled, the result arrays are empty.
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {


// permission was granted, yay! Do the
// contacts-related task you need to do.


} else {


// permission denied, boo! Disable the
// functionality that depends on this permission.


}
return
}


// Add other 'when' lines to check for other
// permissions this app might request.


else -> {
// Ignore all other requests.
}
}
}
请记住,即使你在manifest中设置了所有正确的权限: 唯一允许第三方应用程序写入你的外部卡的地方是“他们自己的目录” (例如/ sdcard / Android /数据/) 试图写入其他地方:你会得到异常: EACCES(权限被拒绝)

在我的例子中,错误出现在行上

      target.createNewFile();

因为我不能在sd卡上创建一个新文件,所以我不得不使用DocumentFile方法。

      documentFile.createFile(mime, target.getName());

对于上述问题,可以用这种方法来解决,

    fos=context.getContentResolver().openOutputStream(documentFile.getUri());

看到这个线程, 如何使用新的SD卡访问API为Android 5.0(棒棒糖)? < / p >

Environment.getExternalStoragePublicDirectory();

当从Android 29开始使用这个已弃用的方法时,你会收到相同的错误:

java.io.FileNotFoundException: open failed: EACCES (Permission denied)

分辨率:

getExternalStoragePublicDirectory在Android Q中已弃用

谷歌在Android问上有一个新特性:用于外部存储的过滤视图。快速修复用于将此代码添加到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。看到答案的同学请点击上面的链接阅读说明。

Android 10 (API 29)引入了作用域存储。更改清单以请求遗留存储并不是长期解决方案。

当我用context.getExternalFilesDir(null)替换我以前的Environment.getExternalStorageDirectory()实例(API 29已弃用)时,我修复了这个问题。

注意,如果存储位置不可用,context.getExternalFilesDir(type)可以返回null,所以无论何时检查是否具有外部权限,都要检查这一点。

阅读更多在这里

android: requestLegacyExternalStorage = " true "添加到Android Manifest中 它与Android 10 (Q)在SDK 29+
或After migrate Android X.

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

将一个文件存储在应用程序目录之外的directory中是限制API 29+之上。因此,要生成一个新文件或创建一个新文件,使用你的应用程序目录,就像这样

所以正确的做法是:-

val file = File(appContext.applicationInfo.dataDir + File.separator + "anyRandomFileName/")

您可以将任何数据写入这个生成的文件!

上面的file是可访问的,而不是throw any exception,因为它位于你自己的开发应用程序的目录中。

另一个选项是Uriel建议的manifest application tag中的android:requestLegacyExternalStorage="true",但它不是永久的解决方案!

请注意解决方案:

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

是临时的,迟早你的应用程序应该迁移到使用Scoped Storage

在Android 10中,你可以使用建议的解决方案来绕过系统限制,但在Android 11 (R) it is mandatory to use scoped storage中,如果你继续使用旧的逻辑,你的应用程序可能会崩溃!

这个视频可能是一个很好的帮助。

我使用下面的过程来处理android 11和targetapi30的情况

  1. 作为预先创建的文件dir根据范围存储在我的情况下根目录文件//<图像/视频…按要求;

  2. 复制选中的文件,并在从我的外部存储选择时复制缓存目录中的文件

  3. 然后在一次上传(在我的发送/上传按钮点击)复制文件从缓存目录到我的范围存储目录,然后做我的上传过程

使用这个解决方案,因为在播放商店上传应用程序时,它会生成MANAGE_EXTERNAL_STORAGE权限的警告,在我的情况下,有时会被播放商店拒绝。

同样,我们使用目标API 30,所以我们不能从内部存储共享或转发文件到应用程序

我在小米设备上遇到同样的错误(android 10)。下面的代码解决了我的问题。 库:Dexter(https://github.com/Karumi/Dexter)和Image picker(https://github.com/Dhaval2404/ImagePicker)

添加manifest (android:requestLegacyExternalStorage="true")

    public void showPickImageSheet(AddImageModel model) {
BottomSheetHelper.showPickImageSheet(this, new BottomSheetHelper.PickImageDialogListener() {
@Override
public void onChooseFromGalleryClicked(Dialog dialog) {
selectedImagePickerPosition = model.getPosition();
Dexter.withContext(OrderReviewActivity.this)                   .withPermissions(Manifest.permission.READ_EXTERNAL_STORAGE)
.withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) {
ImagePicker.with(OrderReviewActivity.this)
.galleryOnly()
.compress(512)
.maxResultSize(852,480)
.start();
}
}


@Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> list, PermissionToken permissionToken) {
permissionToken.continuePermissionRequest();
}


}).check();


dialog.dismiss();
}


@Override
public void onTakePhotoClicked(Dialog dialog) {
selectedImagePickerPosition = model.getPosition();
ImagePicker.with(OrderReviewActivity.this)
.cameraOnly()
.compress(512)
.maxResultSize(852,480)
.start();


dialog.dismiss();
}


@Override
public void onCancelButtonClicked(Dialog dialog) {
dialog.dismiss();
}
});
}

2022 Kotlin请求许可的方式:

private val writeStoragePermissionResult =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->}


private fun askForStoragePermission(): Boolean =
if (hasPermissions(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
) {
true
} else {
writeStoragePermissionResult.launch(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
)
)
false
}


fun hasPermissions(context: Context, vararg permissions: String): Boolean = permissions.all {
ActivityCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}

将此添加到清单,但此权限具有谷歌播放策略。

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