简体   繁体   English

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

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

In my app the user is to select an audio file which the app then handles.在我的应用程序中,用户是 select 应用程序然后处理的音频文件。 The problem is that in order for the app to do what I want it to do with the audio files, I need the URI to be in file format.问题是,为了让应用程序执行我希望它对音频文件执行的操作,我需要 URI 为文件格式。 When I use Android's native music player to browse for the audio file in the app, the URI is a content URI, which looks like this:当我使用Android的原生音乐播放器浏览app中的音频文件时,URI是一个内容URI,看起来是这样的:

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

However, using the popular file manager application Astro, I get the following:但是,使用流行的文件管理器应用程序 Astro,我得到以下信息:

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

The latter is much more accessible for me to work with, but of course I want the app to have functionality with the audio file the user chooses regardless of the program they use to browse their collection.后者对我来说更容易使用,但我当然希望应用程序具有用户选择的音频文件的功能,而不管他们使用什么程序来浏览他们的收藏。 So my question is, is there a way to convert the content:// style URI into a file:// URI?所以我的问题是,有没有办法将content://样式 URI 转换为file:// URI? Otherwise, what would you recommend for me to solve this problem?否则,您会建议我如何解决这个问题? Here is the code which calls up the chooser, for reference:这是调用选择器的代码,供参考:

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

I do the following with the content URI:我对内容 URI 执行以下操作:

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

Then do some FileInputStream stuff with said file.然后用所述文件做一些 FileInputStream 的东西。

Just use getContentResolver().openInputStream(uri) to get an InputStream from a URI.只需使用getContentResolver().openInputStream(uri)从 URI 获取InputStream

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

This is an old answer with deprecated and hacky way of overcoming some specific content resolver pain points.这是克服某些特定内容解析器痛点的不推荐使用和hacky方式的旧答案。 Take it with some huge grains of salt and use the proper openInputStream API if at all possible.尽可能多地使用一些盐,并使用适当的 openInputStream API。

You can use the Content Resolver to get a file:// path from the content:// URI:您可以使用 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);

Try this....尝试这个....

get File from a content uri从内容 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)
    }
}

If you have a content Uri with content://com.externalstorage... you can use this method to get absolute path of a folder or file on Android 19 or above .如果您的内容 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;
}

You can check each part of Uri using println .您可以使用println检查 Uri 的每个部分。 Returned values for my SD card and device main memory are listed below.下面列出了我的 SD 卡和设备主存储器的返回值。 You can access and delete if file is on memory, but I wasn't able to delete file from SD card using this method, only read or opened image using this absolute path.如果文件在内存中,您可以访问和删除,但我无法使用此方法从 SD 卡中删除文件,只能使用此绝对路径读取或打开图像。 If you find a solution to delete using this method, please share.如果您找到使用此方法删除的解决方案,请分享。

SD CARD 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

MAIN MEMORY主存储器

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

If you wish to get Uri with file:/// after getting path use如果您希望在获取路径后使用file:///获取 Uri

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

Inspired answers are Jason LaBrun & Darth Raven .受启发的答案是Jason LaBrunDarth Raven Trying already answered approaches led me to below solution which may mostly cover cursor null cases & conversion from content:// to file://尝试已经回答的方法使我得到了以下解决方案,该解决方案可能主要涵盖光标空情况和从content://file://的转换

To convert file, read&write the file from gained uri要转换文件,请从获得的 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);
}

To get filename use, it will cover cursor null case要使用文件名,它将覆盖游标为空的情况

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

There is good option to converting from mime type to file extention从 mime 类型转换为文件扩展名是一个不错的选择

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

Cursor to obtain name of file光标获取文件名

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

Trying to handle the URI with content:// scheme by calling ContentResolver.query() is not a good solution.通过调用ContentResolver.query()尝试使用 content:// 方案处理 URI 不是一个好的解决方案。 On HTC Desire running 4.2.2 you could get NULL as a query result.在运行 4.2.2 的 HTC Desire 上,您可以获得 NULL 作为查询结果。

Why not to use ContentResolver instead?为什么不使用 ContentResolver 呢? https://stackoverflow.com/a/29141800/3205334 https://stackoverflow.com/a/29141800/3205334

Well I am bit late to answer,but my code is tested好吧,我回答有点晚了,但是我的代码已经过测试

check scheme from 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);
        }

In the above answer I converted the video uri to bytes array , but that's not related to question, I just copied my full code to show the usage of FileInputStream and InputStream as both are working same in my code.在上面的答案中,我将 video uri 转换为 bytes array ,但这与问题无关,我只是复制了我的完整代码以显示FileInputStreamInputStream的用法,因为它们在我的代码中工作相同。

I used the variable context which is getActivity() in my Fragment and in Activity it simply be ActivityName.this我在 Fragment 中使用了 getActivity() 变量上下文,在 Activity 中它只是 ActivityName.this

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

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

you can use this funtion for get file from uri in new android and older您可以使用此功能从新 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)
}

You can use the following android package which can be easier a bit for you您可以使用以下 android 软件包,这对您来说更容易一些

https://github.com/Blankj/AndroidUtilCode https://github.com/Blankj/AndroidUtilCode

Using the above package the code can be like使用上面的包,代码可以像

To Import use below Line要导入使用以下行

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

Your code can be like你的代码可以像

File f = UriUtils.uri2File(result);

Thanks谢谢

you can get filename by uri with simple way您可以通过 uri 以简单的方式获取文件名

Retrieving file information 检索文件信息

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

and easy to read it by using并且易于阅读

contentResolver.openInputStream(uri)

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

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