web-dev-qa-db-fra.com

Android M écrire sur la carte SD - autorisation refusée

J'essaie de copier le fichier de mon application sur la carte SD, mais l'erreur eacces (autorisation refusée). Le système d'exploitation est Android M et j'ai autorisé les autorisations de stockage à l'exécution (vérifiées dans les informations d'application). J'ai également défini la permission d'utilisation dans AndroidManifest.xml

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

Ne fonctionne pas si je copie sur une carte 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)

Fonctionne si je copie dans la mémoire interne

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

Code pour copier le fichier de la source à la destination

/*
 * 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());
    }
}

Explorateur de fichiers ES

J'ai vu qu'ES File Explorer ne peut rien écrire sur la carte SD des appareils Redmi. Voici une vidéo avec la solution . Suivre les étapes a fonctionné pour ES Explorer sur mon appareil. Cela peut-il être fait par programme?

17
Sahil Khanna

Comme suggéré par @CommonsWare, nous devons utiliser le nouveau Storage Access Framework fourni par Android et obtenir l'autorisation de l'utilisateur pour écrire le fichier de la carte SD car vous avez dit qu'il est déjà écrit dans l'explorateur de fichiers File Manager Application ES.

Voici le code permettant à l'utilisateur de choisir la "carte SD":

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

qui ressemblera un peu à ceci:

 enter image description here

Et obtenez le chemin du document dans pickedDiret passez plus loin dans votre bloc copyFile Et utilisez ce chemin pour écrire le fichier:

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());
    }
}
16
shadygoneinsane

Vous devez ajouter le temps d’exécution de la demande d’autorisation dans Android 6.0 (API niveau 23) et supérieur, voici la documentation official

C'est le code pour WRITE_EXTERNAL_STORAGE

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

    return true;
}

Demander la permission autrement comme ceci

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

J'ai également eu ce problème, mais j'ai résolu par utiliser la demande l'autorisation en exécution et après avoir forcé l'autorisation. Après l'autorisation dans les informations de l'application du périphérique Android. après avoir déclaré l’autorisation dans le manifeste => aller au réglage de votre appareil => aller à l’application info => aller à l’autorisation => .__ et enfin autoriser l’autorisation. Rappelez-vous juste que je parle juste après Api niveau 22 signifie de Marshmallow.

1
Peter

Après Android 4.3 sur certains appareils, vous ne pouvez pas obtenir un accès direct en écriture à FileSystem sur une carte SD. 

Vous devez utiliser framework d'accès au stockage pour cela. 

1
Rostyslav Roshak

Je peux voir que vous copiez tout le contenu d'un fichier et essayez de l'écrire dans un autre fichier. Je pourrais suggérer une meilleure façon de faire ceci:

En supposant que vous ayez déjà vérifié l'existence du fichier 

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

Ce code a fonctionné pour mon application ... Faites-moi savoir si cela fonctionne pour vous ou pas ..

1
bharath

Il semble que les autorisations d'exécution soient correctement implémentées, mais le problème semble émaner de l'appareil Si vous utilisez Redmi, vous devez autoriser manuellement l'autorisation d'une application spécifique dans les paramètres de sécurité Redmi Ce lien montre comment activer l'autorisation dans la sécurité redmi

1
Burhanuddin Rashid

Vous ne pouvez pas copier ou supprimer des fichiers et des dossiers sur un stockage externe à l'aide d'une application tierce. comme [Explorateur de fichiers].

C'est la politique de données mise à jour après KitKat Version. 

Si seulement autoriser sur les applications système. Vous pouvez donc utiliser un explorateur de fichier original (venez de la ROM).

SI vous avez besoin d'utiliser une application tierce, alorsROOTvotre appareil. (L'autorisation racine est requise)

0
Jigar Patel

À partir de l'API 23 et des versions ultérieures, vous devez demander explicitement des autorisations. Comme vous l'avez mentionné, vous demandez une autorisation d'exécution. Je vais vous suggérer de faire comme ça:

Dans votre fragment ou activité 

 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...);

Puis remplacez cette méthode dans l'activité/le 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");
            }