web-dev-qa-db-fra.com

Implémenter un sélecteur de fichiers dans Android et copier le fichier sélectionné vers un autre emplacement

J'essaie d'implémenter un sélecteur de fichiers dans mon projet Android. Ce que j'ai pu faire jusqu'à présent, c'est:

Intent chooseFile;
Intent intent;
chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
intent = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(intent, PICKFILE_RESULT_CODE);

Et puis dans ma onActivityResult()

switch(requestCode){
 case PICKFILE_RESULT_CODE:
   if(resultCode==-1){
      Uri uri = data.getData();
      String filePath = uri.getPath();
      Toast.makeText(getActivity(), filePath,
                        Toast.LENGTH_LONG).show();
    }
 break;
}

Cela ouvre un sélecteur de fichiers, mais ce n'est pas ce que je veux. Par exemple, je souhaite sélectionner un fichier (.txt), puis obtenir ce File, puis l'utiliser. Avec ce code, j'ai pensé que j'obtiendrais le chemin complet mais cela ne se produit pas; par exemple, j'obtiens: /document/5318/. Mais avec ce chemin, je ne peux pas obtenir le fichier. J'ai créé une méthode appelée PathToFile() qui renvoie un File:

 private File PathToFile(String path) {
    File tempFileToUpload;
    tempFileToUpload = new File(path);
    return tempFileToUpload;
}

Ce que j'essaie de faire est de laisser l'utilisateur choisir un File de n'importe où signifie DropBox, Drive, SDCard, Mega, etc ... Et je ne trouve pas le moyen de le faire correctement, j'ai essayé d'obtenir le Path puis un File par ce Path... mais ça ne fonctionne pas, donc je pense qu'il vaut mieux obtenir le File lui-même, puis avec ce File par programme je Copy ceci ou Delete.

EDIT (code actuel)

Mon Intent

 Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
 chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
 chooseFile.setType("text/plain");
 startActivityForResult(
      Intent.createChooser(chooseFile, "Choose a file"),
      PICKFILE_RESULT_CODE
 );

Là, j'ai une question parce que je ne sais pas ce qui est supporté comme text/plain, Mais je vais enquêter à ce sujet, mais cela n'a pas d'importance pour le moment.

