Intent.ACTION_GET_CONTENT에서 반환 된 URI에서 파일 이름을 추출하는 방법은 무엇입니까?


101

타사 파일 관리자를 사용하여 파일 시스템에서 파일 (제 경우에는 PDF)을 선택합니다.

이것이 내가 활동을 시작하는 방법입니다.

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType(getString(R.string.app_pdf_mime_type));
intent.addCategory(Intent.CATEGORY_OPENABLE);

String chooserName = getString(R.string.Browse);
Intent chooser = Intent.createChooser(intent, chooserName);

startActivityForResult(chooser, ActivityRequests.BROWSE);

이것이 내가 가진 것입니다 onActivityResult.

Uri uri = data.getData();
if (uri != null) {
    if (uri.toString().startsWith("file:")) {
        fileName = uri.getPath();
    } else { // uri.startsWith("content:")

        Cursor c = getContentResolver().query(uri, null, null, null, null);

        if (c != null && c.moveToFirst()) {

            int id = c.getColumnIndex(Images.Media.DATA);
            if (id != -1) {
                fileName = c.getString(id);
            }
        }
    }
}

코드 조각은 http://www.openintents.org/en/node/829에 있는 Open Intents 파일 관리자 지침 에서 차용되었습니다
.

의 목적 if-else은 이전 버전과의 호환성입니다. 다른 파일 관리자가 모든 종류의 것을 반환한다는 것을 알았 기 때문에 이것이 파일 이름을 얻는 가장 좋은 방법인지 궁금합니다.

예를 들어 Documents ToGo 는 다음과 같은 내용을 반환합니다.

content://com.dataviz.dxtg.documentprovider/document/file%3A%2F%2F%2Fsdcard%2Fdropbox%2FTransfer%2Fconsent.pdf

어떤에 getContentResolver().query()반환 null.

더 흥미롭게하기 위해 이름이 지정되지 않은 파일 관리자 (클라이언트 로그에서이 URI를 얻었습니다)는 다음과 같이 반환했습니다.

/./sdcard/downloads/.bin


URI에서 파일 이름을 추출하는 선호하는 방법이 있습니까 아니면 문자열 구문 분석에 의존해야합니까?


어쩌면 더 나은 답과 같은 질문 : stackoverflow.com/questions/8646246/...
레미

답변:


163

developer.android.com에는 이에 대한 멋진 예제 코드가 있습니다 : https://developer.android.com/guide/topics/providers/document-provider.html

파일 이름을 추출하기위한 압축 버전 ( "this"가 활동이라고 가정) :

public String getFileName(Uri uri) {
  String result = null;
  if (uri.getScheme().equals("content")) {
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    try {
      if (cursor != null && cursor.moveToFirst()) {
        result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
      }
    } finally {
      cursor.close();
    }
  }
  if (result == null) {
    result = uri.getPath();
    int cut = result.lastIndexOf('/');
    if (cut != -1) {
      result = result.substring(cut + 1);
    }
  }
  return result;
}

경우에 당신은 먼저 당신이 변환됩니다 MediaScanner 통지해야 카메라에서 파일의 mimeType를 또는 파일 이름을 읽을하려고 할 때 URI를 에서 방금 만든 파일의 file://content://onScanCompleted(String path, Uri uri)방법 stackoverflow.com/a/5815005/2163045
murt

10
new String[]{OpenableColumns.DISPLAY_NAME}쿼리에 두 번째 매개 변수로 추가 하면보다 효율적인 요청을 위해 열을 필터링합니다.
JM Lord

OpenableColumns.DISPLAY_NAME나를 위해 일하지 않았고 MediaStore.Files.FileColumns.TITLE대신 사용 했습니다.
Dmitry Kopytov

java.lang.IllegalArgumentException: Invalid column latitude불행히도 커서를 만들 때 비디오에서 충돌이 발생합니다 . 그래도 사진에는 완벽하게 작동합니다!
Lucas P.

45

나는 다음과 같은 것을 사용하고 있습니다.

