Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android:从内容URI获取文件URI?_Android_File_Uri - Fatal编程技术网

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

Android:从内容URI获取文件URI?,android,file,uri,Android,File,Uri,在我的应用程序中,用户需要选择一个音频文件,然后由应用程序处理。问题是,为了让应用程序执行我希望它对音频文件执行的操作,我需要URI为文件格式。当我使用Android的原生音乐播放器在应用程序中浏览音频文件时,URI是一个内容URI,如下所示: content://media/external/audio/media/710 但是,使用流行的文件管理器应用程序Astro,我得到以下结果: file:///sdcard/media/audio/ringtones/GetupGetOut.mp3

在我的应用程序中,用户需要选择一个音频文件,然后由应用程序处理。问题是,为了让应用程序执行我希望它对音频文件执行的操作,我需要URI为文件格式。当我使用Android的原生音乐播放器在应用程序中浏览音频文件时,URI是一个内容URI,如下所示:

content://media/external/audio/media/710
但是,使用流行的文件管理器应用程序Astro,我得到以下结果:

file:///sdcard/media/audio/ringtones/GetupGetOut.mp3
后者对我来说更容易使用,但我当然希望应用程序具有用户选择的音频文件的功能,而不管他们使用什么程序浏览他们的收藏。所以我的问题是,有没有办法将
内容://
样式的URI转换为
文件://
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操作。

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


这是一个古老的答案,它采用了一些不推荐的、老套的方法来克服某些特定内容解析器的痛点。如果可能的话,将其与一些大颗粒盐一起使用,并使用适当的openInputStream API。

您可以使用内容解析器从
Content://
URI:

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呢?

如果您的内容Uri带有
content://com.externalstorage...
您可以使用此方法在Android 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
如果希望在获取路径使用后使用
文件://
获取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);
        }
在上面的回答中,我将视频uri转换为字节数组,但这与问题无关, 我只是复制了我的完整代码,以显示
FileInputStream
InputStream
的用法,因为它们在我的代码中工作相同

我在片段中使用了变量context,它是getActivity(),在Activity中,它只是ActivityName.this

context=getActivity()//在片段中

context=ActivityName.this//在活动中

激发灵感的答案是&。尝试已经回答过的方法使我找到了以下解决方案,其中可能主要涉及光标空的情况&从内容://文件://

要转换文件,请从获取的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);
}
要使用文件名,它将覆盖游标null大小写

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获取文件名

而且通过使用

contentResolver.openInputStream(uri)

您正在使用哪些调用不喜欢内容URI?有许多内容URI无法获取文件路径,因为并非所有内容URI都有文件路径。不要使用文件路径。请检查从选择器活动返回给您的URI的方案。如果uri.getScheme.equals(“内容”),请使用内容解析器打开它。如果uri.Scheme.equals(“文件”),请使用普通文件方法打开它。不管是哪种方式,最终都会得到一个InputStream,您可以使用公共代码处理它。实际上,我只是重新阅读了getContentResolver().openInputStream()的文档,它会自动为“内容”或“文件”的方案工作,所以您不需要检查方案。。。如果您可以安全地假设它始终是content://或file://那么openInputStream()将始终有效。是否有方法获取文件而不是InputStream(从content:…)?@kilaka您可以获取文件路径,但这很痛苦。请看,对于使用依赖于文件而不是文件流的封闭源API,但希望使用操作系统允许用户选择文件的人来说,这个答案是不够的。@DanyalAytekin引用的答案正是我所需要的(事实上,我能够修剪很多脂肪,因为我确切地知道我使用的是什么类型的文件)。谢谢,这非常有效。我不能像公认的答案所建议的那样使用InputStream。这只适用于本地文件,例如它不适用于Google Drive有时有效,有时返回file:///storage/emulated/0/... 如果方案是
内容://
,则“\u data”列(
android.provider.MediaStore.Images.ImageColumns.data
)是否始终保证存在?这是一个主要的反模式。某些ContentProviders确实提供了该列,但当您尝试绕过ContentResolver时,不能保证您对
文件
具有读/写访问权限。使用ContentResolver方法操作
content://
URI,这是谷歌工程师鼓励的官方方法。但有时我们只需要路径。我们实际上不必将文件加载到内存中。如果您没有访问权限,则“路径”是无用的。例如,如果应用程序给您一个
content://
uri,对应于它的私有内部目录中的文件,那么在新的Android版本中,您将无法将该uri与
file
API一起使用。ContentResolver是de
contentResolver.openInputStream(uri)