Sur ma onActivityResult() j'ai utilisé la même chose que @ réponse Lukas Knuth , mais je ne sais pas si avec elle je peux Copy cette File à une autre partie de mon SDcard j'attends sa réponse.

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
        Uri content_describer = data.getData();
        //get the path 
        Log.d("Path???", content_describer.getPath());
        BufferedReader reader = null;
        try {
            // open the user-picked file for reading:
            InputStream in = getActivity().getContentResolver().openInputStream(content_describer);
            // now read the content:
            reader = new BufferedReader(new InputStreamReader(in));
            String line;
            StringBuilder builder = new StringBuilder();

            while ((line = reader.readLine()) != null){
                builder.append(line);
            }
            // Do something with the content in
            text.setText(builder.toString());



        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

getPath() de @ Y.S.

Je fais ça :

    String[] projection = { MediaStore.Files.FileColumns.DATA };
            Cursor cursor = getActivity().getContentResolver().query(content_describer, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
cursor.moveToFirst();
cursor.close();
Log.d( "PATH-->",cursor.getString(column_index));

Obtient un NullPointerException:

Java.lang.RuntimeException: échec de la livraison du résultat ResultInfo {who = null, request = 131073, result = -1, data = Intent {dat = file: /// path typ = text/plain flg = 0x3}} to activity {info .androidhive.tabsswipe/info.androidhive.tabsswipe.MainActivity2}: Java.lang.NullPointerException

EDIT avec le code qui fonctionne grâce à @ Y.S. , @ Lukas Knuth , et @ CommonsWare .

C'est le Intent où j'accepte uniquement les fichiers text/plain.

Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("text/plain");
startActivityForResult(
    Intent.createChooser(chooseFile, "Choose a file"),
    PICKFILE_RESULT_CODE
);

Sur ma onActivityResult() je crée un URI où j'obtiens les données du Intent, je crée un File où je sauvegarde le chemin absolu faisant content_describer.getPath();, puis je garde le nom du chemin pour l'utiliser dans un TextView avec content_describer.getLastPathSegment(); (c'était génial @YS ne connaissait pas cette fonction), et je crée un second File que j'ai appelé destination et j'envoie le AbsolutePath pour pouvoir créer ce File.

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
        Uri content_describer = data.getData();
        String src = content_describer.getPath();
        source = new File(src);
        Log.d("src is ", source.toString());
        String filename = content_describer.getLastPathSegment();
        text.setText(filename);
        Log.d("FileName is ",filename);
        destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Test/TestTest/" + filename);
        Log.d("Destination is ", destination.toString());
        SetToFolder.setEnabled(true);
    }
}

J'ai également créé une fonction que vous devez envoyer le source file, Et destination file Que nous avons créé précédemment pour le copier dans le nouveau dossier.

private void copy(File source, File destination) throws IOException {

    FileChannel in = new FileInputStream(source).getChannel();
    FileChannel out = new FileOutputStream(destination).getChannel();

    try {
        in.transferTo(0, in.size(), out);
    } catch(Exception e){
        Log.d("Exception", e.toString());
    } finally {
        if (in != null)
            in.close();
        if (out != null)
            out.close();
    }
}

J'ai aussi créé une fonction qui me dit si ce dossier existe ou non (je dois envoyer le destination file, S'il n'existe pas je crée ce dossier et si ce n'est pas je ne fais rien .

private void DirectoryExist (File destination) {

    if(!destination.isDirectory()) {
        if(destination.mkdirs()){
            Log.d("Carpeta creada","....");
        }else{
            Log.d("Carpeta no creada","....");
        }
    }

Merci encore pour votre aide, j'espère que vous apprécierez ce code créé avec tout le monde :)

16
Skizo-ozᴉʞS

ÉTAPE 1 - Utilisez un _ implicite Intent:

Pour choisir un fichier sur le périphérique, vous devez utiliser un Intent implicite _

Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
chooseFile = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(chooseFile, PICKFILE_RESULT_CODE);

ÉTAPE 2 - Obtenez le chemin absolu du fichier:

Pour obtenir le chemin du fichier à partir d'un Uri, essayez d'abord d'utiliser

Uri uri = data.getData();
String src = uri.getPath();

data est le Intent renvoyé dans onActivityResult().

Si cela ne fonctionne pas, utilisez la méthode suivante:

public String getPath(Uri uri) {

    String path = null;
    String[] projection = { MediaStore.Files.FileColumns.DATA };
    Cursor cursor = getContentResolver().query(uri, projection, null, null, null);

    if(cursor == null){
        path = uri.getPath()
    }
    else{
        cursor.moveToFirst();
        int column_index = cursor.getColumnIndexOrThrow(projection[0]);
        path = cursor.getString(column_index);
        cursor.close();
    }

    return ((path == null || path.isEmpty()) ? (uri.getPath()) : path);
}

Au moins une de ces deux méthodes devrait vous fournir le chemin d'accès correct et complet.

ÉTAPE 3 - Copiez le fichier:

Ce que vous voulez, je crois, c'est copier un fichier d'un endroit à un autre.

Pour ce faire, il est nécessaire d'avoir le chemin de fichier absolu des emplacements source et destination.

Tout d'abord, obtenez le chemin absolu du fichier à l'aide de ma méthode getPath() ou uri.getPath():

String src = getPath(uri);    /* Method defined above. */

ou

Uri uri = data.getData();
String src = uri.getPath();

Ensuite, créez deux objets File comme suit:

File source = new File(src);
String filename = uri.getLastPathSegment();
File destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/CustomFolder/" + filename);

CustomFolder est le répertoire sur votre lecteur externe où vous souhaitez copier le fichier.

Utilisez ensuite la méthode suivante pour copier un fichier d'un endroit à un autre:

private void copy(File source, File destination) {

   FileChannel in = new FileInputStream(source).getChannel();
   FileChannel out = new FileOutputStream(destination).getChannel();

   try {
      in.transferTo(0, in.size(), out);
   } catch(Exception){
      // post to log
   } finally {
      if (in != null)
         in.close();
      if (out != null)
         out.close();
   }
}

Essaye ça. Cela devrait fonctionner.

Remarque: Vis-à-vis de la réponse de Lukas - ce qu'il a fait, c'est utiliser une méthode appelée openInputStream() qui renvoie le content d'un Uri, que ce Uri représente un fichier ou une URL.

Une autre approche prometteuse - le FileProvider:

Il existe un autre moyen par lequel il est possible d'obtenir un fichier à partir d'une autre application. Si une application partage ses fichiers via FileProvider , il est possible d'obtenir un objet FileDescriptor contenant des informations spécifiques sur ce fichier.

Pour ce faire, utilisez le Intent suivant:

Intent mRequestFileIntent = new Intent(Intent.ACTION_GET_CONTENT);
mRequestFileIntent.setType("*/*");
startActivityForResult(mRequestFileIntent, 0);

et dans votre onActivityResult():

@Override
public void onActivityResult(int requestCode, int resultCode,
        Intent returnIntent) {
    // If the selection didn't work
    if (resultCode != RESULT_OK) {
        // Exit without doing anything else
        return;
    } else {
        // Get the file's content URI from the incoming Intent
        Uri returnUri = returnIntent.getData();
        /*
         * Try to open the file for "read" access using the
         * returned URI. If the file isn't found, write to the
         * error log and return.
         */
        try {
            /*
             * Get the content resolver instance for this context, and use it
             * to get a ParcelFileDescriptor for the file.
             */
            mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.e("MainActivity", "File not found.");
            return;
        }
        // Get a regular file descriptor for the file
        FileDescriptor fd = mInputPFD.getFileDescriptor();
        ...
    }
}

mInputPFD est un ParcelFileDescriptor.

Références:

1. Intentions communes - Stockage de fichiers .

2. FileChannel =.

3. FileProvider =.

4. Demande d'un fichier partagé .

35
Y.S

J'ai fait de même pour laisser l'utilisateur choisir une image dans un dossier:

1) il y a un bouton OUVERT:

@Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.btn_open:
        myOpenImagePicker();
        break;
    }
}

2) la fonction de dossier d'image ouverte:

