Android: 从内容 URI 获取文件 URI?

在我的应用程序用户是选择一个音频文件,然后应用程序处理。问题是,为了让应用程序做我想让它做的音频文件,我需要的 URI 是文件格式。当我使用 Android 的本地音乐播放器浏览应用程序中的音频文件时,URI 是一个内容 URI,看起来像这样:

content://media/external/audio/media/710

但是,使用流行的文件管理器应用程序 Astro,我得到了以下结果:

file:///sdcard/media/audio/ringtones/GetupGetOut.mp3

后者对于我来说更容易使用,但是当然我希望应用程序能够具有用户选择的音频文件的功能,而不管他们用什么程序来浏览他们的集合。所以我的问题是,有没有一种方法可以将 content://样式的 URI 转换成 file:// URI?否则,你会建议我如何解决这个问题?下面是调用选择器的代码,以供参考:

Intent ringIntent = new Intent();
ringIntent.setType("audio/mp3");
ringIntent.setAction(Intent.ACTION_GET_CONTENT);
ringIntent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(ringIntent, "Select Ringtone"), SELECT_RINGTONE);

我对内容 URI 执行以下操作:

m_ringerPath = m_ringtoneUri.getPath();
File file = new File(m_ringerPath);

然后用这个文件做一些 FileInputStream 的事情。

188510 次浏览

只需使用 getContentResolver().openInputStream(uri)从 URI 获取 InputStream

Http://developer.android.com/reference/android/content/contentresolver.html#openinputstream (android.net. Uri)

这是一个古老的回答,用一种不被推崇的、蹩脚的方法来克服一些特定的内容解析器的痛点。如果可能的话,使用适当的 openInputStream API。

您可以使用内容解析器从 content:// URI 获取 file://路径:

String filePath = null;
Uri _uri = data.getData();
Log.d("","URI = "+ _uri);
if (_uri != null && "content".equals(_uri.getScheme())) {
Cursor cursor = this.getContentResolver().query(_uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
cursor.moveToFirst();
filePath = cursor.getString(0);
cursor.close();
} else {
filePath = _uri.getPath();
}
Log.d("","Chosen path = "+ filePath);

试图通过调用 ContentResolver.query()来处理带有 content://scheme 的 URI 并不是一个好的解决方案。在运行4.2.2版本的 HTC Desire 上,查询结果可能是 NULL。

为什么不使用 ContentResolver 呢? Https://stackoverflow.com/a/29141800/3205334

如果你有一个内容 Uri 和 content://com.externalstorage...,你可以使用这个方法来获得一个文件夹或文件 安卓19或以上的绝对路径。

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)) {
System.out.println("getPath() uri: " + uri.toString());
System.out.println("getPath() uri authority: " + uri.getAuthority());
System.out.println("getPath() uri path: " + uri.getPath());


// ExternalStorageProvider
if ("com.android.externalstorage.documents".equals(uri.getAuthority())) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
System.out.println("getPath() docId: " + docId + ", split: " + split.length + ", type: " + type);


// This is for checking Main Memory
if ("primary".equalsIgnoreCase(type)) {
if (split.length > 1) {
return Environment.getExternalStorageDirectory() + "/" + split[1] + "/";
} else {
return Environment.getExternalStorageDirectory() + "/";
}
// This is for checking SD Card
} else {
return "storage" + "/" + docId.replace(":", "/");
}


}
}
return null;
}

您可以使用 println检查 Uri 的每个部分。下面列出了 SD 卡和设备主存的返回值。如果文件在内存中,你可以访问和删除,但是我不能使用这个方法 从 SD 卡中删除文件,只能使用这个绝对路径读取或打开图像。如果您找到使用此方法删除的解决方案,请共享。

SD 卡

getPath() uri: content://com.android.externalstorage.documents/tree/612E-B7BF%3A/document/612E-B7BF%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/612E-B7BF:/document/612E-B7BF:
getPath() docId: 612E-B7BF:, split: 1, type: 612E-B7BF

主要记忆

getPath() uri: content://com.android.externalstorage.documents/tree/primary%3A/document/primary%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/primary:/document/primary:
getPath() docId: primary:, split: 1, type: primary

如果您希望在获得路径使用之后使用 file:///获得 Uri

DocumentFile documentFile = DocumentFile.fromFile(new File(path));
documentFile.getUri() // will return a Uri with file Uri

我现在回答有点晚了,但我的代码已经测试过了

来自 uri 的检查方案:

 byte[] videoBytes;


if (uri.getScheme().equals("content")){
InputStream iStream =   context.getContentResolver().openInputStream(uri);
videoBytes = getBytes(iStream);
}else{
File file = new File(uri.getPath());
FileInputStream fileInputStream = new FileInputStream(file);
videoBytes = getBytes(fileInputStream);
}

在上面的答案中,我将 video uri 转换成了字节数组,但这与问题无关, 我只是复制了我的完整代码,以显示 FileInputStreamInputStream的用法,因为它们在我的代码中工作相同。

我在我的片段中使用了 getActivity ()这个变量上下文,在 Activity 中它只是 ActivityName.this

片段

context=ActivityName.this;//活动中

