如何从Intent.ACTION_GET_CONTENT返回的URI中提取文件名?

Vik*_*šan 79 android

我正在使用第三方文件管理器从文件系统中选择一个文件(在我的情况下为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);
Run Code Online (Sandbox Code Playgroud)

这就是我所拥有的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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

代码片段来自Open Intents文件管理器说明,网址为:http:
//www.openintents.org/en/node/829

目的if-else是向后兼容.我想知道这是否是获取文件名的最佳方式,因为我发现其他文件管理器会返回所有类型的东西.

例如,Documents ToGo返回如下内容:

content://com.dataviz.dxtg.documentprovider/document/file%3A%2F%2F%2Fsdcard%2Fdropbox%2FTransfer%2Fconsent.pdf
Run Code Online (Sandbox Code Playgroud)

getContentResolver().query()返回null.

为了使事情变得更有趣,未命名的文件管理器(我从客户端日志中获取此URI)返回类似于:

/./sdcard/downloads/.bin
Run Code Online (Sandbox Code Playgroud)


是否有从URI中提取文件名的首选方法,或者应该求助于字符串解析?

Ste*_*ein 125

developer.android.com有很好的示例代码:https: //developer.android.com/guide/topics/providers/document-provider.html

一个简化版本,只提取文件名(假设"this"是一个Activity):

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 添加`new String [] {OpenableColumns.DISPLAY_NAME}`作为查询的第二个参数将过滤列以获得更有效的请求. (4认同)
  • 在你的后备中,你可以简单地调用 `uri.getLastPathSegment()` 而不是自己解析它。 (2认同)

Ken*_*ing 42

我正在使用这样的东西:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我已经对你的代码进行了一些测试.在OI文件管理器返回的URI上抛出IllegalArgumentException,因为列'title'不存在.在Documents ToGo游标上撤回的URI为null.在未知文件管理器方案返回的URI上(显然)为null. (4认同)
  • 嗯,有趣.测试方案!= null是一个好主意.其实我觉得TITLE不是你想要的.我正在使用它,因为Android中的某些媒体类型(如通过音乐选择器选择的歌曲)具有内容:// media/external/audio/media/78等URI,我想显示比ID号更相关的内容.你可以简单地使用uri.getLastPathSegment(),就像我的代码用于file:// URIs一样,如果你有一个类似于content的内容://...somefile.pdf (2认同)

Bha*_*gav 29

取自检索文件信息| 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。好主啊,为什么他们要把这么琐碎的事情弄得如此烦人呢? (6认同)
  • 它仅适用于内容文件。有关完整方法,请参阅 /sf/answers/1750367041/ (3认同)

Tam*_*afi 13

对于 Kotlin,您可以使用以下内容:

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

private fun Context.getContentFileName(uri: Uri): String? = runCatching {
    contentResolver.query(uri, null, null, null, null)?.use { cursor ->
        cursor.moveToFirst()
        return@use cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME).let(cursor::getString)
    }
}.getOrNull()
Run Code Online (Sandbox Code Playgroud)


Sam*_*uel 12

如果你想要它简短,这应该工作.

Uri uri= data.getData();
File file= new File(uri.getPath());
file.getName();
Run Code Online (Sandbox Code Playgroud)


Met*_*etu 11

获取文件名的最简单方法:

val fileName = File(uri.path).name
// or
val fileName = uri.pathSegments.last()
Run Code Online (Sandbox Code Playgroud)

如果他们给您的名字不正确,则应使用:

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
}
Run Code Online (Sandbox Code Playgroud)


小智 9

最精简的版本:

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

  • 不要忘记关闭光标:) (3认同)

Vas*_*nth 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() {

    }
}
Run Code Online (Sandbox Code Playgroud)


sav*_*eff 6

如果你想拥有带扩展名的文件名,我使用这个函数来获取它。它也适用于谷歌驱动器文件选择

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;
}
Run Code Online (Sandbox Code Playgroud)


Uda*_*ghe 6

我的回答有点过分了,但这里是如何从 android 中的 4 种不同的 uri 类型获取文件名。

  1. 内容提供商 URI[content://com.example.app/sample.png]
  2. 文件路径[file://data/user/0/com.example.app/cache/sample.png]
  3. 资源 uri[android.resource://com.example.app/1234567890][android.resource://com.example.app/raw/sample]
  4. HTTP URI[https://example.com/sample.png]
fun Uri.name(context: Context): String {
    when (scheme) {
        ContentResolver.SCHEME_FILE -> {
            return toFile().nameWithoutExtension
        }
        ContentResolver.SCHEME_CONTENT -> {
            val cursor = context.contentResolver.query(
                this,
                arrayOf(OpenableColumns.DISPLAY_NAME),
                null,
                null,
                null
            ) ?: throw Exception("Failed to obtain cursor from the content resolver")
            cursor.moveToFirst()
            if (cursor.count == 0) {
                throw Exception("The given Uri doesn't represent any file")
            }
            val displayNameColumnIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
            val displayName = cursor.getString(displayNameColumnIndex)
            cursor.close()
            return displayName.substringBeforeLast(".")
        }
        ContentResolver.SCHEME_ANDROID_RESOURCE -> {
            // for uris like [android.resource://com.example.app/1234567890]
            var resourceId = lastPathSegment?.toIntOrNull()
            if (resourceId != null) {
                return context.resources.getResourceName(resourceId)
            }
            // for uris like [android.resource://com.example.app/raw/sample]
            val packageName = authority
            val resourceType = if (pathSegments.size >= 1) {
                pathSegments[0]
            } else {
                throw Exception("Resource type could not be found")
            }
            val resourceEntryName = if (pathSegments.size >= 2) {
                pathSegments[1]
            } else {
                throw Exception("Resource entry name could not be found")
            }
            resourceId = context.resources.getIdentifier(
                resourceEntryName,
                resourceType,
                packageName
            )
            return context.resources.getResourceName(resourceId)
        }
        else -> {
            // probably a http uri
            return toString().substringBeforeLast(".").substringAfterLast("/")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)