@SuppressLint("InlinedApi")
public void myOpenImagePicker() {

    if (Build.VERSION.SDK_INT < 19) {
        Intent intent = new Intent();
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        startActivityForResult(
                Intent.createChooser(intent, "Select Picture"),
                SELECT_FOLDER);

    } else {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        startActivityForResult(intent, SELECT_FOLDER);
    }
}

) le résultat de l'activité où j'obtiens le chemin du fichier image et fais ce que je veux avec le chemin de l'image:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
    case SELECT_FOLDER:
        if (resultCode == RESULT_OK && data != null) {

            Uri originalUri = data.getData();
            String id01 = W_ImgFilePathUtil.getPath(
                    getApplicationContext(), originalUri);
            Bitmap unscaledBitmap = W_ImgScalingUtil.decodeResource(id01,
                    xdrawing.getViewWidth(), xdrawing.getViewHeight(),
                    ScalingLogic.FIT);
            if (unscaledBitmap == null) {
                zprefsutil.ShowToast("IMAGE ERROR", 1);
            } else {
                setExternalScaledBitmap(W_ImgScalingUtil
                        .createScaledBitmap(unscaledBitmap,
                                xdrawing.getViewWidth(),
                                xdrawing.getViewHeight(), ScalingLogic.FIT));
                unscaledBitmap.recycle();
                xdrawing.invalidate();
            }

        }
        break;
    default:
        break;
    }
}

4) et maintenant la PARTIE LA PLUS IMPORTANTE, la classe W_ImgFilePathUtil, le code ne vient pas de moi mais il vous permet de récupérer le chemin complet de tout fichier sélectionné que ce soit sur carte sd, google drive, ... :

public class W_ImgFilePathUtil {

    /**
     * Method for return file path of Gallery image
     * 
     * @param context
     * @param uri
     * @return path of the selected image file from gallery
     */
    @SuppressLint("NewApi")
    public static String getPath(final Context context, final Uri uri) {

        // check here to KitKat or new version
        final boolean isKitKatorUp = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat;

        // DocumentProvider
        if (isKitKatorUp && DocumentsContract.isDocumentUri(context, uri)) {

            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/"
                            + split[1];
                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {

                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"),
                        Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] { split[1] };

                return getDataColumn(context, contentUri, selection,
                        selectionArgs);
            }
        }

        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {

            // Return the remote address
            if (isGooglePhotosUri(uri))
                return uri.getLastPathSegment();

            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     * 
     * @param context
     *            The context.
     * @param uri
     *            The Uri to query.
     * @param selection
     *            (Optional) Filter used in the query.
     * @param selectionArgs
     *            (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri,
            String selection, String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = { column };

        try {
            cursor = context.getContentResolver().query(uri, projection,
                    selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }

    /**
     * @param uri
     *            The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.Android.externalstorage.documents".equals(uri
                .getAuthority());
    }

    /**
     * @param uri
     *            The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.Android.providers.downloads.documents".equals(uri
                .getAuthority());
    }

    /**
     * @param uri
     *            The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.Android.providers.media.documents".equals(uri
                .getAuthority());
    }

    /**
     * @param uri
     *            The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    public static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.Android.apps.photos.content".equals(uri
                .getAuthority());
    }
}

CONCLUSION: le code fonctionne avec le chemin de l'image mais fonctionne bien avec tout type de fichier.

J'espère que cela vous aidera à résoudre votre problème.

PAIX.

5
mourad

Comme @ CommonsWare déjà noté, Android vous renvoie un Uri, qui est un concept plus abstrait qu'un chemin de fichier.

Il peut également décrire un chemin de fichier simple, mais il peut également décrire une ressource accessible via une application (comme content://media/external/audio/media/710).

Si vous souhaitez que votre utilisateur choisisse un fichier du téléphone pour le lire dans votre application, vous pouvez le faire en demandant le fichier (comme vous l'avez fait correctement), puis utilisez le ContentResolver pour obtenir un InputStream pour le Uri renvoyé par le sélecteur.

Voici un exemple:

Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
// Ask specifically for something that can be opened:
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("*/*");
startActivityForResult(
        Intent.createChooser(chooseFile, "Choose a file"),
        PICKFILE_REQUEST_CODE
);

// And then somewhere, in your activity:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICKFILE_REQUEST_CODE && resultCode == RESULT_OK){
        Uri content_describer = data.getData();
        BufferedReader reader = null;
        try {
            // open the user-picked file for reading:
            InputStream in = getContentResolver().openInputStream(content_describer);
            // now read the content:
            reader = new BufferedReader(new InputStreamReader(in));
            String line;
            StringBuilder builder = new StringBuilder();
            while ((line = reader.readLine()) != null){
                builder.append(line);
            }
            // Do something with the content in
            some_view.setText(builder.toString());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Important: Certains fournisseurs (comme Dropbox) stockent/mettent en cache leurs données sur le stockage externe. Vous devrez avoir le Android.permission.READ_EXTERNAL_STORAGE- autorisation déclarée dans votre manifeste, sinon vous obtiendrez FileNotFoundException, même si le fichier est là.


pdate: Oui, vous pouvez copier le fichier en le lisant d'un flux et en l'écrivant sur un autre:

// Error handling is omitted for shorter code!
Uri content_describer = data.getData();
InputStream in = null;
OutputStream out = null;
try {
    // open the user-picked file for reading:
    in = getContentResolver().openInputStream(content_describer);
    // open the output-file:
    out = new FileOutputStream(new File("some/path/to/a/writable/file"));
    // copy the content:
    byte[] buffer = new byte[1024];
    int len;
    while ((len = in.read(buffer)) != -1) {
        out.write(buffer, 0, len);
    }
    // Contents are copied!
} finally {
    if (in != null) {
        in.close();
    }
    if (out != null){
        out.close();
    }
}

La suppression du fichier n'est probablement pas possible, car le fichier ne vous appartient pas , il appartient à l'application qui l'a partagé avec le vôtre. À cet effet, l'application propriétaire est responsable de la suppression du fichier.

4
Lukas Knuth

Passez l'URI retourné dans onActivityResult dans cette méthode

private String getPath(Uri contentURI) {

    String result;
    Cursor cursor = getActivity().getContentResolver().query(contentURI,
            null, null, null, null);

    if (cursor == null) {
        result = contentURI.getPath();
    } else {
        cursor.moveToFirst();
        int idx = cursor
                .getColumnIndex(MediaStore.Images.ImageColumns.DATA);
        result = cursor.getString(idx);
        cursor.close();
    }
    return result;
}
2

n Uri n'est pas un fichier . Un Uri est plus proche d'une URL de serveur Web. C'est une adresse opaque, qui n'a de sens que pour le "serveur" (ou dans ce cas, le ContentProvider).

Tout comme vous utilisez un InputStream pour lire les octets représentés par une URL Web, vous utilisez un InputStream pour lire les octets représentés par le Uri. Vous obtenez un tel flux en appelant openInputStream() sur un ContentResolver.

1
CommonsWare