最近老板喊话做一个涉及到文件上传的项目,文件上传嘛,那肯定就需要选择文件了。选择文件想必大家都不陌生,手机端,电脑端都经常用到——点击一个打开文件选择器的小图标,进入文件选择器,选择好文件之后,啪,返回一个文件所在的真实路径。。。当时想了想貌似也挺简单的,就网上查了点资料然后开始自己码代码,码完之后各种修改(主要是各种权限,所以亲们记得加上)然后欢天喜地的开始真机测试,哟!成功了哎!满心欢喜的来来回回拨弄了好几次,好吧。。。让你浪,出问题了吧...=.=...
在经过我来来回回的测试之后发现,参照网上的方法,一个“file”类型的在回调里面直接调用
uri.getPath();
返回的就是文件的真实路径,然后一个是“content”类型的,参照了一些网上的方法:
Uri uri = data.getData();//得到uri,后面就是将uri转化成file的过程。 String[] proj = {MediaStore.Images.Media.DATA}; Cursor actualimagecursor = managedQuery(uri, proj, null, null, null); int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); actualimagecursor.moveToFirst(); String img_path = actualimagecursor.getString(actual_image_column_index); File file = new File(img_path); Toast.makeText(MainActivity.this, file.toString(), Toast.LENGTH_SHORT).show();
然后发现有些能正常返回文件的真实路径,有些则不能,着实让我郁闷了好久...郁闷之余,有问题咱也得解决不是,干瞪眼也于事无补;为了找出是什么问题,我决定将每一个测试的URI打印出来看看是怎么回事,以下是我的真机honor7打开文件选择器的界面:
我分别对图片、视频、音频、内部存储空间、浏览器-文件管理以及文件管理选择文件所返回的URI进行了测试,测试结果如下:
1、文件选择器——图片 isMediaDocument***content://com.android.providers.media.documents/document/image%3A23 2、文件选择器——视频 isMediaDocument***content://com.android.providers.media.documents/document/video%3A54934 3、文件选择器——音频 isMediaDocument***content://com.android.providers.media.documents/document/audio%3A35883 4、文件选择器——内部存储空间 isExternalStorageDocument***content://com.android.externalstorage.documents/document/primary%3ATest%2FROC2018421103253.wav 5、文件选择器——浏览器-文件管理 file***file:///storage/emulated/0/kiwi/gamecenter/gameCenter_hy3.apk 6、文件选择器——文件管理(也就是我们平常时查找文件用的手机自带文件管理器) content***content://media/external/audio/media/72232
然后我发现了,对于“content”类型,能用上面提到的方法转换成功的,就只有第6点的文件管理所返回的URI能得到正确的文件真实路径,而对1、2、3、4点的URI进行转换时则出现了问题,这到底是为什么呢?经过仔细比对,发现1、2、3、4所对应的URI虽然也都是“content”类型,但是跟第6点所对应的URI来对比,明显发现前者的格式是不一样的,带着疑惑的我上了度娘(想什么呢你们。。),终于发现Android从4.4版本(API_19)开始多了个DocumentsProvider(观察仔细的你们一定也发现了1、2、3、4点里面所返回的URI都包含有“documents”字样对不对),所以上述的方法只适合4.4(不包含)以下版本使用,如果是4.4及以上版本则不好用了,所以,该怎么办呢?答案当然是——贴上代码了!亲们你们说对不对?
首先,是打开文件选择器的代码:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*");//设置类型,我这里是任意类型,任意后缀的可以这样写。 intent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(intent,1);
其次,是回调的代码:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1) { if (resultCode == RESULT_OK) { Uri uri = data.getData(); if (uri != null) { String path = getPath(this, uri); if (path != null) { File file = new File(path); if (file.exists()) { String upLoadFilePath = file.toString(); String upLoadFileName = file.getName(); } } } } } }
很多人就问了,getPath(this,uri)方法呢。。。别急,这是在这呢嘛
public 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)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); // Log.i(TAG,"isExternalStorageDocument***"+uri.toString()); // Log.i(TAG,"docId***"+docId); // 以下是打印示例: // isExternalStorageDocument***content://com.android.externalstorage.documents/document/primary%3ATset%2FROC2018421103253.wav // docId***primary:Test/ROC2018421103253.wav final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } } // DownloadsProvider else if (isDownloadsDocument(uri)) { // Log.i(TAG,"isDownloadsDocument***"+uri.toString()); final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { // Log.i(TAG,"isMediaDocument***"+uri.toString()); final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{split[1]}; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { // Log.i(TAG,"content***"+uri.toString()); return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { // Log.i(TAG,"file***"+uri.toString()); return uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @param selection (Optional) Filter used in the query. * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ public String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } public boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } public boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } public boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); }
也许很多人会问,怎么不写个Demo?其实是因为我相信你们啊!核心代码都给出来了难道区区一个布局文件还会写不出?不可能的,这辈子都不可能的。。
最近在搞Android端发送文件,PC端接收文件这些东东(多大用不知道就是觉得挺好玩的),看看到时候小伙伴如果需求量大的话会在接下来的博客里面写写,讲解一下,到时候会有相应的Demo。
最后面贴上前面获取的URI经过代码所转换成的文件真实路径供各位小伙伴进行参考:
/storage/emulated/0/Pictures/JPEG_20170427_084319-1775672039.jpg /storage/emulated/0/BaiduNetdisk/我的资源/Python/02-数学课程/ML_机器学习中的数学班/第10讲 极大似然估计.mp4 /storage/emulated/0/qqmusic/song/薛之谦 - 深深爱过你(前世) [mqms2].flac /storage/emulated/0/Test/ROC2018421103253.wav /storage/emulated/0/kiwi/gamecenter/gameCenter_hy3.apk /storage/emulated/0/Test/ROC2018421111222.wav祝大家生活愉快哟~~~~