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


101

我正在使用3rd party文件管理器从文件系统中选择文件(以我的情况为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);
            }
        }
    }
}

该代码段是从此处提供的Open Intents File Manager说明中借用的: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

在上面getContentResolver().query()返回null

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

/./sdcard/downloads/.bin


是否有一种从URI提取文件名的首选方法,或者应该采用字符串解析的方法?


也许有更好的回答同一个问题:stackoverflow.com/questions/8646246/...
雷米

Answers:


163

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

如果您尝试从相机读取文件的mimeType或fileName,则首先必须通知MediaScanner,它将在方法stackoverflow.com/a/5815005/2163045file://content://onScanCompleted(String path, Uri uri)
中将

10
new String[]{OpenableColumns.DISPLAY_NAME}作为查询的第二个参数添加将对列进行过滤,以获得更有效的请求。
JM Lord

OpenableColumns.DISPLAY_NAME不适用于我,我MediaStore.Files.FileColumns.TITLE改用了。
德米特里·科皮托夫

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上,抛出IllegalArgumentException,因为“标题”列不存在。在Documents ToGo重新定义的URI上,游标为null。在未知文件管理器方案返回的URI上(显然)为null。
ViktorBrešan2011年

2
嗯,有趣。是的,测试方案!= null是一个好主意。实际上,我认为您不需要TITLE。我之所以使用它,是因为Android中的某些媒体类型(例如通过音乐选择器选择的歌曲)具有URI,例如content:// media / external / audio / media / 78,我想显示比ID号更相关的内容。如果您有类似content://...somefile.pdf的URI,则可以像我的代码用于file:// URI一样简单地使用uri.getLastPathSegment()
Ken Fehling

1
uri.getLastPathSegment(); -您保存了我的一天:)
Lumis

@肯·费林:这对我没用。当我在文件浏览器中单击一个文件时,它可以工作,但是当我单击电子邮件附件时,仍然可以获得content:// ...东西。我在这里没有运气就尝试了所有建议。知道为什么吗?
路易斯·弗洛里特

1
嗨,为什么cursor不是close()d?
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 Thakur

1
我的文件名是user.jpg,但响应中却显示“ 6550”
Tushar Thakur

1
它不返回实际的文件名。实际上,它返回资源URL的最后一部分。
Emdadul Sawon

这不能用作文件!今天,大多数URI必须由MediaStore解码。我和其他人获取数字的原因。因为这些是这些文件的ID。
sud007,

这是4年前的回应。Android显然会改变每个版本。
塞缪尔

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

您可以通过以下方式导入它:导入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

由于某些原因,这不适用于marshmellow。它只是输出“音频和一些数字”
Alex

1

我的答案实际上与@Stefan Haustein非常相似。我在Android开发人员页面“ 检索文件信息”中找到了答案;与Storage Access Framework指南站点相比,此处的信息在此特定主题上更为简洁。在查询结果中,包含文件名的列索引为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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.