Przed KitKat (lub przed nową galerią) Intent.ACTION_GET_CONTENT
zwrócił taki identyfikator URI
content: // media / external / images / media / 3951.
Użycie ContentResolver
i zapytanie dla
MediaStore.Images.Media.DATA
zwróciło adres URL pliku.
Jednak w KitKat galeria zwraca URI (poprzez „Last”) w następujący sposób:
content: //com.android.providers.media.documents/document/image: 3951
Jak sobie z tym poradzić?
android
android-intent
android-gallery
android-contentresolver
Michael Greifeneder
źródło
źródło
Uri
powinno to być możliwe do otwarcia jako strumień przezContentResolver
. Od dawna denerwuję się aplikacjami, które zakładają,content://
Uri
że plik reprezentujący plik można zawsze przekształcić w plikFile
.InputStream
onContentResolver
do wcześniej wyznaczonego miejsca, aby miało znaną nazwę pliku. Wydaje mi się to jednak marnotrawstwem. Jakieś inne sugestie?InputStream
ponad JNI? Niestety, nie ma dla ciebie aż tylu opcji.InputStream
zamiast pliku (co jest świetne). Tylko odczyt znaczników EXIF jest nieco trudny i wymaga biblioteki Drew Noakesa . Wielkie dzięki za komentarze.Odpowiedzi:
Spróbuj tego:
Prawdopodobnie potrzebujesz
dla
źródło
Nie wymaga to żadnych specjalnych uprawnień i działa z Storage Access Framework, a także z nieoficjalnym
ContentProvider
wzorcem (ścieżka pliku w_data
polu).Zobacz aktualną wersję tej metody tutaj .
źródło
Authority: com.google.android.apps.docs.storage
iSegments: [document, acc=1;doc=667]
. Nie jestem pewien, ale załóżmy, żedoc
wartością jestUri
identyfikator, o który możesz zapytać. Najprawdopodobniej będziesz potrzebować uprawnień zgodnie z opisem w „Autoryzuj swoją aplikację na Androida” tutaj: developers.google.com/drive/integrate-android-ui . Zaktualizuj tutaj, jeśli to wymyślisz._data
Nie będzie działać, gdy ContentProvider nie obsługuje. Zaleca się postępować zgodnie z instrukcjami @CommonsWare i nie używać już pełnej ścieżki do pliku, ponieważ może to być plik w chmurze Dropbox zamiast prawdziwego pliku.Miałem ten sam problem, wypróbowałem powyższe rozwiązanie, ale chociaż ogólnie działało, z jakiegoś powodu otrzymywałem odmowę zgody na dostawcę treści Uri dla niektórych obrazów, chociaż
android.permission.MANAGE_DOCUMENTS
pozwolenie zostało poprawnie dodane.W każdym razie znalazłem inne rozwiązanie, które wymusza otwarcie galerii zdjęć zamiast widoku dokumentów KITKAT z:
a następnie załaduj obraz:
EDYTOWAĆ
ACTION_OPEN_DOCUMENT
może wymagać utrzymania flag uprawnień itp. i generalnie często powoduje wyjątki bezpieczeństwa ...Innym rozwiązaniem jest użycie
ACTION_GET_CONTENT
kombinacji, zc.getContentResolver().openInputStream(selectedImageURI)
którą będzie działać zarówno na wersjach pre-KK, jak i KK. Kitkat użyje wtedy widoku nowych dokumentów, a to rozwiązanie będzie działać ze wszystkimi aplikacjami, takimi jak Zdjęcia, Galeria, Eksplorator plików, Dropbox, Dysk Google itp.), Ale pamiętaj, że korzystając z tego rozwiązania, musisz utworzyć obraz w swoimonActivityResult()
i przechowywać go na Na przykład karta SD. Ponowne odtworzenie tego obrazu z zapisanego identyfikatora URI przy następnym uruchomieniu aplikacji spowodowałoby wyjątek dotyczący bezpieczeństwa w narzędziu rozpoznawania treści, nawet jeśli dodasz flagi uprawnień zgodnie z opisem w dokumentacji interfejsu API Google (tak się stało, gdy przeprowadziłem testy)Ponadto Wytyczne API dla programistów Androida sugerują:
źródło
Intent.ACTION_GET_CONTENT
. W każdym razie zachowałemIntent.createChooser()
opakowanie na nowymIntent
, aby umożliwić użytkownikowi wybór aplikacji do przeglądania, i działałem zgodnie z oczekiwaniami. Czy ktoś może zobaczyć wady tego rozwiązania?Tak jak wspomniano w Commonsware, nie należy zakładać, że strumień, który dostajesz,
ContentResolver
można przekształcić w plik.Co naprawdę powinieneś zrobić, to otworzyć
InputStream
zContentProvider
, a następnie utworzyć z niego mapę bitową. Działa również w wersji 4.4 i wcześniejszych, bez potrzeby refleksji.Oczywiście, jeśli zajmujesz się dużymi obrazami, powinieneś załadować je odpowiednimi
inSampleSize
: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html . Ale to inny temat.źródło
Uważam, że już opublikowane odpowiedzi powinny skłonić ludzi do pójścia we właściwym kierunku. Jednak to, co zrobiłem, miało sens dla starszego kodu, który aktualizowałem. Stary kod używał identyfikatora URI z galerii do zmiany, a następnie zapisania obrazów.
Przed wersją 4.4 (i dyskiem Google) identyfikatory URI wyglądałyby następująco: content: // media / external / images / media / 41
Jak stwierdzono w pytaniu, częściej wyglądają tak: content: //com.android.providers.media.documents/document/image: 3951
Ponieważ potrzebowałem możliwości zapisywania obrazów i nie zakłócania już istniejącego kodu, właśnie skopiowałem identyfikator URI z galerii do folderu danych aplikacji. Następnie powstał nowy identyfikator URI z zapisanego pliku obrazu w folderze danych.
Oto pomysł:
Uwaga - copyAndClose () po prostu wykonuje operacje we / wy pliku, aby skopiować InputStream do FileOutputStream. Kod nie został opublikowany.
źródło
Chciałem tylko powiedzieć, że ta odpowiedź jest genialna i używam jej przez długi czas bez problemów. Ale jakiś czas temu natknąłem się na problem polegający na tym, że DownloadsProvider zwraca identyfikatory URI w formacie
content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf
i dlatego aplikacja ulega awarii,NumberFormatException
ponieważ nie można analizować segmentów URI tak długo. Aleraw:
segment zawiera bezpośrednie URI, które można wykorzystać do odzyskania pliku, do którego istnieje odniesienie. Naprawiłem to, zastępującisDownloadsDocument(uri)
if
treść następującymi:źródło
Łączę wiele odpowiedzi w jedno działające rozwiązanie, które daje ścieżkę pliku
Typ MIME nie ma znaczenia dla przykładu.
Wynik obsługi
FilePickUtils
źródło
Pytanie
Jak uzyskać rzeczywistą ścieżkę pliku z identyfikatora URI
Odpowiedź
Według mojej wiedzy nie musimy pobierać ścieżki pliku z identyfikatora URI, ponieważ w większości przypadków możemy bezpośrednio użyć identyfikatora URI do wykonania naszej pracy (np. 1. pobranie mapy bitowej 2. Wysłanie pliku na serwer itp. .)
1. Wysyłanie na serwer
Możemy bezpośrednio wysłać plik na serwer za pomocą tylko identyfikatora URI.
Za pomocą URI możemy uzyskać InputStream, który możemy bezpośrednio wysłać na serwer za pomocą MultiPartEntity.
Przykład
2. Pobieranie mapy bitowej z identyfikatora URI
Jeśli identyfikator URI wskazuje na obraz, otrzymamy bitmapę, w przeciwnym razie null:
Komentarze
Odniesienie
źródło
Ta biblioteka Androida obsługuje zmiany wielkości liter w KitKat (w tym starsze wersje - 2.1+):
https://github.com/iPaulPro/aFileChooser
Użyj
String path = FileUtils.getPath(context, uri)
do przekonwertowania zwróconego Uri na ciąg ścieżki, który można używać we wszystkich wersjach systemu operacyjnego. Zobacz więcej na ten temat tutaj: https://stackoverflow.com/a/20559175/860488źródło
Dla tych, którzy nadal używają kodu @Paul Burke z Android SDK w wersji 23 i wyższej, jeśli Twój projekt napotkał błąd, mówiąc, że brakuje EXTERNAL_PERMISSION, i jesteś pewien, że dodałeś już uprawnienia użytkownika w pliku AndroidManifest.xml. Wynika to z faktu, że możesz korzystać z interfejsu API systemu Android 23 lub nowszego, a Google wymaga ponownego zagwarantowania zgody podczas wykonywania czynności dostępu do pliku w czasie wykonywania.
Oznacza to, że: jeśli twoja wersja zestawu SDK to 23 lub więcej, podczas wybierania pliku obrazu zostaniesz zapytany o uprawnienia do odczytu i zapisu i chcesz poznać jego identyfikator URI.
Poniżej znajduje się mój kod, oprócz rozwiązania Paula Burke'a. Dodam ten kod i mój projekt zacznie działać dobrze.
A w Twojej aktywności i fragmencie, w którym prosisz o identyfikator URI:
W moim przypadku CompatUtils.java to miejsce, w którym definiuję metodę VerifyStoragePermissions (jako typ statyczny, dzięki czemu mogę wywoływać ją w ramach innych działań).
Powinno to również mieć sens, jeśli najpierw sprawdzisz stan if, aby sprawdzić, czy bieżąca wersja zestawu SDK jest wyższa niż 23, czy nie, przed wywołaniem metody replaceStoragePermissions.
źródło
Ta odpowiedź pochodzi od m3n0R na pytanie android uzyskać prawdziwą ścieżkę przez Uri.getPath () i nie twierdzę, że kredyt. Pomyślałem, że ludzie, którzy jeszcze nie rozwiązali tego problemu, mogą z niego skorzystać.
źródło
cursor.getString(idx);
Staraj się unikać używania metody takePersistableUriPermission, ponieważ podniosła ona dla mnie wyjątek czasu wykonywania. / ** * Wybierz z galerii. * /
OnActivity dla wyniku do obsługi danych obrazu:
@Override protected void onActivityResult (int requestCode, int resultCode, intent data) {
źródło
Jeśli ktoś jest zainteresowany, stworzyłem działającą wersję Kotlin dla
ACTION_GET_CONTENT
:źródło
Wypróbowałem kilka odpowiedzi tutaj i myślę, że mam rozwiązanie, które będzie działać za każdym razem i zarządza także uprawnieniami.
Opiera się na sprytnym rozwiązaniu LEO. Ten post powinien zawierać cały kod potrzebny do tego, aby działał, i powinien działać na każdym telefonie i wersji Androida;)
Aby móc wybrać plik z karty SD, potrzebujesz go w swoim manifeście:
Stałe:
Sprawdź uprawnienia i uruchom ImagePick, jeśli to możliwe
Odpowiedź na pozwolenie
Zarządzaj odpowiedzią na uprawnienia
Uruchom wybór obrazu
Zarządzaj odpowiedzią na pobranie obrazu
To wszystko ludzie; to działa dla mnie na wszystkich telefonach, które mam.
źródło
To totalny hack, ale oto co zrobiłem ...
Podczas zabawy z konfigurowaniem DocumentsProvider zauważyłem, że przykładowy kod (w
getDocIdForFile
, około linii 450) generuje unikalny identyfikator dla wybranego dokumentu na podstawie ścieżki pliku (unikalnej) w stosunku do określonego katalogu głównego, który mu podałeś (to znaczy co ustawiłeśmBaseDir
w linii 96).URI wygląda więc tak:
content://com.example.provider/document/root:path/to/the/file
Jak mówią doktorzy, zakłada tylko jeden katalog główny (w moim przypadku jest to możliwe,
Environment.getExternalStorageDirectory()
ale możesz użyć go gdzie indziej ... następnie pobiera ścieżkę pliku, zaczynając od katalogu głównego i czyni go unikalnym identyfikatorem, poprzedzającym „root:
”. może określić ścieżkę, eliminując"/document/root:
„część z uri.getPath (), tworząc rzeczywistą ścieżkę do pliku, wykonując coś takiego:Wiem. To wstyd, ale zadziałało. Ponownie, zależy to od ciebie, używając własnego dostawcy dokumentów w swojej aplikacji do wygenerowania identyfikatora dokumentu.
(Jest też lepszy sposób na zbudowanie ścieżki, która nie zakłada, że „/” jest separatorem ścieżki itp. Ale masz pomysł.)
źródło
file://
zamiary z zewnętrznego selektora plików, możesz również sprawdzić uprawnienia, jak w powyższym przykładzie, aby upewnić się, że pochodzi od niestandardowego dostawcy, a jeśli tak, możesz użyj również ścieżki, aby „wykuć” nowyfile://
zamiar, używając wyodrębnionej ścieżki, a następnieStartActivity()
pozwól aplikacji ją odebrać. Wiem okropne.To działało dobrze dla mnie:
źródło
Opierając się na odpowiedzi Paula Burke napotkałem wiele problemów z rozwiązywaniem ścieżki URI zewnętrznej karty SD, ponieważ większość sugerowanych funkcji „wbudowanych” zwraca ścieżki, które nie są rozwiązywane do plików.
Jest to jednak moje podejście do jego // obsługi TODO woluminów innych niż podstawowe .
Uwaga: zależy to od hierarchii, która może być inna u każdego producenta telefonu - nie przetestowałem ich wszystkich (do tej pory działało dobrze na Xperia Z3 API 23 i Samsung Galaxy A3 API 23).
Potwierdź, jeśli nie działa dobrze gdzie indziej.
źródło
Odpowiedź @paul burke działa dobrze zarówno dla zdjęć z aparatu, jak i galerii dla interfejsu API poziomu 19 i wyższego, ale nie działa, jeśli minimalny zestaw SDK twojego projektu na Androida jest ustawiony poniżej 19, a niektóre powyższe odpowiedzi nie działają zarówno dla galerii, jak i aparat fotograficzny. Cóż, zmodyfikowałem kod @paul burke, który działa na poziomie API poniżej 19. Poniżej znajduje się kod.
źródło
Odpowiedź na twoje pytanie brzmi: musisz mieć uprawnienia. Wpisz następujący kod w pliku manifest.xml:
To działało dla mnie ...
źródło