Erhalten Sie einen echten Pfad von URI, Android KitKat neues Speicherzugriffs-Framework

Vor dem neuen Galeriezugriff in Android 4.4 (KitKat) habe ich mit dieser Methode meinen echten Pfad auf der SD-Karte bekommen:

public String getPath(Uri uri) { String[] projection = { MediaStore.Images.Media.DATA }; Cursor cursor = managedQuery(uri, projection, null, null, null); startManagingCursor(cursor); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); return cursor.getString(column_index); } 

Jetzt gibt der Intent.ACTION_GET_CONTENT verschiedene Daten zurück:

Vor:

 content://media/external/images/media/62 

Jetzt:

 content://com.android.providers.media.documents/document/image:62 

Wie kann ich den richtigen Pfad auf der SD-Karte erhalten?

Hinweis: Diese Antwort behandelt einen Teil des Problems. Eine vollständige Lösung (in Form einer Bibliothek) finden Sie in Paul Burkes Antwort .

Sie können den URI verwenden, um die document id MediaStore.Images.Media.EXTERNAL_CONTENT_URI , und dann entweder MediaStore.Images.Media.EXTERNAL_CONTENT_URI oder MediaStore.Images.Media.INTERNAL_CONTENT_URI (abhängig von der SD- MediaStore.Images.Media.EXTERNAL_CONTENT_URI MediaStore.Images.Media.INTERNAL_CONTENT_URI .

Um die Dokument-ID zu erhalten:

 // Will return "image:x*" String wholeID = DocumentsContract.getDocumentId(uriThatYouCurrentlyHave); // Split at colon, use second item in the array String id = wholeID.split(":")[1]; String[] column = { MediaStore.Images.Media.DATA }; // where id is equal to String sel = MediaStore.Images.Media._ID + "=?"; Cursor cursor = getContentResolver(). query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column, sel, new String[]{ id }, null); String filePath = ""; int columnIndex = cursor.getColumnIndex(column[0]); if (cursor.moveToFirst()) { filePath = cursor.getString(columnIndex); } cursor.close(); 

Referenz: Ich kann den Beitrag, aus dem diese Lösung stammt, nicht finden. Ich wollte das Originalplakat bitten, hier etwas beizutragen. Werde heute Abend mehr sehen.

Dadurch wird der Dateipfad von MediaProvider, DownloadsProvider und ExternalStorageProvider abgerufen, während auf die von Ihnen erwähnte inoffizielle ContentProvider-Methode zurückgegriffen wird.

 /** * Get a file path from a Uri. This will get the the path for Storage Access * Framework Documents, as well as the _data field for the MediaStore and * other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @author paulburke */ public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // DocumentProvider if (isKitKat && 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]; } // TODO handle non-primary volumes } // 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 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 column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_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()); } 

Diese stammen aus meiner Open-Source-Bibliothek aFileChooser .

Die folgende Antwort wird von https://Stackoverflow.com/users/3082682/cvizv auf einer Seite geschrieben, die nicht mehr existiert, da er nicht genug Rep hat, um eine Frage zu beantworten, ich poste sie. Keine Gutschriften von mir.

 public String getImagePath(Uri uri){ Cursor cursor = getContentResolver().query(uri, null, null, null, null); cursor.moveToFirst(); String document_id = cursor.getString(0); document_id = document_id.substring(document_id.lastIndexOf(":")+1); cursor.close(); cursor = getContentResolver().query( android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Images.Media._ID + " = ? ", new String[]{document_id}, null); cursor.moveToFirst(); String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); cursor.close(); return path; } 

Edit: Es gibt einen Fluss auf den Code; Wenn das Gerät über mehr als einen externen Speicher verfügt (externe SD-Karte, externer USB usw.), funktionieren oberhalb des Codes keine nicht-primären Speicher.

Vor dem neuen Galeriezugriff in KitKat habe ich mit dieser Methode meinen echten Pfad in SD-Karte bekommen

Das war nie zuverlässig. Es ist nicht erforderlich, dass der ACTION_GET_CONTENT , den Sie von einer ACTION_GET_CONTENT oder ACTION_PICK Anforderung zurückgegeben haben, vom MediaStore indiziert werden MediaStore oder sogar eine Datei im Dateisystem darstellen muss. Der Uri könnte beispielsweise einen Stream darstellen, in dem eine verschlüsselte Datei für Sie entschlüsselt wird.

Wie kann ich den richtigen Pfad in SD-Karte erhalten?

Es gibt keine Anforderung, dass es eine Datei gibt, die dem Uri .

Ja, ich brauche wirklich einen Weg

Kopieren Sie dann die Datei aus dem Stream in Ihre eigene temporäre Datei und verwenden Sie sie. Besser noch, benutze den Stream direkt und vermeide die temporäre Datei.

Ich habe meine Intent.ACTION_GET_CONTENT für Intent.ACTION_PICK geändert

Das wird deiner Situation nicht helfen. Es gibt keine Anforderung, dass eine ACTION_PICK Antwort für einen Uri , der eine Datei im Dateisystem hat, die Sie irgendwie magisch ableiten können.

Diese Antwort basiert auf Ihrer etwas vagen Beschreibung. Ich Intent.ACTION_GET_CONTENT davon aus, dass Sie eine Absicht mit Aktion Intent.ACTION_GET_CONTENT : Intent.ACTION_GET_CONTENT

