Java.io.FileNotFoundException:在设备上打开失败EACCES(权限被拒绝)

25

当设备只有外部存储时,下面的代码从服务器下载文件并将其保存在存储中。但是,如果设备没有内部存储,则会发生以下异常:

java.io.filenotfoundexception open failed eacces (permission denied)

public void downloadFile(String dlUrl, String dlName) {
    int count;

    HttpURLConnection con = null;
    InputStream is = null;
    FileOutputStream fos = null;

    try {
        URL url = new URL( dlUrl );
        con = (HttpURLConnection) url.openConnection();
        con.setDoInput(true);
        con.connect();

        is = url.openStream();
        String dir = Environment.getExternalStorageDirectory() + Util.DL_DIRECTORY;
        File file = new File( dir );
        if( !file.exists() ){
            file.mkdir();
        }

        Util.LOG_W(TAG, "Downloading: " + dlName + " ...");

        fos = new FileOutputStream(file + "/" +  dlName);
        byte data[] = new byte[1024];

        while( (count = is.read(data)) != -1 ){
            fos.write(data, 0, count);
        }

        Util.LOG_D(TAG, dlName + " Download Complete!");


    } catch (Exception e) {
        Util.LOG_E(TAG, "DOWNLOAD ERROR = " + e.toString() );
        bServiceDownloading = false;
    }
    finally{
        try {
            if( is != null)
                is.close();
            if( fos != null)
                fos.close();
            if( con != null)
                con.disconnect();
        } catch (Exception e) {
            Util.LOG_E(TAG, "CLOSE ERROR = " + e.toString() );
        }
    }
}

在清单文件中,我有以下内容:

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

有什么建议可能是原因吗? 顺便说一下,Environment.getExternalStorageDirectory()返回/mnt/sdcard/file.mkdir()返回false。


1
尝试使用 File file = new File( dir + "/" + dlName ); - Pankaj Kumar
很遗憾,结果是一样的。顺便说一下,file.mkdir()返回false,我认为这就是问题所在。@PankajKumar - Lazy Ninja
选择权在你手中...你可以在断开设备与电脑的连接后进行检查。 - Pankaj Kumar
你最终解决了吗? - hB0
1
@hB0 我没有用最理想的方式解决它,但我已经发布了一个解答,说明了我是如何处理这个问题的。希望能对你有所帮助。 - Lazy Ninja
显示剩余5条评论
11个回答

37

此属性在针对Android 10或更高版本的应用程序中默认为“false”。

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

17

这个问题似乎是由几个因素引起的。

检查#1
首先在您的清单文件中添加此权限,并检查它是否起作用:

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

</application>

检查#2:

如果您正在模拟器上运行,请检查属性以查看是否有SD卡。

检查#3:

禁用设备到计算机的文件传输。 如果启用,应用程序将无法访问SD卡。

检查#4:
如果仍然不起作用,请尝试以下操作:

 String dir = Environment.getExternalStorageDirectory().getAbsolutePath()

对我而言,以下解决方案起作用:
问题在于getExternalStorageDirectory返回/mnt/sdcard,但我需要外部存储实际路径/mnt/sdcard-ext,而android没有API可以获取可移动SD卡的绝对路径。
我的解决方法是将目录硬编码如下:

String dir = "/mnt/sdcard-ext" ;

因为该应用程序仅打算在一台设备上运行,因此上述操作完成了任务。
如果您遇到相同的问题,请使用文件管理器应用程序查找外部目录的名称并将其硬编码。


1
这是一个特定于设备的路径(正如您所提到的)。例如:我在这里看到的是String dir =“/mnt/sdcard”。 - hB0
如果您想要访问所有安卓手机的外部存储(或非外部存储)中的文件,您会建议采用什么解决方案? - Raphael Royer-Rivard
从你的回答中,我应该为检查#2做些什么? - Hemang

10

使用READ_EXTERNAL_STORAGE权限从设备中读取数据。


2
感谢您的建议,但不幸的是它并没有解决问题。 - Lazy Ninja
19
任何声明WRITE_EXTERNAL_STORAGE权限的应用程序都会隐式获得此权限。http://developer.android.com/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE - Dan Osipov

6

你有尝试在模拟器上运行吗?检查一下属性是否有SD卡。我曾经遇到过同样的问题,因为模拟器没有SD卡。请检查你的模拟器是否有SD卡。


是的,我正在模拟器上尝试。您所指的属性在哪里检查? - Hemang

4

我曾经遇到过同样的问题,通过禁用从设备到计算机的文件传输来解决了它。因为如果启用文件传输,则SD卡对于调试应用程序不可访问。


谢谢,这对我也是这种情况。 - Lev
1
哦,它解决了我的问题,但是花了3个小时来调试。 - Rahul Matte

3
try
Environment.getExternalStorageDirectory().getAbsolutePath()

不要忘记添加

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

1
我犯了一个非常愚蠢的错误。 我已经把它放在了AndroidManifest.xml中。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

还需要在Java文件中添加权限,以便以编程方式获取权限。 但是Manifest.permission.READ_EXTERNAL_STORAGE存在错误。 请使用Manifest.permission.WRITE_EXTERNAL_STORAGE

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestCode);

请参考以下链接:https://dev59.com/FKLia4cB1Zd3GeqPfz29,返回其整数值。 - Sanket Sangani

1

尝试使用mkdirs而不是mkdir。如果您正在创建一个目录路径且父级不存在,则应使用mkdirs。


1

0

首先在您的清单文件中声明权限:

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

现在在清单文件的应用程序标签头中:

android:requestLegacyExternalStorage="true"

现在在清单文件的标签之间定义应用程序提供程序,如下所示:

 <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_path" />
    </provider>

现在在 res 文件夹中创建一个名为 xml 的文件夹,像这样:参考此图片

现在创建一个名为 provider_path.xml 的 xml 文件,并将以下代码复制到其中:

<?xml version="1.0" encoding="utf-8"?>
<path>
<external-path
    name="external_files"
    path="." />

现在在您的活动中:

String filename = null ;
URL url = null;
try {
                        url = new URL("http://websitename.com/sample.pdf");
                        filename = url.getPath();
                        filename = filename.substring(filename.lastIndexOf('/')+1);

                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    }
  File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+"/"+filename);
                    if(file.exists()){
                        Uri uri = FileProvider.getUriForFile(context, "com.example.www"+".provider",file);
                        Intent i = new Intent(Intent.ACTION_VIEW);
                        i.setDataAndType(uri, "application/pdf");
                        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        context.startActivity(i);
                    }
                    else {
                        //download file here
                        new AlertDialog.Builder(context)
                                .setTitle("Information")
                                .setMessage("Do you want to download this file ?")
                                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        dialog.dismiss();
                                    }
                                })
                                .setPositiveButton("Continue", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url+""));
                                        request.setTitle(filename);
                                        request.setMimeType("application/pdf");
                                        request.allowScanningByMediaScanner();
                                        request.setAllowedOverMetered(true);
                                        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                                        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
                                        DownloadManager downloadManager = (DownloadManager)context.getSystemService(DOWNLOAD_SERVICE);
                                        downloadManager.enqueue(request);
                                    }
                                }).show();

                    }

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