it-swarm.com.de

Holen Sie sich den richtigen Pfad von URI, Android KitKat new storage access framework

Vor dem Zugriff auf die neue Galerie in Android 4.4 (KitKat) habe ich mit dieser Methode meinen tatsächlichen Pfad auf der SD-Karte gefunden:

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 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 tatsächlichen Pfad auf der SD-Karte ermitteln?

196
Álvaro

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

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

So erhalten Sie die Dokument-ID:

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

Hinweis: Ich kann den Beitrag, von dem diese Lösung stammt, nicht finden. Ich wollte das Originalplakat bitten, hier einen Beitrag zu leisten. Werde heute Abend noch ein bisschen schauen.

117
Vikram

Dadurch wird der Dateipfad vom 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 .

495
Paul Burke

die Antwort unten wird von https://stackoverflow.com/users/3082682/cvizv auf einer Seite geschrieben, die nicht mehr existiert, da er nicht genug Repräsentanten hat, um eine Frage zu beantworten. Ich poste sie . Keine Credits 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;
}

Bearbeiten: Der Code enthält einen Fluss. Wenn das Gerät über mehr als einen externen Speicher verfügt (externe SD-Karte, externer USB usw.), funktioniert der Code über nicht primären Speichern nicht.

70
guness

Vor dem Zugriff auf die neue Galerie in KitKat habe ich mit dieser Methode meinen wirklichen Pfad in SD-Karte erhalten

Das war niemals zuverlässig. Es ist nicht erforderlich, dass die Uri, die Sie von einer ACTION_GET_CONTENT - oder ACTION_PICK -Anforderung erhalten, von der MediaStore indiziert wird oder sogar eine Datei auf der darstellt Dateisystem. Das Uri könnte beispielsweise einen Stream darstellen, in dem eine verschlüsselte Datei für Sie sofort entschlüsselt wird.

Wie könnte ich es schaffen, den tatsächlichen Pfad in SD-Karte zu erhalten?

Es ist nicht erforderlich, dass eine Datei vorhanden ist, die dem Uri entspricht.

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, verwenden Sie einfach den Stream direkt und vermeiden Sie die temporäre Datei.

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

Das wird Ihrer Situation nicht helfen. Es ist nicht erforderlich, dass eine ACTION_PICK -Antwort für ein Uri gilt, das eine Datei im Dateisystem enthält, die Sie auf magische Weise ableiten können.

27
CommonsWare

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

Und jetzt bekommst du content://com.Android.providers.media.documents/document/image:62 zurück, anstatt den vorherigen Medienanbieter-URI, richtig?

Auf Android 4.4 (KitKat) wird die neue DocumentsActivity geöffnet, wenn ein Intent.ACTION_GET_CONTENT ausgelöst wird. Dies führt zu einer Rasteransicht (oder Listenansicht), in der Sie ein Bild auswählen können. Daraufhin werden die folgenden URIs zurückgegeben Aufrufen des Kontexts (Beispiel): content://com.Android.providers.media.documents/document/image:62 (dies sind die URIs für den neuen Dokumentanbieter. Die zugrunde liegenden Daten werden abstrahiert, indem generische URIs für Dokumentanbieter für Clients bereitgestellt werden.).

Sie können jedoch sowohl auf die Galerie als auch auf andere Aktivitäten zugreifen, die auf Intent.ACTION_GET_CONTENT reagieren, indem Sie die Schublade in der DocumentsActivity verwenden (ziehen Sie von links nach rechts, um eine Schubladen-Benutzeroberfläche mit einer Galerie zur Auswahl anzuzeigen von). Genau wie vor KitKat.

Wenn Sie in der Klasse "DocumentsActivity" noch auswählen müssen, und die Datei-URI benötigen, sollten Sie in der Lage sein, die folgende Abfrage (mit contentresolver) durchzuführen (Warnung, das ist hacky!): 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). Verwenden Sie diesen Namen in einer Auswahl (bei Abfragen) an mediaprovider, um die dieser Auswahl entsprechende Zeile abzurufen. Hier können Sie auch die Datei-URI abrufen.

Die empfohlenen Möglichkeiten, auf den Dokumentanbieter zuzugreifen, finden Sie hier (holen Sie sich einen Inputstream oder einen Dateideskriptor, um die Datei/Bitmap zu lesen):

Beispiele für die Verwendung von documentprovider

9
Magnus

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

Bei mir hat es geklappt, wenn ich die Absicht auf PICK geändert habe. Dies wurde in AVD für Android 4.4 und in AVD für Android 2.1 getestet.

Berechtigung READ_EXTERNAL_STORAGE hinzufügen:

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

Ä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 : "";
 }
7

Versuche dies:

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

Verwenden Sie im onActivityResult Folgendes:

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

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

Um mit Versionen unterhalb von KitKat arbeiten zu können, erstellen Sie diese Klasse:

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<String> 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<String> 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());
    }
}
5
Danylo Volokh

Wir müssen die folgenden Änderungen/Korrekturen in unserem früheren Galerie-Auswahlcode für onActivityResult () vornehmen, um nahtlos auf Android 4.4 (KitKat) und allen anderen früheren Versionen zu funktionieren.

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
}
3
Sachin Gupta