source

URI, Android KitKat의 새로운 스토리지 액세스 프레임워크에서 실제 경로 얻기

lovecheck 2023. 9. 4. 20:25
반응형

URI, Android KitKat의 새로운 스토리지 액세스 프레임워크에서 실제 경로 얻기

Android 4.4(KitKat)에서 새로운 갤러리에 액세스하기 전에 저는 다음과 같은 방법으로 SD 카드의 실제 경로를 얻었습니다.

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);
}

이제, 의도.ACTION_GET_CONTENT가 다른 데이터를 반환합니다.

이전:

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

이제:

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

어떻게 하면 SD 카드의 실제 경로를 얻을 수 있습니까?

이렇게 하면 MediaProvider, DownloadsProvider 및 ExternalStorageProvider에서 파일 경로를 가져오는 동시에 언급한 비공식 ContentProvider 메서드로 돌아갑니다.

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

이것들은 제 오픈 소스 라이브러리인 FileChooser에서 가져온 것입니다.

참고: 이 답변은 문제의 일부를 해결합니다.완전한 해결책(라이브러리 형태)은 Paul Burke의 답변을 참조하십시오.

하여 URI를 얻을 수 .document id그런 다음 질문을 합니다.MediaStore.Images.Media.EXTERNAL_CONTENT_URI또는MediaStore.Images.Media.INTERNAL_CONTENT_URI(SD 카드 상황에 따라 다름).

문서 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();

참조:이 솔루션을 가져온 게시물을 찾을 수 없습니다.저는 원래 포스터에 여기에 기여해 달라고 부탁하고 싶었습니다.오늘 밤에 좀 더 살펴볼게요.

아래 답변은 더 이상 존재하지 않는 페이지에 https://stackoverflow.com/users/3082682/cvizv 에 의해 작성되었습니다. 그가 질문에 대답할 충분한 담당자가 없기 때문에, 저는 그것을 게시합니다.저는 학점이 없습니다.

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;
}

편집: 코드에 흐름이 있습니다. 장치에 둘 이상의 외부 저장소(외부 sdcard, 외부 usb 등)가 있는 경우 코드 위에 있는 것은 기본 저장소에서 작동하지 않습니다.

킷캣에서 새로운 갤러리에 액세스하기 전에 나는 이 방법으로 sdcard에서 나의 실제 경로를 얻었습니다.

그것은 결코 믿을 수 없었습니다.다음과 같은 요건은 없습니다.Uri당신이 돌아왔다는 것.ACTION_GET_CONTENT또는ACTION_PICK은 요을인합니다로 인덱싱되어야 합니다.MediaStore또는 파일 시스템에 있는 파일을 나타내야 합니다.Uri예를 들어 암호화된 파일이 즉시 해독되는 스트림을 나타낼 수 있습니다.

어떻게 하면 sdcard에서 실제 경로를 얻을 수 있습니까?

다에해파일있필없습다니요는에 하는 파일이 필요는 .Uri.

네, 저는 정말로 길이 필요합니다.

그런 다음 스트림에서 자신의 임시 파일로 파일을 복사하고 사용합니다.더 좋은 것은 스트림을 직접 사용하고 임시 파일을 사용하지 않는 것입니다.

의도가 변경되었습니다.ACTION_GET_CONTENT for Intent.ACTION_PICK

그것은 당신의 상황에 도움이 되지 않을 것입니다.다음과 같은 요건은 없습니다.ACTION_PICK는 " 한대반니다입"에 대한 입니다.Uri어떻게든 마법으로 도출할 수 있는 파일 시스템에 파일이 있습니다.

저도 똑같은 문제를 겪었습니다.웹사이트에 업로드하기 위해서는 파일 이름이 필요합니다.

제가 PICK으로 마음을 바꾸면 효과가 있었습니다.이것은 Android 4.4용 AVD와 Android 2.1용 AVD에서 테스트되었습니다.

READ_EXTERNAL_STORAGE 권한 추가:

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

의도 변경:

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

실제 경로를 얻기 위해 코드를 변경할 필요가 없었습니다.

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

이 답변은 당신의 다소 모호한 설명에 근거한 것입니다.합니다: 당이의도행취동거생라각다니합고했을을신로으적.Intent.ACTION_GET_CONTENT

그리고 이제 당신은content://com.android.providers.media.documents/document/image:62이전의 미디어 제공자 URI 대신 뒤로 돌아갔죠?

4는 Android 4.4(KitKat)에서 Documents Activity를 열 때 .Intent.ACTION_GET_CONTENT가 실행되어 이미지를 선택할 수 있는 그리드 보기(또는 목록 보기)로 이동합니다. 그러면 다음 URI가 호출 컨텍스트(예)로 반환됩니다.content://com.android.providers.media.documents/document/image:62(이것은 새 문서 공급자에 대한 URI이며, 일반 문서 공급자 URI를 클라이언트에 제공하여 기본 데이터를 추상화합니다.)

수 .Intent.ACTION_GET_CONTENTDocuments Activity(문서 작업)의 드로어를 사용하면(왼쪽에서 오른쪽으로 끌면 선택할 수 있는 갤러리가 있는 드로어 UI가 표시됩니다).킷캣 전처럼.

DocumentsActivity 클래스에서 선택할 항목을 선택하고 URI 파일이 필요한 경우 다음(이것은 hacky! 경고) 쿼리(콘텐츠 해결기 포함)를 수행할 수 있습니다.content://com.android.providers.media.documents/document/image:62URI를 선택하고 커서에서 _display_name 값을 읽습니다.이 이름은 다소 고유한 이름입니다(로컬 파일의 파일 이름만 해당). 미디어 공급자에게 선택할 때 이 이름을 사용하여 이 선택에 해당하는 올바른 행을 여기서 가져올 수도 있습니다.

문서 공급자에 액세스하는 권장 방법은 다음에서 찾을 수 있습니다(파일/비트맵을 읽을 입력 스트림 또는 파일 설명자 가져오기).

문서 공급자 사용 예제

사용해 보십시오.

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

onActivityResult에서 다음을 사용합니다.

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

여기 폴 버크의 답변의 업데이트된 버전이 있습니다.Android 4.4(KitKat) 이하 버전에는 DocumentsContract 클래스가 없습니다.

KitKat 아래 버전에서 작업하려면 다음 클래스를 만듭니다.

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

Android 4.4(KitKat) 및 다른 모든 이전 버전에서도 원활하게 실행되도록 ActivityResult()의 갤러리 선택기 코드의 이전 버전에서 다음과 같은 변경/수정 작업을 수행해야 합니다.

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
}

언급URL : https://stackoverflow.com/questions/20067508/get-real-path-from-uri-android-kitkat-new-storage-access-framework

반응형