【问题标题】:android - sd card writing permission denied (EACCES)android - sd 卡写入权限被拒绝(EACCES)
【发布时间】:2017-12-21 16:14:09
【问题描述】:

我正在尝试写入 SD 卡,而不是写入设备本身上安装的“外部”存储(调用 getExternalStorageDirectory 时返回的存储)。我可以读取 SD 卡中的每个文件,但无法在其 PUBLIC 目录中更改或写入任何内容,尝试执行此操作时检索到此错误:“java.io.IOException: open failed: EACCES (Permission denied)”。

我在这里看到了很多类似问题的答案,但大多数只谈论“外部存储”(主要),而这不是 SD 卡(在当今的大多数设备上)。我知道我可以在 SD 卡中写入我的应用程序的私有目录,但这不是我想要的,我想更改公共文件。最后,有人告诉我,我无法在 Android 6.0 Marshmallow 中写入 sd 卡的公共目录,但我不相信 Android 会以这种方式损害开发人员。

注意:我在运行时请求权限,但即使在运行时授权、直接在应用程序管理器中授权、设备与 USB 连接或断开连接后,错误仍然存​​在。

我使用的代码如下:

import android.Manifest;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import static android.widget.Toast.LENGTH_LONG;

public class MainActivity extends AppCompatActivity {

    private static String TAG = "SdCardTest";
    private static final int REQUEST_WRITE_STORAGE = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        int permission = ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            Log.i(TAG, "Permission to record denied");

            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage("Permission is required to access SD card.").setTitle("Permission required");

                builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface dialog, int id) {
                        Log.i(TAG, "Clicked");
                        makeRequest();
                    }
                });

                AlertDialog dialog = builder.create();
                dialog.show();

            } else {
                makeRequest();
            }
        }
    }

    protected void makeRequest() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_WRITE_STORAGE: {

                if (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {

                    Log.i(TAG, "Permission has been denied by user");
                    Toast.makeText(this, "Permission has been denied by user", LENGTH_LONG).show();

                } else {

                    Log.i(TAG, "Permission has been granted by user");
                    Toast.makeText(this, "Permission has been granted by user", LENGTH_LONG).show();

                    //Here I WRITE to SD card

                    } catch (IOException e) {
                        e.printStackTrace();
                        Toast.makeText(this, "Not working", LENGTH_LONG).show();
                    }

                }
                return;
            }
        }
    }
}

我的清单文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.patrick.testasdcompermissaoemexecucao">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

【问题讨论】:

  • 您可以使用 Storage Access Framework 中的 DocumentFile 在 SD 卡上进行写入。谷歌为它。使用 Intent.ACTION_OPEN_DOCUMENT 让用户选择文件。
  • @VladMatvienko 我尝试了你的建议,但没有奏效。在这一行“DocumentFilepickDir = DocumentFile.fromTreeUri(this, treeUri);”在存储中选择 TXT 文件后,出现“java.lang.IllegalArgumentException: Invalid URI:”错误。您在上一个问题中回答的代码工作正常吗?
  • 是的,对我来说效果很好
  • 无论如何,您至少可以将该答案用作解决方案的基础。

标签: android sd-card permission-denied


【解决方案1】:

感谢@VladMatvienko,我能够编写一个代码,让我可以读取、写入和编辑我的 SD 卡上的任何目录或子目录。

这是我的完整代码,您可以将其复制并粘贴到 MainActivity 中以测试存储访问框架:

import android.app.Activity;
import android.content.Intent;
import android.content.UriPermission;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.support.v4.provider.DocumentFile;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    int SAVE_REQUEST_CODE = 102;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }


    public void saveFile(View view) {
        List<UriPermission> permissions = getContentResolver().getPersistedUriPermissions();
        if (permissions != null && permissions.size() > 0) {

            //pickedDir is the directory the user selected (I chose SD's root).
            DocumentFile pickedDir = DocumentFile.fromTreeUri(this, permissions.get(0).getUri());



//=====You can use these lines to write into an existent folder or directory========================
            DocumentFile aux = pickedDir.findFile("aaabrao");//aaabrao is the name of a existing folder in my SD.

    //Use DocumentFile file = pickedDir.createDirectory("aaabrao"); to create a new folder in the pickedDir directory.

            if(aux != null) {
                //Creating the object "file" is essencial for you to write "some text" INSIDE the TXT file. Otherwise, your TXT will be a blank.
                DocumentFile file = aux.createFile("text/plain", "try5.txt");
                writeFileContent(file.getUri());
            }
//==================================================================================================



//=====You can use these lines to write to the primary chosen directory (SD's root, in my case)====
            DocumentFile file2 = pickedDir.createFile("text/plain", "try5.txt");
            writeFileContent(file2.getUri());
//==================================================================================================

        } else {

            startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), SAVE_REQUEST_CODE);
        }
    }

    public void onActivityResult(int requestCode, int resultCode, Intent resultData) {

        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == SAVE_REQUEST_CODE) {
                if (resultData != null) {
                    Uri treeUri=resultData.getData();

                    //This line gets your persistent permission to write SD (or anywhere else) without prompting a request everytime.
                    getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

                    //These lines will write to the choosen directory (SD's root was my chosen one). You could also write to an existent folder inside SD's root like me in saveFile().
                    DocumentFile pickedDir = DocumentFile.fromTreeUri(getBaseContext(), treeUri);
                    DocumentFile file = pickedDir.createFile("text/plain", "try5.txt");
                    writeFileContent(file.getUri());

                }
            }
        }
    }


    private void writeFileContent(Uri uri) {
        try {
            ParcelFileDescriptor pfd = this.getContentResolver().openFileDescriptor(uri, "w");

            FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());

            String textContent = "some text";

            fileOutputStream.write(textContent.getBytes());

            fileOutputStream.close();
            pfd.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

我使这个 activity_main 使用按钮调用 saveFile():

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.patrick.testasdcompermissaoemexecucao.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button2"
        android:text="Write"
        android:onClick="saveFile"
        android:layout_below="@+id/imageButton"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        tools:ignore="UnknownId"
        android:layout_gravity="center" />
</RelativeLayout>

你必须改变

tools:context="com.example.patrick.testasdcompermissaoemexecucao.MainActivity">

为您的应用程序的目录。如果您不知道怎么做,请先创建一个空白项目并复制相应的行,然后再将我的代码粘贴到那里。

清单文件无需更改。

祝你好运!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多