为什么 MediaProvider 间歇性地拒绝外部文件访问权限?

Why does MediaProvider intermittently refuse external file access permission?

提问人:Lee 提问时间:10/10/2023 最后编辑:oolyviLee 更新时间:10/11/2023 访问量:65

问:

在运行 Android 6 的 Pixel 13a 上,在创建/访问/删除文件时,我有时会进入 Logcat 日志MediaProvider: Permission to access file storage/emulated/0/Documents/com.myappname/myfilename is denied

为了提供一些上下文,下面的 Java 代码是在自定义 Cordova 插件中调用的。早期的测试没有发现这个问题。即使是现在,这也只发生在某些难以定义的情况下。每当它失败时,它都会记录上述消息,我认为这意味着问题不在于调用此消息的更高级别的 Javascript 和 Cordova 中间层。

有时 Java 可以成功创建文件并读取它,但在尝试删除它时会失败,我什至无法使用 Android 自己的文件应用程序删除该文件,但后来我可以了。

我认为文件可能仍在缓冲中,并尝试在连续请求之间引入延迟,尽管这并没有解决它。

我还想知道这是否可能是作用域存储问题,但为什么它有时会起作用,而其他时候却不起作用?

Android清单(phrasefix).xml:

...
    <uses-sdk
        android:minSdkVersion="24"
        android:targetSdkVersion="33" />
...
    <uses-permission android:name="ANDROID.PERMISSION.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="ANDROID.PERMISSION.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="ANDROID.PERMISSION.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
...

离线数据文件:.java:

public class OfflineDataFile {  
    
    private final String filePath;

    public OfflineDataFile(Context context) {
        String pkg = context.getPackageName();
        String fileDir = pkg+".OfflineData";
        filePath = "/Documents/" + fileDir;
    }

    private File getAppDir() throws IOException {
        String state = Environment.getExternalStorageState();
        if ( Environment.MEDIA_MOUNTED.equals( state ) ) {
                return new File( Environment.getExternalStorageDirectory() + filePath );
        } else {
            throw new IOException("Could not mount storage");
        }
    }

    public void write(String filename, byte[] data) throws IOException {
        File appDirectory = getAppDir();

        if ( !appDirectory.exists() ) {
            appDirectory.mkdirs();
        }

        File dataFile = new File( appDirectory, filename );
        dataFile.createNewFile();

        FileOutputStream fos = new FileOutputStream(dataFile);
        fos.write(data);
        fos.close();
    }

    public byte[] read(String filename, Integer size) throws IOException {
            
        File appDirectory = getAppDir();

        if ( !appDirectory.exists() ) {
            throw new IOException("Data directory does not exist");
        }

        File dataFile = new File( appDirectory, filename );
        InputStream input = new FileInputStream(dataFile);
        byte [] data = OfflineDataUtil.readInputStream(input, size);
        input.close();
        return data;
    }

    protected void remove(String filename) {
        File appDirectory;

        try {
            appDirectory = getAppDir();
        } catch (IOException e) {           
            return;
        }

        if ( !appDirectory.exists() ) {
            return;
        }

        File dataFile = new File( appDirectory, filename );

        if ( !dataFile.exists() ) {
            return;
        }

        dataFile.delete();
    }

}
Android 文件 权限 android-permissions

评论

1赞 blackapps 10/10/2023
if ( Environment.MEDIA_MOUNTED.equals......您可以删除该语句,因为外部存储始终可用。
1赞 blackapps 10/10/2023
if (!appDirectory.exists() ) { throw new IOException.....奇怪的结构。在调用 read() 函数之前,应检查该文件是否存在。
0赞 blackapps 10/10/2023
我也想知道为什么/在哪里会有 MediaProvider 进来。
0赞 Lee 10/10/2023
@blackapps “在 Android 11 或更高版本中......MediaProvider 成为外部存储的文件系统处理程序(用于 FUSE),“作用域存储

答:

0赞 Lee 10/10/2023 #1

为了解决这个问题,我决定使用私有外部存储而不是共享外部存储,因为这些文件仅供应用程序在内部使用,因此将其重写如下(现在根据注释进行编辑)。

离线数据文件:.java:

public class OfflineDataFile {  
    
    private final File appDir;

    public OfflineDataFile(Context context) throws IOException {
        appDir = context.getExternalFilesDir("OfflineData");
    }

    public void write(String filename, byte[] data) throws IOException {

        File dataFile = new File( appDir, filename );

        FileOutputStream fos = new FileOutputStream(dataFile);
        fos.write(data);
        fos.close();
    }

    public byte[] read(String filename, Integer size) throws IOException {

        File dataFile = new File( appDir, filename );
        
        InputStream input = new FileInputStream(dataFile);
        byte [] data = OfflineDataUtil.readInputStream(input, size);
        input.close();
        
        return data;
    }

    protected void remove(String filename) throws IOException {

        File dataFile = new File( appDir, filename );
        
        if (!dataFile.delete()) {
            throw new IOException("Unable to delete file");
        }
    
    }

}

评论

1赞 blackapps 10/10/2023
appDir = new File( dirPath );更改为 appDir = getExternalFilesDir(“OfflineData”);
1赞 blackapps 10/10/2023
dataFile.createNewFile();您可以删除该语句。
1赞 blackapps 10/10/2023
if ( !appDir.exists() ) { appDir.mkdirs(); }您可以将其全部删除。或者用 if ( !appDir.exists() ) { if(!appDir.mkdirs()) return ;
1赞 blackapps 10/10/2023
并且:if(!dataFile.delete()) 返回 false 并大喊“哦不!