String scheme = uri.getScheme();
if (scheme.equals("file")) {
    fileName = uri.getLastPathSegment();
}
else if (scheme.equals("content")) {
    String[] proj = { MediaStore.Images.Media.TITLE };
    Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
    if (cursor != null && cursor.getCount() != 0) {
        int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE);
        cursor.moveToFirst();
        fileName = cursor.getString(columnIndex);
    }
    if (cursor != null) {
        cursor.close();
    }
}

4
귀하의 코드에 대해 몇 가지 테스트를 실행했습니다. OI 파일 관리자가 반환 한 URI에서 'title'열이 존재하지 않아 IllegalArgumentException이 발생합니다. Documents ToGo 커서가 반환 한 URI에 null이 있습니다. 알 수없는 파일 관리자 체계에 의해 반환 된 URI는 (분명히) null입니다.
Viktor Brešan 2011

2
흠, 흥미 롭군요. 예, 스키마! = null을 테스트하는 것이 좋습니다. 사실 TITLE이 당신이 원하는 것 같지 않습니다. Android의 특정 미디어 유형 (음악 선택기를 통해 선택한 노래 등)에는 content : // media / external / audio / media / 78과 같은 URI가 있고 ID 번호보다 관련성이 더 높은 것을 표시하고 싶었 기 때문에 사용했습니다. 당신은 단순히 파일에 대한 내 코드의 사용과 같은 uri.getLastPathSegment ()를 사용할 수 있습니다 : // URI를 당신이 URI 내용과 같은 경우 : //...somefile.pdf
켄 펠링

1
uri.getLastPathSegment (); -당신은 내 하루를 저장했습니다 :)
Lumis

@Ken Fehling : 이것은 나를 위해 작동하지 않았습니다. 파일 브라우저에서 파일을 클릭하면 작동하지만 이메일 첨부 파일을 클릭하면 여전히 content : // ... 항목이 표시됩니다. 나는 운없이 여기에서 모든 제안을 시도했습니다. 왜 그런지 아세요?
Luis A. Florit 2013 년

1
안녕, 왜 cursor하지 않는 close()거라고?
fikr4n

34

파일 정보 검색 에서 가져옴 | Android 개발자

파일 이름 검색.

private String queryName(ContentResolver resolver, Uri uri) {
    Cursor returnCursor =
            resolver.query(uri, null, null, null, null);
    assert returnCursor != null;
    int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
    returnCursor.moveToFirst();
    String name = returnCursor.getString(nameIndex);
    returnCursor.close();
    return name;
}

1
나는 이것을 너무 오랫동안 찾고 있었다!
dasfima

7
감사합니다. 맙소사 왜 그렇게 사소한 일을 그렇게 짜증나게해야합니까?
TylerJames

1
콘텐츠 파일에서만 작동합니다. 참조 stackoverflow.com/a/25005243/6325722 전체 방법
조니 다섯

18

파일 이름을 얻는 가장 쉬운 방법 :

val fileName = File(uri.path).name
// or
val fileName = uri.pathSegments.last()

올바른 이름을 제공하지 않는 경우 다음을 사용해야합니다.

fun Uri.getName(context: Context): String {
    val returnCursor = context.contentResolver.query(this, null, null, null, null)
    val nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
    returnCursor.moveToFirst()
    val fileName = returnCursor.getString(nameIndex)
    returnCursor.close()
    return fileName
}

오직 File(uri.path).name작품, 감사합니다!
Sam Chen

17

짧게 원하면 작동합니다.

Uri uri= data.getData();
File file= new File(uri.getPath());
file.getName();

1
나는 file.getName ()하지만 실제적인 이름의 이름이 무엇입니까
Tushar 딴

1
내 파일 이름은 user.jpg하지만 난 응답에서 "6550"는 무엇입니까
Tushar 딴

1
실제 파일 이름을 반환하지 않습니다. 실제로 리소스 URL의 마지막 부분을 반환합니다.
Emdadul Sawon

이것은 파일로 작동하지 않습니다! 대부분의 URI는 오늘날 MediaStore에서 디코딩되어야합니다. 나와 다른 사람들이 숫자를 얻는 이유. 그 파일의 ID이기 때문입니다.
sud007

이것은 4 년 전에 주어진 응답이었습니다. Android는 분명히 모든 릴리스를 변경하고 있습니다.
Samuel

7

아래 코드를 사용하여 프로젝트의 Uri에서 파일 이름 및 파일 크기를 가져옵니다.

/**
 * Used to get file detail from uri.
 * <p>
 * 1. Used to get file detail (name & size) from uri.
 * 2. Getting file details from uri is different for different uri scheme,
 * 2.a. For "File Uri Scheme" - We will get file from uri & then get its details.
 * 2.b. For "Content Uri Scheme" - We will get the file details by querying content resolver.
 *
 * @param uri Uri.
 * @return file detail.
 */
public static FileDetail getFileDetailFromUri(final Context context, final Uri uri) {
    FileDetail fileDetail = null;
    if (uri != null) {
        fileDetail = new FileDetail();
        // File Scheme.
        if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
            File file = new File(uri.getPath());
            fileDetail.fileName = file.getName();
            fileDetail.fileSize = file.length();
        }
        // 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);
                int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
                fileDetail.fileName = returnCursor.getString(nameIndex);
                fileDetail.fileSize = returnCursor.getLong(sizeIndex);
                returnCursor.close();
            }
        }
    }
    return fileDetail;
}

/**
 * File Detail.
 * <p>
 * 1. Model used to hold file details.
 */
public static class FileDetail {

    // fileSize.
    public String fileName;

    // fileSize in bytes.
    public long fileSize;

    /**
     * Constructor.
     */
    public FileDetail() {

    }
}

외부 또는 내부 데이터에서 파일 이름을로드하는 문제를 해결하는 데 도움이되었습니다! 감사합니다!
브랜든

6

가장 압축 된 버전 :

public String getNameFromURI(Uri uri) {
    Cursor c = getContentResolver().query(uri, null, null, null, null);
    c.moveToFirst();
    return c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}

1
커서를 닫는 것을 잊지 마세요 :)
BekaBot

5

Kotlin의 경우 다음과 같이 사용할 수 있습니다.

object FileUtils {

   fun Context.getFileName(uri: Uri): String?
        = when (uri.scheme) {
            ContentResolver.SCHEME_FILE -> File(uri.path).name
            ContentResolver.SCHEME_CONTENT -> getCursorContent(uri)
            else -> null
        }

    private fun Context.getCursorContent(uri: Uri): String? 
        = try {
            contentResolver.query(uri, null, null, null, null)?.let { cursor ->
                cursor.run {
                    if (moveToFirst()) getString(getColumnIndex(OpenableColumns.DISPLAY_NAME))
                    else null
                }.also { cursor.close() }
            }
        } catch (e : Exception) { null }

좋은 대답이지만 제 생각에는 이러한 재미 (Context.getFileName 및 Context.getCursorContent)를 이름이 'Context'인 파일에 삽입하고 단어 개체 FileUtils를 삭제하고 확장명으로 사용하는 것이 좋습니다 (예 : val txtFileName = context.getFileName). (uri)
Alexey Simchenko

@LumisD 당신은 모든 파일에서 이러한 확장을 가져올 수 있습니다, 당신의 제안은 정확하지만 나는 메소드가 글로벌 일 때 싫어합니다. 특정 메서드를 가져 오는 경우에만보고 싶고 항상 그런 것은 아닙니다. 그리고 때로는 동일한 메소드 이름과 다른 소스를 선호하므로 필요한 항목을 올바른 위치에 가져옵니다.)
Tamim Attafi

다음과 같은 방법으로 가져올 수 있습니다. import com.example.FileUtils.getFileName 또는 context.getFileName (uri)를 작성하고 Alt + Enter를 누르면 IDE가 자동으로 수행합니다.)
Tamim Attafi

4
public String getFilename() 
{
/*  Intent intent = getIntent();
    String name = intent.getData().getLastPathSegment();
    return name;*/
    Uri uri=getIntent().getData();
    String fileName = null;
    Context context=getApplicationContext();
    String scheme = uri.getScheme();
    if (scheme.equals("file")) {
        fileName = uri.getLastPathSegment();
    }
    else if (scheme.equals("content")) {
        String[] proj = { MediaStore.Video.Media.TITLE };
        Uri contentUri = null;
        Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
        if (cursor != null && cursor.getCount() != 0) {
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE);
            cursor.moveToFirst();
            fileName = cursor.getString(columnIndex);
        }
    }
    return fileName;
}

이것은 내 경우에는 작동하지 않았지만 Vasanth 대답은 작동했습니다.
Nabzi

2
String Fpath = getPath(this, uri) ;
File file = new File(Fpath);
String filename = file.getName();

4
코드가하는 일을 설명해 주시겠습니까? 이렇게하면 귀하의 답변 이이 문제로 인해 걸림돌이되는 다른 사용자에게 더 유용합니다. 감사합니다
wmk 2015-08-16

어떤 이유로 이것은 marshmellow에서 작동하지 않습니다. 그것은 단지 "오디오 및 일부 번호를"출력
알렉스

1

내 버전의 답변은 실제로 @Stefan Haustein과 매우 유사합니다. Android 개발자 페이지 파일 정보 검색 에서 답변을 찾았습니다 . 여기에있는 정보는 스토리지 액세스 프레임 워크 가이드 사이트 보다이 특정 주제에 대해 더욱 요약되어 있습니다. 쿼리 결과에서 파일 이름을 포함하는 열 인덱스는 OpenableColumns.DISPLAY_NAME입니다. 열 인덱스에 대한 다른 답변 / 솔루션은 나를 위해 일하지 않았습니다. 다음은 샘플 함수입니다.

 /**
 * @param uri uri of file.
 * @param contentResolver access to server app.
 * @return the name of the file.
 */
def extractFileName(uri: Uri, contentResolver: ContentResolver): Option[String] = {

    var fileName: Option[String] = None
    if (uri.getScheme.equals("file")) {

        fileName = Option(uri.getLastPathSegment)
    } else if (uri.getScheme.equals("content")) {

        var cursor: Cursor = null
        try {

            // Query the server app to get the file's display name and size.
            cursor = contentResolver.query(uri, null, null, null, null)

            // Get the column indexes of the data in the Cursor,
            // move to the first row in the Cursor, get the data.
            if (cursor != null && cursor.moveToFirst()) {

                val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                fileName = Option(cursor.getString(nameIndex))
            }

        } finally {

            if (cursor != null) {
                cursor.close()
            }

        }

    }

    fileName
}

1

먼저 URI개체를 URL개체 로 변환 한 다음 File개체를 사용 하여 파일 이름을 검색해야합니다.

try
    {
        URL videoUrl = uri.toURL();
        File tempFile = new File(videoUrl.getFile());
        String fileName = tempFile.getName();
    }
    catch (Exception e)
    {

    }

그게 아주 쉽습니다.


1

xamarin / c #에 대한 Stefan Haustein 함수 :

public string GetFilenameFromURI(Android.Net.Uri uri)
        {
            string result = null;
            if (uri.Scheme == "content")
            {
                using (var cursor = Application.Context.ContentResolver.Query(uri, null, null, null, null))
                {
                    try
                    {
                        if (cursor != null && cursor.MoveToFirst())
                        {
                            result = cursor.GetString(cursor.GetColumnIndex(OpenableColumns.DisplayName));
                        }
                    }
                    finally
                    {
                        cursor.Close();
                    }
                }
            }
            if (result == null)
            {
                result = uri.Path;
                int cut = result.LastIndexOf('/');
                if (cut != -1)
                {
                    result = result.Substring(cut + 1);
                }
            }
            return result;
        }

1

확장자가있는 파일 이름을 갖고 싶다면이 함수를 사용하여 가져옵니다. 또한 Google 드라이브 파일 선택과 함께 작동합니다.

public static String getFileName(Uri uri) {
    String result;

    //if uri is content
    if (uri.getScheme() != null && uri.getScheme().equals("content")) {
        Cursor cursor = global.getInstance().context.getContentResolver().query(uri, null, null, null, null);
        try {
            if (cursor != null && cursor.moveToFirst()) {
                //local filesystem
                int index = cursor.getColumnIndex("_data");
                if(index == -1)
                    //google drive
                    index = cursor.getColumnIndex("_display_name");
                result = cursor.getString(index);
                if(result != null)
                    uri = Uri.parse(result);
                else
                    return null;
            }
        } finally {
            cursor.close();
        }
    }

    result = uri.getPath();

    //get filename + ext of path
    int cut = result.lastIndexOf('/');
    if (cut != -1)
        result = result.substring(cut + 1);
    return result;
}

1

이것은 실제로 나를 위해 일했습니다.

private String uri2filename() {

    String ret;
    String scheme = uri.getScheme();

    if (scheme.equals("file")) {
        ret = uri.getLastPathSegment();
    }
    else if (scheme.equals("content")) {
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            ret = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
        }
   }
   return ret;
}

0

이것을 시도하십시오 :

  private String displayName(Uri uri) {

             Cursor mCursor =
                     getApplicationContext().getContentResolver().query(uri, null, null, null, null);
             int indexedname = mCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
             mCursor.moveToFirst();
             String filename = mCursor.getString(indexedname);
             mCursor.close();
             return filename;
 }

0

모든 답변의 조합

여기에 제시된 모든 답변과 일부 Airgram이 SDK에서 수행 한 작업-Github에서 오픈 소스로 제공 한 유틸리티를 읽은 후 도착한 내용은 다음과 같습니다.

https://github.com/mankum93/UriUtilsAndroid/tree/master/app/src/main/java/com/androiduriutils

용법

호출만큼 간단 UriUtils.getDisplayNameSize()합니다. 콘텐츠의 이름과 크기를 모두 제공합니다.

참고 : content : // Uri에서만 작동합니다.

다음은 코드에 대한 간략한 설명입니다.

/**
 * References:
 * - https://www.programcreek.com/java-api-examples/?code=MLNO/airgram/airgram-master/TMessagesProj/src/main/java/ir/hamzad/telegram/MediaController.java
 * - /programming/5568874/how-to-extract-the-file-name-from-uri-returned-from-intent-action-get-content
 *
 * @author Manish@bit.ly/2HjxA0C
 * Created on: 03-07-2020
 */
public final class UriUtils {


    public static final int CONTENT_SIZE_INVALID = -1;

    /**
     * @param context context
     * @param contentUri content Uri, i.e, of the scheme <code>content://</code>
     * @return The Display name and size for content. In case of non-determination, display name
     * would be null and content size would be {@link #CONTENT_SIZE_INVALID}
     */
    @NonNull
    public static DisplayNameAndSize getDisplayNameSize(@NonNull Context context, @NonNull Uri contentUri){

        final String scheme = contentUri.getScheme();
        if(scheme == null || !scheme.equals(ContentResolver.SCHEME_CONTENT)){
            throw new RuntimeException("Only scheme content:// is accepted");
        }

        final DisplayNameAndSize displayNameAndSize = new DisplayNameAndSize();
        displayNameAndSize.size = CONTENT_SIZE_INVALID;

        String[] projection = new String[]{MediaStore.Images.Media.DATA, OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
        Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null);
        try {
            if (cursor != null && cursor.moveToFirst()) {

                // Try extracting content size

                int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
                if (sizeIndex != -1) {
                    displayNameAndSize.size = cursor.getLong(sizeIndex);
                }

                // Try extracting display name
                String name = null;

                // Strategy: The column name is NOT guaranteed to be indexed by DISPLAY_NAME
                // so, we try two methods
                int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                if (nameIndex != -1) {
                    name = cursor.getString(nameIndex);
                }

                if (nameIndex == -1 || name == null) {
                    nameIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
                    if (nameIndex != -1) {
                        name = cursor.getString(nameIndex);
                    }
                }
                displayNameAndSize.displayName = name;
            }
        }
        finally {
            if(cursor != null){
                cursor.close();
            }
        }

        // We tried querying the ContentResolver...didn't work out
        // Try extracting the last path segment
        if(displayNameAndSize.displayName == null){
            displayNameAndSize.displayName = contentUri.getLastPathSegment();
        }

        return displayNameAndSize;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.