启发式答案是 Jason LaBrun达斯・雷文。尝试已经回答的方法导致我以下的解决方案,可能主要包括光标空的情况下和转换从 内容://档案://

要转换文件,从获得的 uri 读写文件

public static Uri getFilePathFromUri(Uri uri) throws IOException {
String fileName = getFileName(uri);
File file = new File(myContext.getExternalCacheDir(), fileName);
file.createNewFile();
try (OutputStream outputStream = new FileOutputStream(file);
InputStream inputStream = myContext.getContentResolver().openInputStream(uri)) {
FileUtil.copyStream(inputStream, outputStream); //Simply reads input to output stream
outputStream.flush();
}
return Uri.fromFile(file);
}

为了获得文件名的使用,它将覆盖游标为空的情况

public static String getFileName(Uri uri) {
String fileName = getFileNameFromCursor(uri);
if (fileName == null) {
String fileExtension = getFileExtension(uri);
fileName = "temp_file" + (fileExtension != null ? "." + fileExtension : "");
} else if (!fileName.contains(".")) {
String fileExtension = getFileExtension(uri);
fileName = fileName + "." + fileExtension;
}
return fileName;
}

从 mime 类型转换为文件扩展名是一个很好的选择

 public static String getFileExtension(Uri uri) {
String fileType = myContext.getContentResolver().getType(uri);
return MimeTypeMap.getSingleton().getExtensionFromMimeType(fileType);
}

光标获取文件名

public static String getFileNameFromCursor(Uri uri) {
Cursor fileCursor = myContext.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
String fileName = null;
if (fileCursor != null && fileCursor.moveToFirst()) {
int cIndex = fileCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (cIndex != -1) {
fileName = fileCursor.getString(cIndex);
}
}
return fileName;
}

试试这个。

从内容 URI 获取文件

fun fileFromContentUri(context: Context, contentUri: Uri): File {
// Preparing Temp file name
val fileExtension = getFileExtension(context, contentUri)
val fileName = "temp_file" + if (fileExtension != null) ".$fileExtension" else ""


// Creating Temp file
val tempFile = File(context.cacheDir, fileName)
tempFile.createNewFile()


try {
val oStream = FileOutputStream(tempFile)
val inputStream = context.contentResolver.openInputStream(contentUri)


inputStream?.let {
copy(inputStream, oStream)
}


oStream.flush()
} catch (e: Exception) {
e.printStackTrace()
}


return tempFile
}


private fun getFileExtension(context: Context, uri: Uri): String? {
val fileType: String? = context.contentResolver.getType(uri)
return MimeTypeMap.getSingleton().getExtensionFromMimeType(fileType)
}


@Throws(IOException::class)
private fun copy(source: InputStream, target: OutputStream) {
val buf = ByteArray(8192)
var length: Int
while (source.read(buf).also { length = it } > 0) {
target.write(buf, 0, length)
}
}

你可以用简单的方法通过 uri 获得文件名

检索文件信息

fun get_filename_by_uri(uri : Uri) : String{
contentResolver.query(uri, null, null, null, null).use { cursor ->
cursor?.let {
val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
it.moveToFirst()
return it.getString(nameIndex)
}
}
return ""
}

易于阅读

contentResolver.openInputStream(uri)

你可以使用这个函数在新的和旧的机器人中从 uri 获取文件

fun getFileFromUri(context: Context, uri: Uri?): File? {
uri ?: return null
uri.path ?: return null


var newUriString = uri.toString()
newUriString = newUriString.replace(
"content://com.android.providers.downloads.documents/",
"content://com.android.providers.media.documents/"
)
newUriString = newUriString.replace(
"/msf%3A", "/image%3A"
)
val newUri = Uri.parse(newUriString)


var realPath = String()
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if (newUri.path?.contains("/document/image:") == true) {
databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
selection = "_id=?"
selectionArgs = arrayOf(DocumentsContract.getDocumentId(newUri).split(":")[1])
} else {
databaseUri = newUri
selection = null
selectionArgs = null
}
try {
val column = "_data"
val projection = arrayOf(column)
val cursor = context.contentResolver.query(
databaseUri,
projection,
selection,
selectionArgs,
null
)
cursor?.let {
if (it.moveToFirst()) {
val columnIndex = cursor.getColumnIndexOrThrow(column)
realPath = cursor.getString(columnIndex)
}
cursor.close()
}
} catch (e: Exception) {
Log.i("GetFileUri Exception:", e.message ?: "")
}
val path = realPath.ifEmpty {
when {
newUri.path?.contains("/document/raw:") == true -> newUri.path?.replace(
"/document/raw:",
""
)
newUri.path?.contains("/document/primary:") == true -> newUri.path?.replace(
"/document/primary:",
"/storage/emulated/0/"
)
else -> return null
}
}
return if (path.isNullOrEmpty()) null else File(path)
}

你可以使用下面的 android 软件包,这对你来说更容易一些

Https://github.com/blankj/androidutilcode

使用上面的包,代码可以类似于

以下行导入使用

Import com.blaj.utilcode.util.UriUtils;

你的代码就像

File f = UriUtils.uri2File(result);

谢谢