繁体   English   中英

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

[英]Android: Getting a file URI from a content URI?

在我的应用程序中,用户是 select 应用程序然后处理的音频文件。 问题是,为了让应用程序执行我希望它对音频文件执行的操作,我需要 URI 为文件格式。 当我使用Android的原生音乐播放器浏览app中的音频文件时,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 的东西。

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

http://developer.android.com/reference/android/content/ContentResolver.html#openInputStream(android.net.Uri)

这是克服某些特定内容解析器痛点的不推荐使用和hacky方式的旧答案。 尽可能多地使用一些盐,并使用适当的 openInputStream API。

您可以使用 Content Resolver 从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);

尝试这个....

从内容 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 包含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

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

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

受启发的答案是Jason LaBrunDarth Raven 尝试已经回答的方法使我得到了以下解决方案,该解决方案可能主要涵盖光标空情况和从content://file://的转换

要转换文件,请从获得的 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;
}

通过调用ContentResolver.query()尝试使用 content:// 方案处理 URI 不是一个好的解决方案。 在运行 4.2.2 的 HTC Desire 上,您可以获得 NULL 作为查询结果。

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

好吧,我回答有点晚了,但是我的代码已经过测试

从 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 转换为 bytes array ,但这与问题无关,我只是复制了我的完整代码以显示FileInputStreamInputStream的用法,因为它们在我的代码中工作相同。

我在 Fragment 中使用了 getActivity() 变量上下文,在 Activity 中它只是 ActivityName.this

context=getActivity(); //在片段中

context=ActivityName.this; // 在活动中

您可以使用此功能从新 android 和旧版本的 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

使用上面的包,代码可以像

要导入使用以下行

导入 com.blankj.utilcode.util.UriUtils;

你的代码可以像

File f = UriUtils.uri2File(result);

谢谢

您可以通过 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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM