【问题标题】:Android M write to SD Card - Permission DeniedAndroid M 写入 SD 卡 - 权限被拒绝
【发布时间】:2017-04-13 13:01:57
【问题描述】:

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

<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 File Explorer 也无法在 Redmi 设备的 SD 卡上写入任何内容。 Here's a video with solution。按照适用于我设备上的 ES Explorer 的步骤操作。这可以通过编程方式完成吗?

【问题讨论】:

  • 我认为你需要两个:跨度>
  • @David.BC。 WRITE 隐含包含READ,因此没有必要。
  • @Rotwang 我已经允许使用运行时权限的存储权限。
  • 您无权写入removable storage 上的任意位置。使用存储访问框架。
  • 同意 CommonsGuy 所说的。这是您尝试写入的位置的问题。你可以试试这个 Environment.getExternalStorageDirectory().toString()+ "/YourFolder" 作为输出路径吗?

标签: android android-6.0-marshmallow


【解决方案1】:

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

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

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

看起来有点像这样:

并在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());
    }
}

【讨论】:

  • 谢谢!但是第一次之后,如何检查用户是否已经授予权限?还是每次都需要让用户选择路径?
  • 为什么要显示目的地派克?没有这个,其他文件管理器怎么做?
  • 谢谢...非常有帮助。
  • @Choletski 好问题,但我已经尝试过没有这一步,但它没有用。如果您找到任何替代方案,请告诉我们!
【解决方案2】:

您需要在 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);

【讨论】:

    【解决方案3】:

    我也遇到了这个问题,但我通过在运行时使用请求权限并在强行授予权限后解决了这个问题。在 Android 设备的应用信息中获得权限后。在清单中声明权限后 => 进入设备设置 => 进入应用信息 => 进入权限 => 最后允许权限。请记住,我只是在 api 级别 22 之后谈论棉花糖的意思。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        在某些设备上的 Android 4.3 之后,您无法直接访问 SD 卡上的 FileSystem。

        您应该为此使用storage access framework

        【讨论】:

          【解决方案6】:

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

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

          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){}
          

          此代码适用于我的应用程序...让我知道这是否适合您..

          【讨论】:

          • 您能否再粘贴一些代码,例如您尝试对文件进行操作的活动文件……这样我可以更好地帮助您
          • 你说你从应用信息中获得了许可。如果你这样做,那么每次清除应用数据时都必须给予许可。所以我猜你试图复制文件从内部存储中,然后你清除了应用程序的数据。所以它已经失去了对存储的访问权限,这一次没有给予许可,你可能试图访问存储。所以你得到了错误
          • 我已使用运行时权限允许存储权限。而且我不会清除数据。
          • 请显示您的文件处理代码,否则我们如何预测兄弟?
          • 兄弟,我已经更改了答案,请检查并告诉我
          【解决方案7】:

          您无法使用第三方应用复制或删除外部存储上的文件和文件夹。像 [文件资源管理器]。

          KITKAT 版本更新后的数据政策。

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-12-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-04-01
            • 2016-11-15
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多