Und jetzt bekommst du content://com.android.providers.media.documents/document/image:62 statt der bisherigen Medienanbieter-URI zurück, richtig?

Auf Android 4.4 (KitKat) wird die neue DocumentsActivity geöffnet, wenn ein Intent.ACTION_GET_CONTENT wird, was zu einer Grid-Ansicht (oder Intent.ACTION_GET_CONTENT ) führt, in der Sie ein Bild auswählen können. Dadurch werden die folgenden URIs in den aufrufenden Kontext zurückgegeben (Beispiel): content://com.android.providers.media.documents/document/image:62 (dies sind die URIs des neuen Dokumentanbieters, abstrahiert er die zugrunde liegenden Daten, indem er den Clients generische Dokumentanbieter-URIs bereitstellt).

Sie können jedoch auf Galerie und andere Aktivitäten, die auf Intent.ACTION_GET_CONTENT reagieren, Intent.ACTION_GET_CONTENT indem Sie das Fach in der DocumentsActivity verwenden (ziehen Sie von links nach rechts und Sie sehen eine Benutzeroberfläche mit Galerie, aus der Sie auswählen können). Genau wie vor KitKat.

Wenn Sie immer noch die DocumentsActivity-class auswählen und den Datei-URI benötigen, sollten Sie die folgende Abfrage durchführen können (Achtung dies ist Hacky!) (Mit contentresolver): content://com.android.providers.media.documents/document/image:62 URI und lesen Sie den Wert _display_name vom Cursor. Dies ist ein etwas eindeutiger Name (nur der Dateiname in lokalen Dateien) und verwenden Sie das in einer Auswahl (bei der Abfrage) an mediaprovider, um die richtige Zeile zu erhalten, die dieser Auswahl entspricht. Von hier aus können Sie auch den Datei-URI abrufen.

Die empfohlenen Wege zum Zugriff auf den Dokumentanbieter finden Sie hier (erhalten Sie einen Inputstream oder einen Dateideskriptor, um die Datei / Bitmap zu lesen):

Beispiele für die Verwendung von Documentprovider

Ich hatte genau das gleiche Problem. Ich brauche den Dateinamen, um ihn auf eine Website hochladen zu können.

Es funktionierte für mich, wenn ich die Absicht zu PICK änderte. Dies wurde in AVD für Android 4.4 und in AVD für Android 2.1 getestet.

Fügen Sie die Berechtigung READ_EXTERNAL_STORAGE hinzu:

  

Ändern Sie die Absicht:

 Intent i = new Intent( Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI ); startActivityForResult(i, 66453666); /* OLD CODE Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult( Intent.createChooser( intent, "Select Image" ), 66453666 ); */ 

Ich musste meinen Code nicht ändern, um den tatsächlichen Pfad zu erhalten:

 // Convert the image URI to the direct file system path of the image file public String mf_szGetRealPathFromURI(final Context context, final Uri ac_Uri ) { String result = ""; boolean isok = false; Cursor cursor = null; try { String[] proj = { MediaStore.Images.Media.DATA }; cursor = context.getContentResolver().query(ac_Uri, proj, null, null, null); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); result = cursor.getString(column_index); isok = true; } finally { if (cursor != null) { cursor.close(); } } return isok ? result : ""; } 

Versuche dies:

 //KITKAT i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(i, CHOOSE_IMAGE_REQUEST); 

Verwenden Sie Folgendes im OnActivityResult:

 Uri selectedImageURI = data.getData(); input = c.getContentResolver().openInputStream(selectedImageURI); BitmapFactory.decodeStream(input , null, opts); 

Hier ist eine aktualisierte Version von Paul Burkes Antwort . In Versionen unter Android 4.4 (KitKat) haben wir nicht die DocumentsContract- class.

Um mit Versionen unter KitKat zu arbeiten, erstellen Sie diese class:

 public class DocumentsContract { private static final String DOCUMENT_URIS = "com.android.providers.media.documents " + "com.android.externalstorage.documents " + "com.android.providers.downloads.documents " + "com.android.providers.media.documents"; private static final String PATH_DOCUMENT = "document"; private static final String TAG = DocumentsContract.class.getSimpleName(); public static String getDocumentId(Uri documentUri) { final List paths = documentUri.getPathSegments(); if (paths.size() < 2) { throw new IllegalArgumentException("Not a document: " + documentUri); } if (!PATH_DOCUMENT.equals(paths.get(0))) { throw new IllegalArgumentException("Not a document: " + documentUri); } return paths.get(1); } public static boolean isDocumentUri(Uri uri) { final List paths = uri.getPathSegments(); Logger.v(TAG, "paths[" + paths + "]"); if (paths.size() < 2) { return false; } if (!PATH_DOCUMENT.equals(paths.get(0))) { return false; } return DOCUMENT_URIS.contains(uri.getAuthority()); } } 

Wir müssen die folgenden Änderungen / Korrekturen in unserem früheren onActivityResult () Galeriewähler-Code vornehmen, um nahtlos unter Android 4.4 (KitKat) und allen anderen früheren Versionen zu laufen.

 Uri selectedImgFileUri = data.getData(); if (selectedImgFileUri == null ) { // The user has not selected any photo } try { InputStream input = mActivity.getContentResolver().openInputStream(selectedImgFileUri); mSelectedPhotoBmp = BitmapFactory.decodeStream(input); } catch (Throwable tr) { // Show message to try again }