Java.io.FileNotFoundException: /storage/emulated/0/New file.txt:打开失败:EACCES(权限被拒绝)

84

我一直在尝试加密文件并将这些文件写回到同一位置。但是我收到了错误消息,提示 "java.io.FileNotFoundException: /storage/emulated/0/New file.txt: open failed: EACCES (Permission denied)"

我的Manifest文件如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.tdk.mytestapplication2">

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


<application
    android:allowBackup="true"

我认为我在那里提供了正确的权限。而我用来加密文件的代码是这样的。

public static void encrypt(SecretKey secretKey, String filePath){
    try {
        // Here you read the cleartext.
        FileInputStream fis = new FileInputStream(filePath);
        // This stream write the encrypted text. This stream will be wrapped by another stream.
        FileOutputStream fos = new FileOutputStream(filePath);

        // Create cipher
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        // Wrap the output stream
        CipherOutputStream cos = new CipherOutputStream(fos, cipher);

        // Write bytes
        int b;
        byte[] d = new byte[8];
        while ((b = fis.read(d)) != -1) {
            cos.write(d, 0, b);
        }

        // Flush and close streams.
        cos.flush();
        cos.close();
        fis.close();

    }catch(IOException e){
        e.printStackTrace();
    }catch (NoSuchAlgorithmException e){
        e.printStackTrace();
    }catch(NoSuchPaddingException e){
        e.printStackTrace();
    }catch(InvalidKeyException e){
        e.printStackTrace();
    }
}

我在一个按钮里使用了这个方法

Button btnEncrypt = (Button) findViewById(R.id.btnEnc);
    btnEncrypt.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            aesKey = EncAndDec.generateKey();
            String filePath = editText.getText().toString();
            //Generating the file hash
            String md5Hash = MD5Hash.getMD5(filePath);

            System.out.println(aesKey.toString());
            System.out.println(filePath);
            System.out.println(md5Hash);

            //Encrypting the file
            for(int i=1; i<100; i++) {
                EncAndDec.encrypt(aesKey, filePath);
            }
        }
    });

但我仍然无法解决这个错误。请有人帮忙!


https://dev59.com/fFwY5IYBdhLWcg3waXS5 - CommonsWare
请确认一下:文件 /storage/emulated/0/New file.txt 是否存在?在您浏览设备时,是否能看到它? - ishmaelMakitla
@ishmaelMakitla 嗯,由于消息是“权限被拒绝”,因此该文件确实存在(如果不存在,消息将是“找不到文件”) - niceman
问题在于你的错误信息包括 java.io.FileNotFoundException: /storage/emulated/0/New file.txt...,因此我想问一下你是否确认该文件确实存在 - 如果是这样,那么错误信息可能会误导你。但在你的情况下似乎有些奇怪,因为你已经声明了权限,但仍然收到了 Permission Denied 的错误提示 - 所以请再次检查文件是否存在。 - ishmaelMakitla
@Tharindu 你正在运行哪个版本的Android? - Bryan
显示剩余2条评论
5个回答

141
如果您正在运行Android 29,则必须使用作用域存储。但是,目前您可以通过在应用程序标签中使用以下代码来绕过此问题:android:requestLegacyExternalStorage="true"

1
你真的救了我,但我在安卓5上仍然有问题。 - Islam Assem
2
无法相信他们默认放置了 false... 真是个笑话,我花了两个小时在上面。 - Zhar
它能工作,但这只是一个临时解决方案吗?我已经添加了清单权限和应用程序内使用权限,但仍需要旧版权限... 这感觉不对。 - Mete
这很奇怪,但是在添加了这行代码(以及WRITE_EXTERNAL_STORAGE的运行时权限)之后,现在我能够看到ExternalRootDirectory路径,之前它是未定义的。然而,当我打开目录阅读器时,它显示为0个计数,请问有什么建议吗?看起来,在Android30 API上运行时,与29相同的问题。 - Dharmesh
3
这个解决方案是临时的,在Android 11上无效。你需要使用MediaStore类。点击这个链接获取永久解决方案。https://sujeetkumargpt06.medium.com/storage-access-in-android-11-and-above-scoped-storage-8dc439e87441 - Sujeet
显示剩余5条评论

61

我猜您正在运行 Android 6.0 Marshmallow(API 23)或更高版本。如果是这种情况,您在尝试读/写外部存储之前必须实现运行时权限


3
只有当目标SDK版本号为23或更高时才成立。 - devnull69
1
我的目标SDK版本是19,但模拟器是23。 - Tharindu
@Tharindu Hm,正如@devnull69所提到的那样,如果您的targetSdkVersion为22或更低,则仍应使用旧的权限模型。这可能不是问题所在。 - Bryan
@Bryan 刚刚安装完一些组件,还在安装中。我会尝试运行并在这里更新。 - Tharindu
@Bryan 我已经降级到 Android APK 19 KitKat,现在它能正常工作了。感谢你的帮助。 - Tharindu
显示剩余5条评论

24

实现运行时权限以在Android 6.0 Marshmallow (API 23)或更高版本上运行您的应用程序。

或者您可以手动启用存储权限-

前往 设置 > 应用程序 > "your_app_name" > 点击它 > 然后点击权限 > 然后启用存储权限。就这样。

但我建议选择第一种方法,即在您的代码中实现运行时权限。


2
实际上,如果您只需要为调试目的保存一些东西,第二个建议要好得多。 - Łukasz Sromek
是的,它可以用于调试目的。@ŁukaszSromek - Debasish Mondal
针对Android 11.0或更高版本您有什么想法? - Arpan24x7

16

在Android 11上,应用程序不能再访问外部存储器中任何其他应用程序专用的、专门的目录中的文件。

为了保护用户隐私,在运行Android 11或更高版本的设备上,系统进一步限制了您的应用程序对其他应用程序私有目录的访问权限。

请求MANAGE_EXTERNAL_STORAGE权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" 
tools:ignore="ScopedStorage"/>

请求外部存储权限

ActivityCompat.requestPermissions( this,
new String[]{
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.MANAGE_EXTERNAL_STORAGE
}, 1
);

检查 MANAGE_EXTERNAL_STORAGE 权限

// If you have access to the external storage, do whatever you need
if (Environment.isExternalStorageManager()){

// If you don't have access, launch a new activity to show the user the system's dialog
// to allow access to the external storage
}else{
  Intent intent = new Intent();
  intent.setAction(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
  Uri uri = Uri.fromParts("package", this.getPackageName(), null);
  intent.setData(uri);
  startActivity(intent);
}

1
避免使用 MANAGE_EXTERNAL_STORAGE,否则您的应用可能无法在Play商店上被接受。 - ismail alaoui
请考虑迁移到有范围存储 (scoped storage) 来避免使用危险的 MANAGE 权限。 - amartin1911

2

对于SDK 29:

String str1 = "";
folder1 = new File(String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)));
if (folder1.exists()) {str1 = folder1.toString() + File.separator;}

public static void createTextFile(String sBody, String FileName, String Where) {
    try {
        File gpxfile = new File(Where, FileName);
        FileWriter writer = new FileWriter(gpxfile);
        writer.append(sBody);
        writer.flush();
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

然后你可以像这样保存文件:
createTextFile("This is Content","file.txt",str1);

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