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

我使用第三方文件管理器从文件系统中选择一个文件(在我的案例中是 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

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

例如,文件待办返回以下内容: < br/>

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

其中 getContentResolver().query()返回 null

为了让事情变得更有趣,未命名的文件管理器(我从客户机日志中获得了这个 URI)返回了类似下面的内容:

/./sdcard/downloads/.bin


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

146494 次浏览

我用的是这样的东西:

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

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;
}
String Fpath = getPath(this, uri) ;
File file = new File(Fpath);
String filename = file.getName();

我的答案其实和@Stefan Hausstein 非常相似。我在 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
}

首先,您需要将 URI对象转换为 URL对象,然后使用 File对象检索文件名:

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


}

就是这样,很简单。

取自 检索文件信息 | 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;
}

我使用下面的代码从我的项目 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() {


}
}

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

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

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

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
}

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

如果你想要有扩展名的文件名,我使用这个函数来得到它。 它也与谷歌驱动器文件选择工程

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

事实上,这对我很有效:

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

对于科特林,你可以使用这样的东西:

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

请试试这个:

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

最精简的版本:

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

所有答案的组合

以下是我在阅读了这里提供的所有答案之后得出的结论,以及一些 Airgram 人在他们的 SDK 中所做的工作——我在 Github 上开源的一个实用程序:

Https://github.com/mankum93/uriutilsandroid/tree/master/app/src/main/java/com/androiduriutils

用法

就像调用 UriUtils.getDisplayNameSize()一样简单,它同时提供内容的名称和大小。

注意: 只适用于内容://Uri

下面是一些代码:

/**
* References:
* - https://www.programcreek.com/java-api-examples/?code=MLNO/airgram/airgram-master/TMessagesProj/src/main/java/ir/hamzad/telegram/MediaController.java
* - https://stackoverflow.com/questions/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;
}
}

我从开发者官网找到一些信息

我从开发商的网站上得到了一些信息

取得游标

val cursor = context.contentResolver.query(fileUri, null, null, null, null)

接着就可以获取名称和文件大小

val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
cursor.moveToFirst()
val fileName = cursor.getString(nameIndex)
val size = cursor.getLong(sizeIndex)

别忘了关闭资源

别忘了关闭资源

检索文件信息

这将从 Uri 返回没有文件扩展名的文件名。

fun Uri.getFileName(): String? {
return this.path?.let { path -> File(path).name }
}

这里 我描述了一种获取扩展名的文件名的方法。

如果有人正在寻找一个 Kotlin的答案,特别是一个 extension函数,这里是去的方式。

fun Uri.getOriginalFileName(context: Context): String? {
return context.contentResolver.query(this, null, null, null, null)?.use {
val nameColumnIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
it.moveToFirst()
it.getString(nameColumnIndex)
}
}

下面是我的 utils 方法来实现这一点。你可以复制/粘贴并从任何地方使用它。

public class FileUtils {


/**
* Return file name from Uri given.
* @param context the context, cannot be null.
* @param uri uri request for file name, cannot be null
* @return the corresponding display name for file defined in uri or null if error occurs.
*/
public String getNameFromURI(@NonNull Context context,  @NonNull Uri uri) {
String result = null;
Cursor c = null;
try {
c = context.getContentResolver().query(uri, null, null, null, null);
c.moveToFirst();
result = c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
catch (Exception e){
// error occurs
}
finally {
if(c != null){
c.close();
}
}
return result;
}
...
}

还有用法。

String fileName = FileUtils.getNameFromContentUri(context, myuri);
if(fileName != null){
// do stuff
}

问候。

这个怎么样?

 Uri uri = result.getData().getClipData().getItemAt(i).getUri();
uri = Uri.parse(uri.getLastPathSegment());
System.out.println(uri.getLastPathSegment());

这将打印扩展名的文件名

我的回答可能有点过了,但是下面介绍如何在 android 中从4种不同的 uri 类型中获得文件名。

  1. 内容提供商 uri [content://com.example.app/sample.png]
  2. 文件 URI [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. 请联络 [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("/")
}
}
}

试试这个,

Intent data = result.getData();
// check condition
if (data != null) {
Uri sUri = data.getData();
@SuppressLint("Recycle")
Cursor returnCursor = getContentResolver().query(sUri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
String file_name = returnCursor.getString(nameIndex);
}