在具有Oreo及更高版本操作系统的Android设备中,如何从uri获取文件路径

时间:2019-11-25 10:41:18

标签: android kotlin audio

要使用Intent选择音频文件,我正在使用此功能:-

fun selectAudioFromStorage() {
    val pictureActionIntent = Intent(Intent.ACTION_GET_CONTENT, null)
    pictureActionIntent.type = "audio/*"
    pictureActionIntent.putExtra("return-data", true)
    startActivityForResult(pictureActionIntent, ResultConstants.RC_SELECT_AUDIO)
}
片段类的

onActivityResult提供这样的所选音频文件的Uri:

content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Forganfinale.mp3

在Android(Lollipop)上,使用光标将uri转换为真实路径的效果很好,但是对于Oreo设备,我无法从此音频的Uri获取真实路径。

3 个答案:

答案 0 :(得分:1)

我解决了。只需调用此getPathFromURI方法,它将返回文件路径

object RealPathHelper {

private const val BUFFER_SIZE = 1024 * 2


@TargetApi(Build.VERSION_CODES.O)
fun getPathFromURI(
    context: Context,
    contentUri: Uri,
    fileNamePrefix: String,
    defaultFileExtension: String
): String? {
    val uriPath: String = contentUri.path ?: return null
    val fileName: String = MediaFileHelper.getFileNameWithExtension(uriPath)

    if (fileName.isNotBlank()) {
        val destFile =
            createOutputFile(context, contentUri, fileNamePrefix, defaultFileExtension)
        copyUriToFile(context, contentUri, destFile)
        return destFile.absolutePath
    }
    return null
}

private fun createOutputFile(
    context: Context,
    contentUri: Uri,
    fileNamePrefix: String,
    defaultFileExtension: String
): File {
    var count = 0
    var file: File

    val uriPath: String? = contentUri.path
    val fileExtension = if (uriPath == null) defaultFileExtension
    else MediaFileHelper.getFileExtension(uriPath)

    do {
        count++

        val mFileName = "$fileNamePrefix${StringHelper.getUniqueId()}$count$fileExtension"
        val newFilePath =
            "${context.getExternalFilesDir(null)?.absolutePath}${context.getString(R.string.audio_select_directory)}/$mFileName"

        file = File(newFilePath)

    } while (file.exists() && !file.isDirectory)

    return file
}

private fun copyUriToFile(context: Context, srcUri: Uri, dstFile: File) {
    try {
        val inputStream = context.contentResolver.openInputStream(srcUri) ?: return
        val outputStream = FileOutputStream(dstFile)
        inputStream.copyTo(outputStream, BUFFER_SIZE)
        inputStream.close()
        outputStream.close()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

答案 1 :(得分:1)

由于文件路径在API 29及更高版本中无法用于公共文件,我建议您不要尝试使用它们,而应使用Java FileDescriptor

按照https://developer.android.com/training/data-storage#scoped-storage

更具体地说https://developer.android.com/training/data-storage/shared/media#open-file-descriptor

许多API允许您提供FileDescriptor而不是路径。

例如采用 https://developer.android.com/reference/android/media/MediaPlayer.html#setDataSource(java.io.FileDescriptor)

代替

https://developer.android.com/reference/android/media/MediaPlayer.html#setDataSource(java.lang.String)

要从URI中获取fileDescriptor,请执行以下操作(在Java中)


ParcelFileDescriptor pfd =
                this.getContentResolver().
                        openFileDescriptor(URI, "r");

FileDescriptor fileDescriptor = pfd.getFileDescriptor();

答案 2 :(得分:0)

我通常使用该类来获取路径,您可以看一下该类几乎适用于所有版本

from ipywidgets import interact, Dropdown

geo = {'USA':['CHI','NYC'],'Russia':['MOW','LED']}
geo2={'CHI':['1','2'],'NYC':['3','4'],'MOW':['5','6'],'LED':['7','8']}

countryW = Dropdown(options = geo.keys())
cityW = Dropdown(options = geo[countryW.value]) # options = geo[countryW.value] is to remove inital error but not that necessary.
districtW = Dropdown()

@interact(country = countryW, city = cityW, district = districtW)
def print_city(country, city, district):
    cityW.options = geo[country] # Here is the trick, i.e. update cityW.options based on country, namely countryW.value.
    districtW.options = geo2[city] # Dittoo
    print(country, city, district)

参考:https://gist.github.com/tatocaster/32aad15f6e0c50311626