KitKatの前(または新しいGalleryの前)、Intent.ACTION_GET_CONTENT
は次のようなURIを返しました。
内容://メディア/外部/画像/メディア/ 3951。
ContentResolver
を使用してMediaStore.Images.Media.DATA
を問い合わせると、ファイルのURLが返されました。
しかしKitKatでは、Galleryは次のように( "Last"経由で)URIを返します。
content://com.Android.providers.media.documents/document/image:3951
これをどうやって処理するのですか?
これを試して:
if (Build.VERSION.SDK_INT <19){
Intent intent = new Intent();
intent.setType("image/jpeg");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.select_picture)),GALLERY_INTENT_CALLED);
} else {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/jpeg");
startActivityForResult(intent, GALLERY_KitKat_INTENT_CALLED);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK) return;
if (null == data) return;
Uri originalUri = null;
if (requestCode == GALLERY_INTENT_CALLED) {
originalUri = data.getData();
} else if (requestCode == GALLERY_KitKat_INTENT_CALLED) {
originalUri = data.getData();
final int takeFlags = data.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(originalUri, takeFlags);
}
loadSomeStreamAsynkTask(originalUri);
}
おそらく必要
@SuppressLint( "NewApi")
for
takePersistableUriPermission
これは特別な権限を必要とせず、Storage Access Framework、および非公式のContentProvider
パターン(_data
フィールドのファイルパス)と連携します。
/**
* 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 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());
}
このメソッドの最新版 はこちら でご覧ください。
同じ問題を抱えて、上記の解決策を試してみましたが、それは一般的にはうまくいきましたが、何らかの理由でAndroid.permission.MANAGE_DOCUMENTS
パーミッションを適切に追加しましたが、何らかの理由でUriコンテンツプロバイダのパーミッション拒否を得ていました。
とにかくKitKatのドキュメントビューの代わりに画像ギャラリーを開くことを強制することである他の解決策を見つけました:
// KitKat
i = new Intent(Intent.ACTION_PICK,Android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, CHOOSE_IMAGE_REQUEST);
そして画像をロードします。
Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
BitmapFactory.decodeStream(input , null, opts);
EDIT
ACTION_OPEN_DOCUMENT
はパーミッションフラグなどを永続化することをあなたに要求するかもしれず、一般的にしばしばセキュリティ例外をもたらします...
他の解決策はACTION_GET_CONTENT
をc.getContentResolver().openInputStream(selectedImageURI)
と組み合わせて使用することです。これはpre-KKとKKの両方で動作します。その場合、KitKatは新しいドキュメントビューを使用し、このソリューションは写真、ギャラリー、ファイルエクスプローラ、Dropbox、Google Driveなどのすべてのアプリケーションで動作しますが、このソリューションを使用するときはonActivityResult()
に画像を作成して保存する必要があります。例えばSDカードに。次回のアプリ起動時に保存されたURIからこの画像を再作成すると、Google APIドキュメントに記載されているように権限フラグを追加した場合でもコンテンツリゾルバにセキュリティ例外がスローされます。
さらにAndroid開発者向けAPIガイドラインでは、以下のことが示唆されています。
ACTION_OPEN_DOCUMENTはACTION_GET_CONTENTに代わるものではありません。使用するものは、アプリのニーズによって異なります。
あなたのアプリに単純にデータの読み込み/読み込みをさせたい場合はACTION_GET_CONTENTを使用してください。この方法では、アプリは画像ファイルなどのデータのコピーをインポートします。
アプリに、ドキュメントプロバイダが所有するドキュメントへの長期にわたる永続的なアクセスを許可する場合は、ACTION_OPEN_DOCUMENTを使用します。例としては、ユーザーがドキュメントプロバイダーに保存されている画像を編集できる写真編集アプリがあります。
Commonswareが述べたように、ContentResolver
を介して取得したストリームはファイルに変換可能であると想定してはいけません。
あなたが本当にすべきことはInputStream
からContentProvider
を開いて、それからそれからビットマップを作成することです。そしてそれは4.4以前のバージョンでも同様に動作します。リフレクションは不要です。
//cxt -> current context
InputStream input;
Bitmap bmp;
try {
input = cxt.getContentResolver().openInputStream(fileUri);
bmp = BitmapFactory.decodeStream(input);
} catch (FileNotFoundException e1) {
}
もちろん、大きな画像を扱う場合は、適切なinSampleSize
を付けて読み込む必要があります。 http://developer.Android.com/training/displaying-bitmaps/load-bitmap.html しかしそれは別の話題です。
私は、すでに投稿された回答が人々を正しい方向へ向かわせるはずだと思います。しかし、これが私が更新していたレガシコードにとって意味のあることです。従来のコードでは、ギャラリーのURIを使用して画像を変更して保存していました。
4.4(およびGoogleドライブ)より前のURIは、content:// media/external/images/media/41のようになります。
質問で述べたように、彼らはより頻繁にこのように見えます:content://com.Android.providers.media.documents/document/image:3951
画像を保存し、既存のコードを邪魔しない機能が必要だったので、URIをギャラリーからアプリのデータフォルダーにコピーしただけです。その後、データフォルダに保存された画像ファイルから新しいURIを作成しました。
これがアイデアです:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent), CHOOSE_IMAGE_REQUEST);
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
File tempFile = new File(this.getFilesDir().getAbsolutePath(), "temp_image");
//Copy URI contents into temporary file.
try {
tempFile.createNewFile();
copyAndClose(this.getContentResolver().openInputStream(data.getData()),new FileOutputStream(tempFile));
}
catch (IOException e) {
//Log Error
}
//Now fetch the new URI
Uri newUri = Uri.fromFile(tempFile);
/* Use new URI object just like you used to */
}
注 - copyAndClose()は、InputStreamをFileOutputStreamにコピーするためにファイル入出力を行います。コードは掲載されていません。
この答え は素晴らしいもので、問題なく長い間使用しています。しかし、しばらく前に、DownloadsProviderがURIをcontent://com.Android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf
の形式で返すという問題に遭遇しました。そのため、URIセグメントを長く解析できないため、appがNumberFormatException
でクラッシュします。しかしraw:
セグメントには、参照ファイルを取得するために使用できる直接のURIが含まれています。そのため、isDownloadsDocument(uri)
if
の内容を次のように置き換えて修正しました。
final String id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
return id.replaceFirst("raw:", "");
}
try {
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
Log.e("FileUtils", "Downloads provider returned unexpected uri " + uri.toString(), e);
return null;
}
}
私は複数の答えを一つの実用的な解決策にまとめました。
MIMEタイプは、この例の目的には関係ありません。
Intent intent;
if(Build.VERSION.SDK_INT >= 19){
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
}else{
intent = new Intent(Intent.ACTION_GET_CONTENT);
}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("application/octet-stream");
if(isAdded()){
startActivityForResult(intent, RESULT_CODE);
}
取扱い結果
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if( requestCode == RESULT_CODE && resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
if (uri != null && !uri.toString().isEmpty()) {
if(Build.VERSION.SDK_INT >= 19){
final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
//noinspection ResourceType
getActivity().getContentResolver()
.takePersistableUriPermission(uri, takeFlags);
}
String filePath = FilePickUtils.getSmartFilePath(getActivity(), uri);
// do what you need with it...
}
}
}
FilePickUtils
import Android.annotation.SuppressLint;
import Android.content.ContentUris;
import Android.content.Context;
import Android.database.Cursor;
import Android.net.Uri;
import Android.os.Build;
import Android.os.Environment;
import Android.provider.DocumentsContract;
import Android.provider.MediaStore;
public class FilePickUtils {
private static String getPathDeprecated(Context ctx, Uri uri) {
if( uri == null ) {
return null;
}
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = ctx.getContentResolver().query(uri, projection, null, null, null);
if( cursor != null ){
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
return uri.getPath();
}
public static String getSmartFilePath(Context ctx, Uri uri){
if (Build.VERSION.SDK_INT < 19) {
return getPathDeprecated(ctx, uri);
}
return FilePickUtils.getPath(ctx, uri);
}
@SuppressLint("NewApi")
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());
}
}
質問
URIから実際のファイルパスを取得する方法
答え
私の知る限りでは、URIからファイルパスを取得する必要はありません。ほとんどの場合、作業を完了するためにURIを直接使用することができます(1.ビットマップの取得2.サーバーへのファイルの送信など)。 。)
1。サーバーに送信中
URIだけを使用してファイルをサーバーに直接送信できます。
URIを使用してInputStreamを取得できます。これはMultiPartEntityを使用してサーバーに直接送信できます。
例
/**
* Used to form Multi Entity for a URI (URI pointing to some file, which we got from other application).
*
* @param uri URI.
* @param context Context.
* @return Multi Part Entity.
*/
public MultipartEntity formMultiPartEntityForUri(final Uri uri, final Context context) {
MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
try {
InputStream inputStream = mContext.getContentResolver().openInputStream(uri);
if (inputStream != null) {
ContentBody contentBody = new InputStreamBody(inputStream, getFileNameFromUri(uri, context));
multipartEntity.addPart("[YOUR_KEY]", contentBody);
}
}
catch (Exception exp) {
Log.e("TAG", exp.getMessage());
}
return multipartEntity;
}
/**
* Used to get a file name from a URI.
*
* @param uri URI.
* @param context Context.
* @return File name from URI.
*/
public String getFileNameFromUri(final Uri uri, final Context context) {
String fileName = null;
if (uri != null) {
// Get file name.
// File Scheme.
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
File file = new File(uri.getPath());
fileName = file.getName();
}
// Content Scheme.
else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
Cursor returnCursor =
context.getContentResolver().query(uri, null, null, null, null);
if (returnCursor != null && returnCursor.moveToFirst()) {
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
fileName = returnCursor.getString(nameIndex);
returnCursor.close();
}
}
}
return fileName;
}
2。 URIからビットマップを取得する
URIが画像を指している場合はビットマップを取得します。それ以外の場合はnullです。
/**
* Used to create bitmap for the given URI.
* <p>
* 1. Convert the given URI to bitmap.
* 2. Calculate ratio (depending on bitmap size) on how much we need to subSample the original bitmap.
* 3. Create bitmap bitmap depending on the ration from URI.
* 4. Reference - http://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri
*
* @param context Context.
* @param uri URI to the file.
* @param bitmapSize Bitmap size required in PX.
* @return Bitmap bitmap created for the given URI.
* @throws IOException
*/
public static Bitmap createBitmapFromUri(final Context context, Uri uri, final int bitmapSize) throws IOException {
// 1. Convert the given URI to bitmap.
InputStream input = context.getContentResolver().openInputStream(uri);
BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
onlyBoundsOptions.inJustDecodeBounds = true;
onlyBoundsOptions.inDither = true;//optional
onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
input.close();
if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
return null;
}
// 2. Calculate ratio.
int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
double ratio = (originalSize > bitmapSize) ? (originalSize / bitmapSize) : 1.0;
// 3. Create bitmap.
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
bitmapOptions.inDither = true;//optional
bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
input = context.getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
input.close();
return bitmap;
}
/**
* For Bitmap option inSampleSize - We need to give value in power of two.
*
* @param ratio Ratio to be rounded of to power of two.
* @return Ratio rounded of to nearest power of two.
*/
private static int getPowerOfTwoForSampleRatio(final double ratio) {
int k = Integer.highestOneBit((int) Math.floor(ratio));
if (k == 0) return 1;
else return k;
}
コメント
参照先
このAndroidライブラリは、KitKatでの大文字と小文字の変更を処理します(古いバージョンを含む - 2.1以降)。
https://github.com/iPaulPro/aFileChooser
String path = FileUtils.getPath(context, uri)
を使用して、返されたUriをすべてのOSバージョンで使用可能なパス文字列に変換します。ここでそれについての詳細を参照してください。 https://stackoverflow.com/a/20559175/860488
まだAndroid SDKバージョン23以降で@Paul Burkeのコードを使用している人のために、あなたのプロジェクトがEXTERNAL_PERMISSIONを見逃しているというエラーに遭遇し、そしてあなたがあなたのAndroidManifest.xmlファイルにすでにユーザーパーミッションを追加したのであれば間違いない。これは、Android API 23以降およびGoogleでは、実行時にファイルにアクセスするためのアクションを実行している間に再び許可を保証する必要があるためです。
SDKのバージョンが23以上の場合は、画像ファイルを選択しているときにそのURIを知りたいときに、READ&WRITE許可を求められます。
以下は、Paul Burkeの解決策に加えて、私のコードです。これらのコードを追加すると、プロジェクトは正常に機能し始めます。
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSINOS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static void verifyStoragePermissions(Activity activity) {
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
PERMISSINOS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
そして、あなたがURIを要求しているところであなたの活動と断片の中で:
private void pickPhotoFromGallery() {
CompatUtils.verifyStoragePermissions(this);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
// startActivityForResult(intent, REQUEST_PHOTO_LIBRARY);
startActivityForResult(Intent.createChooser(intent, "选择照片"),
REQUEST_PHOTO_LIBRARY);
}
私の場合は、CompatUtils.JavaがverifyStoragePermissionsメソッドを定義する場所です(静的型として、他のアクティビティ内で呼び出すことができます)。
また、verifyStoragePermissionsメソッドを呼び出す前に、現在のSDKバージョンが23より上であるかどうかを確認するために最初にif状態を作成すると、より意味があります。
これが私がすることです:
Uri selectedImageURI = data.getData(); imageFile = new File(getRealPathFromURI(selectedImageURI)); private String getRealPathFromURI(Uri contentURI) { Cursor cursor = getContentResolver().query(contentURI, null, null, null, null); if (cursor == null) { // Source is Dropbox or other similar local file path return contentURI.getPath(); } else { cursor.moveToFirst(); int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); return cursor.getString(idx); } }
注:
managedQuery()
メソッドは推奨されていないので、使用していません。
この回答は質問でm3n0Rからのものです AndroidはUri.getPath()で実際のパスを取得します 。私はちょうどこの問題をまだ解決していない人がこれを使うことができると思った。
もし誰かに興味があるなら、私はACTION_GET_CONTENT
のために実用的なKotlinバージョンを作りました:
var path: String = uri.path // uri = any content Uri
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if (path.contains("/document/image:")) { // files selected from "Documents"
databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
selection = "_id=?"
selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
} else { // files selected from all other sources, especially on Samsung devices
databaseUri = uri
selection = null
selectionArgs = null
}
try {
val projection = arrayOf(MediaStore.Images.Media.DATA,
MediaStore.Images.Media._ID,
MediaStore.Images.Media.ORIENTATION,
MediaStore.Images.Media.DATE_TAKEN) // some example data you can query
val cursor = context.contentResolver.query(databaseUri,
projection, selection, selectionArgs, null)
if (cursor.moveToFirst()) {
// do whatever you like with the data
}
cursor.close()
} catch (e: Exception) {
Log.e(TAG, e.message, e)
}
実行時例外が発生したため、takePersistableUriPermissionメソッドの使用は避けてください。/** *ギャラリーから選択してください。 * /
public void selectFromGallery() {
if (Build.VERSION.SDK_INT < AppConstants.KitKat_API_VERSION) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED);
} else {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KitKat_INTENT_CALLED);
}
}
画像データを扱う結果のOnActivity:
@Override protected void onActivityResult(intリクエストコード、int結果コード、インテントデータ){
//gallery intent result handling before kit-kat version
if(requestCode==AppConstants.GALLERY_INTENT_CALLED
&& resultCode == RESULT_OK) {
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String filePath = cursor.getString(columnIndex);
cursor.close();
photoFile = new File(filePath);
mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP);
}
//gallery intent result handling after kit-kat version
else if (requestCode == AppConstants.GALLERY_AFTER_KitKat_INTENT_CALLED
&& resultCode == RESULT_OK) {
Uri selectedImage = data.getData();
InputStream input = null;
OutputStream output = null;
try {
//converting the input stream into file to crop the
//selected image from sd-card.
input = getApplicationContext().getContentResolver().openInputStream(selectedImage);
try {
photoFile = mImgCropping.createImageFile();
} catch (IOException e) {
e.printStackTrace();
}catch(Exception e) {
e.printStackTrace();
}
output = new FileOutputStream(photoFile);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = input.read(bytes)) != -1) {
try {
output.write(bytes, 0, read);
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
私はここでいくつかの答えを試してみました、そして私は毎回うまくいくだろうと同様に許可を管理する解決策を持っていると思います。
それはLEOからの巧妙な解決策に基づいています。この記事には、これを機能させるために必要なすべてのコードが含まれている必要があります。また、あらゆる携帯電話およびAndroidバージョンで機能するはずです。
SDカードからファイルを選ぶ機能を持つためには、マニフェストにこれが必要です。
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />
private static final int PICK_IMAGE = 456; // Whatever number you like
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; // Whatever number you like
public static final String FILE_TEMP_NAME = "temp_image"; // Whatever file name you like
if (ContextCompat.checkSelfPermission(getThis(),
Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getThis(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_EXTERNAL);
}
else {
launchImagePick();
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull
String permissions[],
@NonNull
int[] grantResults) {
if (manageReadExternalPermissionResponse(this, requestCode, grantResults)) {
launchImagePick();
}
}
public static boolean manageReadExternalPermissionResponse(final Activity activity, int requestCode, int[] grantResults) {
if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission was granted, yay! Do the
// contacts-related task you need to do.
return true;
} else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {
boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity,
Manifest.permission.READ_EXTERNAL_STORAGE);
if (!showRationale) {
// The user also CHECKED "never ask again".
// You can either enable some fall back,
// disable features of your app
// or open another dialog explaining
// again the permission and directing to
// the app setting.
} else {
// The user did NOT check "never ask again".
// This is a good place to explain the user
// why you need the permission and ask if he/she wants
// to accept it (the rationale).
}
} else {
// Permission denied, boo! Disable the
// functionality that depends on this permission.
}
}
return false;
}
画像ピックを起動
private void launchImagePick() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, PICK_IMAGE);
// see onActivityResult
}
画像選択応答の管理
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE) {
if (resultCode == Activity.RESULT_OK) {
if (data != null && data.getData() != null) {
try {
InputStream inputStream = getContentResolver().openInputStream(data.getData())
if (inputStream != null) {
// No special persmission needed to store the file like that
FileOutputStream fos = openFileOutput(FILE_TEMP_NAME, Context.MODE_PRIVATE);
final int BUFFER_SIZE = 1 << 10 << 3; // 8 KiB buffer
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) > -1) {
fos.write(buffer, 0, bytesRead);
}
inputStream.close();
fos.close();
File tempImageFile = new File(getFilesDir()+"/"+FILE_TEMP_NAME);
// Do whatever you want with the File
// Delete when not needed anymore
deleteFile(FILE_TEMP_NAME);
}
}
catch (Exception e) {
e.printStackTrace();
}
} else {
// Error display
}
} else {
// The user did not select any image
}
}
}
それはすべての人たちです。これは私が持っているすべての電話で私のために働きます。
Paul Burke氏の回答 に基づいて、私は外部SDカードのURIパスを解決する多くの問題に直面しました。
しかし、これは私の// TODOが非プライマリボリュームを処理する方法です。
String resolvedPath = "";
File[] possibleExtSdComposites = context.getExternalFilesDirs(null);
for (File f : possibleExtSdComposites) {
// Reset final path
resolvedPath = "";
// Construct list of folders
ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/")));
// Look for folder "<your_application_id>"
int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID);
// ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard's root is /storage/0000-0000/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files
ArrayList<String> hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2));
// Construct list containing full possible path to the file
hierarchyList.add(tail);
String possibleFilePath = TextUtils.join("/", hierarchyList);
// If file is found --> success
if (idx != -1 && new File(possibleFilePath).exists()) {
resolvedPath = possibleFilePath;
break;
}
}
if (!resolvedPath.equals("")) {
return resolvedPath;
} else {
return null;
}
それはすべての電話メーカーで異なるかもしれない階層に依存することに注意してください - 私はそれらすべてをテストしていません(それはこれまでXperia Z3 API 23とSamsung Galaxy A3 API 23でうまくいった)。
他でうまくいかないか確認してください。
これは私にとってはうまくいきました:
else if(requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK)
{
Uri uri = data.getData();
Log.i(TAG, "old uri = " + uri);
dumpImageMetaData(uri);
try {
ParcelFileDescriptor parcelFileDescriptor =
getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
Log.i(TAG, "File descriptor " + fileDescriptor.toString());
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
options.inSampleSize =
BitmapHelper.calculateInSampleSize(options,
User.PICTURE_MAX_WIDTH_IN_PIXELS,
User.PICTURE_MAX_HEIGHT_IN_PIXELS);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
imageViewPic.setImageBitmap(bitmap);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
// get byte array here
byte[] picData = stream.toByteArray();
ParseFile picFile = new ParseFile(picData);
user.setProfilePicture(picFile);
}
catch(FileNotFoundException exc)
{
Log.i(TAG, "File not found: " + exc.toString());
}
}
これは完全なハックですが、私がやったことは...
DocumentsProvider の設定で遊んでいると、 サンプルコード (getDocIdForFile
の450行目付近)が選択されたドキュメントの一意のIDを生成することに気付きました指定したルートに相対的なファイルの(一意の)パス(つまり、mBaseDir
を96行目で設定したもの)に指定します。
したがって、URIは次のようになります。
content://com.example.provider/document/root:path/to/the/file
ドキュメントが言うように、それは単一のルートのみを想定しています(私の場合はEnvironment.getExternalStorageDirectory()
ですが、別の場所で使用できます...その後、ファイルパスを取得し、ルートから始まり、一意のIDを追加します"root:
"。したがって、uri.getPath()から"/document/root:
"部分を削除してパスを決定し、次のようにして実際のファイルパスを作成します。
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// check resultcodes and such, then...
uri = data.getData();
if (uri.getAuthority().equals("com.example.provider")) {
String path = Environment.getExternalStorageDirectory(0.toString()
.concat("/")
.concat(uri.getPath().substring("/document/root:".length())));
doSomethingWithThePath(path); }
else {
// another provider (maybe a cloud-based service such as GDrive)
// created this uri. So handle it, or don't. You can allow specific
// local filesystem providers, filter non-filesystem path results, etc.
}
知っている。恥ずかしいですが、うまくいきました。繰り返しますが、これは、アプリでownドキュメントプロバイダーを使用してドキュメントIDを生成することに依存しています。
(また、「/」がパス区切り文字であると想定しないパスを構築するより良い方法があります。しかし、あなたはアイデアを得ます。)