Android M写入SD卡 - 权限被拒绝

时间:2017-03-28 09:44:33

标签: android android-6.0-marshmallow

我正在尝试将文件从我的应用程序中复制到SD卡,但我收到错误 eacces(权限被拒绝)。操作系统是Android M,我已经允许运行时存储权限(在应用信息中检查)。我还在AndroidManifest.xml中设置了uses-permission

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

如果我复制到SD卡

,则无效
Source: data/user/0/com.example.myapp/cache/SomeFile.txt
Destination: /storage/1032-2568/SomeFolder/
Error: java.io.FileNotFoundException: /storage/1032-2568/SomeFolder/SomeFile.txt: open failed: EACCES (Permission denied)

如果我复制到内部存储

,则有效
Source: data/user/0/com.example.myapp/cache/SomeFile.txt
Destination: /storage/emulated/0/SomeFolder/

将文件从源复制到目标的代码

/*
 * Below are the parameters I have tried
 *
 * inputPath - data/user/0/com.example.myapp/cache or data/user/0/com.example.myapp/cache/
 * inputFile - /SomeFile.txt or SomeFile.txt
 * outputPath - /storage/1032-2568/SomeFolder/ or /storage/1032-2568/SomeFolder
 */
public static void copyFile(String inputPath, String inputFile, String outputPath) {

    InputStream in = null;
    OutputStream out = null;
    try {

        //create output directory if it doesn't exist
        File dir = new File (outputPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        in = new FileInputStream(inputPath + inputFile);
        out = new FileOutputStream(outputPath + inputFile);

        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();


        // write the output file (You have now copied the file)
        out.flush();
        out.close();
    }
    catch (FileNotFoundException fnfe1) {
        /* I get the error here */
        Log.e("tag", fnfe1.getMessage());
    }
    catch (Exception e) {
        Log.e("tag", e.getMessage());
    }
}

ES文件资源管理器

我看到ES文件资源管理器也无法在Redmi设备上的SD卡上写任何内容。 Here's a video with solution。按照我的设备上的ES Explorer的步骤。这可以通过编程方式完成吗?

8 个答案:

答案 0 :(得分:18)

正如@CommonsWare所建议的那样,我们必须使用android提供的新存储访问框架,并且必须获得用户的许可才能编写SD卡文件,如您所说,这已经在文件管理器应用程序ES文件资源管理器中编写。

以下是让用户选择“SD卡”的代码:

startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), requestCode);

看起来有点像这样:

enter image description here

pickedDir中获取文档路径并在copyFile块中进一步传递 并使用此路径写入文件:

public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
    if (resultCode != RESULT_OK)
        return;
    else {
        Uri treeUri = resultData.getData();
        DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
        grantUriPermission(getPackageName(), treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        copyFile(sdCard.toString(), "/File.txt", path + "/new", pickedDir);
    }
}


public void copyFile(String inputPath, String inputFile, String outputPath, DocumentFile pickedDir) {

    InputStream in = null;
    OutputStream out = null;
    try {

        //create output directory if it doesn't exist
        File dir = new File(outputPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        in = new FileInputStream(inputPath + inputFile);
        //out = new FileOutputStream(outputPath + inputFile);

        DocumentFile file = pickedDir.createFile("//MIME type", outputPath);
        out = getContentResolver().openOutputStream(file.getUri());

        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }
        in.close();


        // write the output file (You have now copied the file)
        out.flush();
        out.close();
    } catch (FileNotFoundException fnfe1) {
    /* I get the error here */
        Log.e("tag", fnfe1.getMessage());
    } catch (Exception e) {
        Log.e("tag", e.getMessage());
    }
}

答案 1 :(得分:9)

您需要在Android 6.0(API级别23)及更高版本中添加权限请求运行时,这里是official文档

这是WRITE_EXTERNAL_STORAGE

的代码
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
    Log.d(TAG,"Permission is granted");

    return true;
}

请求像这样的其他许可

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

答案 2 :(得分:1)

我可以看到您正在复制一个文件的整个内容并尝试将其写入另一个文件。我可以建议一个更好的方法:

假设您已经检查过文件是否存在

StringWriter temp=new StringWriter();
        try{
            FileInputStream fis=new FileInputStream(inputFile+inputPath);
            int i;
            while((i=fis.read())!=-1)
            {
                temp.write((char)i);
            }
            fis.close();
            FileOutputStream fos = new FileOutputStream(outputPath, false);  // true or false based on opening mode as appending or writing
            fos.write(temp.toString(rs1).getBytes());
            fos.close();
        }
        catch (Exception e){}

此代码适用于我的应用程序...如果这对您有用,请告诉我..

答案 3 :(得分:1)

我也遇到了这个问题,但我通过在运行时使用请求权限并强行给予权限后解决。在Android设备的应用信息中获得权限。声明manifest =&gt;中的权限后,转到设备的设置=&gt;转到app info =&gt;转到权限=&gt; 并最终允许许可。只记得我刚刚谈到api 22级意味着来自棉花糖。

答案 4 :(得分:1)

似乎运行时权限已正确实现,但问题似乎来自设备 如果您使用的是Redmi,则必须在Redmi安全设置中手动允许特定应用程序的权限 此link显示了如何在redmi安全性中启用权限

答案 5 :(得分:1)

在某些设备上运行Android 4.3后,您无法直接对SD卡上的FileSystem进行写入访问。

您应该使用storage access framework

答案 6 :(得分:0)

您无法复制或删除文件&amp;使用第三方应用程序在外部存储上的文件比如[文件浏览器]。

KITKAT 版本之后更新了其数据政策。

如果仅允许在系统应用上使用。因此,您可以使用原始文件资源管理器(来自ROM)。

如果您需要使用第三方应用,那么 ROOT 您的设备。 (需要root权限)

答案 7 :(得分:0)

从Api 23及以上版本,您需要明确要求权限。正如您所提到的那样,您正在请求运行时权限。我建议你这样做:

在你的片段或活动中

 if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED)

        requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                PERMISSION_CODE);
    else
        copyFile(params...);

然后在Activity / Fragment

中覆盖此方法
     @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            switch (requestCode) {
                case PERMISSION_CODE:
                    copyFile(params...);
                    break;
            }
        }

// if user checks "don't show again" and presses deny on the permission dialog 
// then it will not show up again, in that case you need to show some message to the user 
// to go in the settings and enable permissions manually

         else if (!ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                ToastManager.showLongToast(getActivity(), "Go to app settings and turn on permissions to enjoy complete features");
            }