Vor KitKat (oder vor der neuen Galerie) wurde Intent.ACTION_GET_CONTENT
eine URI wie diese zurückgegeben
Inhalt: // media / external / images / media / 3951.
Mit dem ContentResolver
und Abfragen für wird
MediaStore.Images.Media.DATA
die Datei-URL zurückgegeben.
In KitKat gibt die Galerie jedoch eine URI (über "Last") wie folgt zurück:
Inhalt: //com.android.providers.media.documents/document/image: 3951
Wie gehe ich damit um?
android
android-intent
android-gallery
android-contentresolver
Michael Greifeneder
quelle
quelle
Uri
als Stream über geöffnet werden könnenContentResolver
. Ich war lange nervös wegen Apps, die davon ausgehen,content://
Uri
dass eine Datei, die eine Datei darstellt, immer in eine konvertiert werden kannFile
.InputStream
auf dem Feld erhalten wurden,ContentResolver
an einen zuvor festgelegten Ort zu kopieren, damit sie einen bekannten Dateinamen haben. Das klingt für mich jedoch verschwenderisch. Irgendwelche anderen Vorschläge?InputStream
Over-JNI zu arbeiten? Leider gibt es nicht allzu viele Optionen für Sie.InputStream
anstelle einer Datei verwenden können (was großartig ist). Nur das Lesen von EXIF-Tags ist etwas schwierig und erfordert die Bibliothek von Drew Noakes . Vielen Dank für Ihre Kommentare.Antworten:
Versuche dies:
Wahrscheinlich brauchen
zum
quelle
Dies erfordert keine besonderen Berechtigungen und funktioniert mit dem Storage Access Framework sowie dem inoffiziellen
ContentProvider
Muster (Dateipfad im_data
Feld).Eine aktuelle Version dieser Methode finden Sie hier .
quelle
Authority: com.google.android.apps.docs.storage
und zurückSegments: [document, acc=1;doc=667]
. Ich bin nicht sicher, gehe aber davon aus, dass derdoc
Wert dieUri
ID ist, gegen die Sie abfragen können. Sie müssen wahrscheinlich Berechtigungen einrichten, wie unter "Autorisieren Ihrer App auf Android" hier beschrieben: developer.google.com/drive/integrate-android-ui . Bitte aktualisieren Sie hier, wenn Sie es herausfinden._data
würde nicht funktionieren, wenn ContentProvider es nicht unterstützt. Es wird empfohlen, die Anweisungen von @CommonsWare zu befolgen und nicht mehr den vollständigen Dateipfad zu verwenden, da es sich möglicherweise um eine Datei in der Dropbox-Cloud anstelle einer echten Datei handelt.Hatte das gleiche Problem, versuchte die obige Lösung, aber obwohl es im Allgemeinen funktionierte, erhielt ich aus irgendeinem Grund die Verweigerung der
android.permission.MANAGE_DOCUMENTS
Berechtigung für einige Bilder beim Uri-Inhaltsanbieter, obwohl ich die Berechtigung ordnungsgemäß hinzugefügt hatte.Auf jeden Fall wurde eine andere Lösung gefunden, die das Öffnen der Bildergalerie anstelle der Ansicht von KITKAT-Dokumenten erzwingt:
und laden Sie dann das Bild:
BEARBEITEN
ACTION_OPEN_DOCUMENT
Möglicherweise müssen Sie Berechtigungsflags usw. beibehalten. Dies führt im Allgemeinen häufig zu Sicherheitsausnahmen ...Eine andere Lösung besteht darin, die
ACTION_GET_CONTENT
Kombination zu verwenden, mitc.getContentResolver().openInputStream(selectedImageURI)
der sowohl bei Pre-KK als auch bei KK gearbeitet werden kann. Kitkat verwendet dann die Ansicht "Neue Dokumente" und diese Lösung funktioniert mit allen Apps wie Fotos, Galerie, Datei-Explorer, Dropbox, Google Drive usw.). Beachten Sie jedoch, dass Sie bei Verwendung dieser Lösung ein Bild in Ihrem erstellenonActivityResult()
und darauf speichern müssen SD-Karte zum Beispiel. Wenn Sie dieses Bild beim nächsten App-Start aus der gespeicherten URL wiederherstellen, wird eine Sicherheitsausnahme für den Content Resolver ausgelöst, selbst wenn Sie Berechtigungsflags hinzufügen, wie in den Google API-Dokumenten beschrieben (genau das ist passiert, als ich einige Tests durchgeführt habe).Zusätzlich schlagen die API-Richtlinien für Android-Entwickler Folgendes vor:
quelle
Intent.ACTION_GET_CONTENT
. Wie auch immer, ich habe denIntent.createChooser()
Wrapper auf dem neuen Stand gehaltenIntent
, damit der Benutzer die App zum Surfen auswählen kann, und habe wie erwartet gearbeitet. Kann jemand Nachteile für diese Lösung sehen?Genau wie Commonsware erwähnt, sollten Sie nicht davon ausgehen, dass der Stream, über den Sie erhalten
ContentResolver
in eine Datei ist.Was Sie wirklich tun sollten, ist, das
InputStream
von zu öffnenContentProvider
und dann eine Bitmap daraus zu erstellen. Und es funktioniert auch mit 4.4 und früheren Versionen, ohne dass Überlegungen erforderlich sind.Wenn Sie mit großen Bildern umgehen, sollten Sie diese natürlich mit den entsprechenden Bildern laden
inSampleSize
folgenden Informationen : http://developer.android.com/training/displaying-bitmaps/load-bitmap.html . Aber das ist ein anderes Thema.quelle
Ich glaube, die bereits veröffentlichten Antworten sollten die Leute in die richtige Richtung bringen. Hier ist jedoch, was ich getan habe, was für den Legacy-Code, den ich aktualisierte, Sinn machte. Der Legacy-Code verwendete den URI aus der Galerie, um die Bilder zu ändern und dann zu speichern.
Vor 4.4 (und Google Drive) sahen die URIs folgendermaßen aus: content: // media / external / images / media / 41
Wie in der Frage angegeben, sehen sie häufiger so aus: content: //com.android.providers.media.documents/document/image: 3951
Da ich die Möglichkeit brauchte, Bilder zu speichern und den bereits vorhandenen Code nicht zu stören, habe ich einfach den URI aus der Galerie in den Datenordner der App kopiert. Dann entstand ein neuer URI aus der gespeicherten Bilddatei im Datenordner.
Hier ist die Idee:
Hinweis - copyAndClose () führt nur Datei-E / A aus, um InputStream in einen FileOutputStream zu kopieren. Der Code wird nicht veröffentlicht.
quelle
Ich wollte nur sagen, dass diese Antwort brillant ist und ich sie lange Zeit ohne Probleme benutze. Aber vor einiger Zeit bin ich auf ein Problem gestoßen, dass DownloadsProvider URIs im Format zurückgibt
content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf
und daher die App abstürzt,NumberFormatException
da es unmöglich ist, ihre Uri-Segmente so lange zu analysieren. Dasraw:
Segment enthält jedoch eine direkte URL, mit der eine referenzierte Datei abgerufen werden kann. Also habe ich es behoben, indem ichisDownloadsDocument(uri)
if
Inhalte durch folgende ersetzt habe:quelle
Ich habe mehrere Antworten zu einer funktionierenden Lösung kombiniert, die sich aus dem Dateipfad ergibt
Der MIME-Typ ist für den Beispielzweck irrelevant.
Handhabungsergebnis
FilePickUtils
quelle
Frage
So erhalten Sie einen tatsächlichen Dateipfad von einem URI
Antworten
Meines Wissens müssen wir den Dateipfad nicht von einem URI abrufen, da wir in den meisten Fällen den URI direkt verwenden können, um unsere Arbeit zu erledigen (z. B. 1. Bitmap abrufen 2. Eine Datei an den Server senden usw. .)
1. Senden an den Server
Wir können die Datei direkt über den URI an den Server senden.
Mit dem URI können wir InputStream erhalten, den wir mit MultiPartEntity direkt an den Server senden können.
Beispiel
2. Abrufen einer BitMap von einem URI
Wenn der URI auf das Bild zeigt, erhalten wir eine Bitmap, andernfalls null:
Bemerkungen
Referenz
quelle
Diese Android-Bibliothek behandelt die Falländerungen in KitKat (einschließlich der älteren Versionen - 2.1+):
https://github.com/iPaulPro/aFileChooser
Verwenden Sie die
String path = FileUtils.getPath(context, uri)
, um den zurückgegebenen Uri in eine Pfadzeichenfolge zu konvertieren, die auf allen Betriebssystemversionen verwendet werden kann. Weitere Informationen finden Sie hier: https://stackoverflow.com/a/20559175/860488quelle
Für diejenigen, die den Code von @Paul Burke mit Android SDK Version 23 und höher noch verwenden, wenn in Ihrem Projekt der Fehler aufgetreten ist, dass EXTERNAL_PERMISSION fehlt, und Sie sehr sicher sind, dass Sie Ihrer AndroidManifest.xml-Datei bereits Benutzerberechtigungen hinzugefügt haben. Dies liegt daran, dass Sie möglicherweise in Android API 23 oder höher und Google die Berechtigung erneut garantieren müssen, während Sie die Aktion ausführen, um zur Laufzeit auf die Datei zuzugreifen.
Das bedeutet: Wenn Ihre SDK-Version 23 oder höher ist, werden Sie bei der Auswahl der Bilddatei um die Berechtigung LESEN & SCHREIBEN gebeten und möchten deren URI kennen.
Und das Folgende ist mein Code, zusätzlich zu Paul Burkes Lösung. Ich füge diesen Code hinzu und mein Projekt funktioniert einwandfrei.
Und in Ihrer Aktivität und Ihrem Fragment, in dem Sie nach der URI fragen:
In meinem Fall definiere ich in CompatUtils.java die Methode verifyStoragePermissions (als statischen Typ, damit ich sie in anderen Aktivitäten aufrufen kann).
Es sollte auch sinnvoller sein, wenn Sie zuerst einen if-Status festlegen, um festzustellen, ob die aktuelle SDK-Version über 23 liegt oder nicht, bevor Sie die verifyStoragePermissions-Methode aufrufen.
quelle
Diese Antwort ist von m3n0R auf Frage Android bekommen echten Pfad von Uri.getPath () und ich beanspruche keine Gutschrift. Ich dachte nur, dass Leute, die dieses Problem noch nicht gelöst haben, dies nutzen könnten.
quelle
cursor.getString(idx);
Bitte versuchen Sie, die Verwendung der takePersistableUriPermission-Methode zu vermeiden, da sie für mich eine Laufzeitausnahme auslöste. / ** * Aus Galerie auswählen. * /
OnActivity für das Ergebnis zur Verarbeitung der Bilddaten:
@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) {
quelle
Wenn jemand interessiert ist, habe ich eine funktionierende Kotlin-Version erstellt für
ACTION_GET_CONTENT
:quelle
Ich habe hier einige der Antworten ausprobiert und denke, ich habe eine Lösung, die jedes Mal funktioniert und auch Berechtigungen verwaltet.
Es basiert auf der cleveren Lösung von LEO. Dieser Beitrag sollte den gesamten Code enthalten, den Sie benötigen, damit dies funktioniert, und er sollte auf jedem Telefon und jeder Android-Version funktionieren;)
Um eine Datei von einer SD-Karte auswählen zu können, benötigen Sie Folgendes in Ihrem Manifest:
Konstanten:
Überprüfen Sie die Berechtigung und starten Sie wenn möglichImagePick
Berechtigungsantwort
Berechtigungsantwort verwalten
Starten Sie die Bildauswahl
Verwalten der Bildauswahlantwort
Das war's Leute; Das funktioniert bei allen Telefonen, die ich habe.
quelle
Dies ist ein totaler Hack, aber hier ist was ich getan habe ...
Während ich mit dem Einrichten eines DocumentsProviders spielte , bemerkte ich, dass der Beispielcode (in
getDocIdForFile
, um Zeile 450) eine eindeutige ID für ein ausgewähltes Dokument generiert, basierend auf dem (eindeutigen) Pfad der Datei relativ zu dem angegebenen Stammverzeichnis, das Sie ihm geben (d. H. was SiemBaseDir
in Zeile 96 eingestellt haben).Die URI sieht also ungefähr so aus:
content://com.example.provider/document/root:path/to/the/file
Wie in den Dokumenten angegeben, wird nur ein einziger Stamm angenommen (in meinem Fall
Environment.getExternalStorageDirectory()
können Sie ihn aber woanders verwenden ... dann wird der Dateipfad beginnend am Stamm verwendet und zur eindeutigen ID, die "root:
" vorangestellt wird . Also ich Sie können den Pfad bestimmen, indem Sie den"/document/root:
Teil "aus uri.getPath () entfernen und einen tatsächlichen Dateipfad erstellen, indem Sie Folgendes tun:Ich weiß. Es ist beschämend, aber es hat funktioniert. Dies hängt wiederum davon ab, dass Sie Ihre eigenen verwenden Dokumentenanbieter in Ihrer App verwenden, um die Dokument-ID zu generieren.
(Es gibt auch eine bessere Möglichkeit, den Pfad zu erstellen, bei der nicht angenommen wird, dass "/" das Pfadtrennzeichen usw. ist. Aber Sie haben die Idee.)
quelle
file://
Absichten von einer externen Dateiauswahl verarbeitet, können Sie auch die Berechtigung wie im obigen Beispiel überprüfen, um sicherzustellen, dass sie von Ihrem benutzerdefinierten Anbieter stammt, und wenn ja, können Sie dies Verwenden Sie den Pfad auch, um eine neuefile://
Absicht mithilfe des von Ihnen extrahierten Pfads zu "fälschen" ,StartActivity()
und lassen Sie sie dann von Ihrer App abholen. Ich weiß, schrecklich.Das hat bei mir gut funktioniert:
quelle
Aufbauend auf Paul Burkes Antwort ich viele Probleme beim Auflösen des URI-Pfads einer externen SD-Karte, da die meisten der vorgeschlagenen "integrierten" Funktionen Pfade zurückgeben, die nicht in Dateien aufgelöst werden.
Dies ist jedoch mein Ansatz für sein // TODO-Handle für nicht primäre Volumes .
Beachten Sie, dass dies von der Hierarchie abhängt, die bei jedem Telefonhersteller unterschiedlich sein kann. Ich habe nicht alle getestet (es hat bisher bei Xperia Z3 API 23 und Samsung Galaxy A3 API 23 gut funktioniert).
Bitte bestätigen Sie, wenn es anderswo nicht gut funktioniert.
quelle
Die Antwort von @paul burke funktioniert sowohl für Kamera- als auch für Galeriebilder für API-Level 19 und höher. Sie funktioniert jedoch nicht, wenn das Mindest-SDK Ihres Android-Projekts auf unter 19 eingestellt ist, und einige der oben genannten Antworten funktionieren nicht für Galerie und Kamera. Nun, ich habe den Code von @paul burke geändert, der für API-Level unter 19 funktioniert. Unten ist der Code.
quelle
Die Antwort auf Ihre Frage lautet, dass Sie über Berechtigungen verfügen müssen. Geben Sie den folgenden Code in Ihre Datei manifest.xml ein:
Es hat bei mir funktioniert ...
quelle