Android7.0以上Uri转路径的方法实现(已验证)

网络上看到过很多种Uri转路径的方法,可基本上都只适用于很少的Uri值,可能没有结果(例如,对于由MediaStore索引的非本地文件),也可能没有可用的结果(例如,对于可移动存储上的文件)。

解决方法

使用ContentResolver和openInputStream()在Uri标识的内容上获取InputStream。在控制的文件上使用InputStream和FileOutputStream复制内容,然后使用该文件。

代码如下:

private static String getFilePathForN(Context context, Uri uri) {
  try {
    Cursor returnCursor = context.getContentResolver().query(uri, null, null, null, null);
    int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
    returnCursor.moveToFirst();
    String name = (returnCursor.getString(nameIndex));
    File file = new File(context.getFilesDir(), name);
    InputStream inputStream = context.getContentResolver().openInputStream(uri);
    FileOutputStream outputStream = new FileOutputStream(file);
    int read = 0;
    int maxBufferSize = 1 * 1024 * 1024;
    int bytesAvailable = inputStream.available();
    int bufferSize = Math.min(bytesAvailable, maxBufferSize);
    final byte[] buffers = new byte[bufferSize];
    while ((read = inputStream.read(buffers)) != -1) {
      outputStream.write(buffers, 0, read);
    }
    returnCursor.close();
    inputStream.close();
    outputStream.close();
    return file.getPath();
  } catch (Exception e) {
    e.printStackTrace();
  }
  return null;
}

附上全系统的代码:

/**
 * 文件Uri转路径(兼容各品牌手机)
 */
public class PathUtils {
  /**
   * android7.0以上处理方法
   */
  private static String getFilePathForN(Context context, Uri uri) {
    try {
      Cursor returnCursor = context.getContentResolver().query(uri, null, null, null, null);
      int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
      returnCursor.moveToFirst();
      String name = (returnCursor.getString(nameIndex));
      File file = new File(context.getFilesDir(), name);
      InputStream inputStream = context.getContentResolver().openInputStream(uri);
      FileOutputStream outputStream = new FileOutputStream(file);
      int read = 0;
      int maxBufferSize = 1 * 1024 * 1024;
      int bytesAvailable = inputStream.available();

      int bufferSize = Math.min(bytesAvailable, maxBufferSize);

      final byte[] buffers = new byte[bufferSize];
      while ((read = inputStream.read(buffers)) != -1) {
        outputStream.write(buffers, 0, read);
      }
      returnCursor.close();
      inputStream.close();
      outputStream.close();
      return file.getPath();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

  /**
   * 全平台处理方法
   */
  public static String getPath(final Context context, final Uri uri) throws Exception {

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    final boolean isN = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;

    if (isN) {
      return getFilePathForN(context, uri);
    }

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
      // ExternalStorageProvider
      if (isExternalStorageDocument(uri)) {
        final String docId = DocumentsContract.getDocumentId(uri);
        final String[] split = docId.split(":");
        final String type = split[0];

        if ("primary".equalsIgnoreCase(type)) {
          return Environment.getExternalStorageDirectory() + "/" + split[1];
        }

      }
      // DownloadsProvider
      else if (isDownloadsDocument(uri)) {

        final String id = DocumentsContract.getDocumentId(uri);
        final Uri contentUri = ContentUris.withAppendedId(
            Uri.parse("content://downloads/public_downloads"), StringUtils.toLong(id));

        return getDataColumn(context, contentUri, null, null);
      }
      // MediaProvider
      else if (isMediaDocument(uri)) {
        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())) {
      return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
      return uri.getPath();
    }

    return null;
  }

  /**
   * 获取此Uri的数据列的值。这对于MediaStore uri和其他基于文件的内容提供程序非常有用。
   */
  public static 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);
      }
    } catch (IllegalArgumentException e){
      //do nothing
    } finally {
      if (cursor != null)
        cursor.close();
    }
    return null;
  }

  public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
  }

  public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
  }

  public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri.getAuthority());
  }
}

参考资料:https://stackoverflow.com/questions/42508383/illegalargumentexception-column-data-does-not-exist

另发现一篇,亲测,Android 4.4到Android 10可用,测试的系统有VIVO、OPPO、MIUI、EMUI...

解决的国内产商问题:华为的黄色图标管理器,他返回了4.4的标准的Uri了,不是4.4以上的标准的Uri,导致解析的时候,判断到版本 > 4.4,然后用了4.4以上的标准的解析,然后失败了,并非不回调。

直接可用的代码片段:

public class FileUtils {

  private Context context;

  public FileUtils(Context context) {
    this.context = context;
  }

  public String getFilePathByUri(Uri uri) {
    // 以 file:// 开头的
    if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
      return uri.getPath();
    }
    // 以/storage开头的也直接返回
    if (isOtherDocument(uri)) {
      return uri.getPath();
    }
    // 版本兼容的获取!
    String path = getFilePathByUri_BELOWAPI11(uri);
    if (path != null) {
      LogUtils.d("getFilePathByUri_BELOWAPI11获取到的路径为:" + path);
      return path;
    }
    path = getFilePathByUri_API11to18(uri);
    if (path != null) {
      LogUtils.d("getFilePathByUri_API11to18获取到的路径为:" + path);
      return path;
    }
    path = getFilePathByUri_API19(uri);
    LogUtils.d("getFilePathByUri_API19获取到的路径为:" + path);
    return path;
  }

  private String getFilePathByUri_BELOWAPI11(Uri uri) {
    // 以 content:// 开头的,比如 content://media/extenral/images/media/17766
    if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
      String path = null;
      String[] projection = new String[]{MediaStore.Images.Media.DATA};
      Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
      if (cursor != null) {
        if (cursor.moveToFirst()) {
          int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
          if (columnIndex > -1) {
            path = cursor.getString(columnIndex);
          }
        }
        cursor.close();
      }
      return path;
    }
    return null;
  }

  private String getFilePathByUri_API11to18(Uri contentUri) {
    String[] projection = {MediaStore.Images.Media.DATA};
    String result = null;
    CursorLoader cursorLoader = new CursorLoader(context, contentUri, projection, null, null, null);
    Cursor cursor = cursorLoader.loadInBackground();
    if (cursor != null) {
      int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
      cursor.moveToFirst();
      result = cursor.getString(column_index);
      cursor.close();
    }
    return result;
  }

  private String getFilePathByUri_API19(Uri uri) {
    // 4.4及之后的 是以 content:// 开头的,比如 content://com.android.providers.media.documents/document/image%3A235700
    if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
      if (DocumentsContract.isDocumentUri(context, uri)) {
        if (isExternalStorageDocument(uri)) {
          // ExternalStorageProvider
          String docId = DocumentsContract.getDocumentId(uri);
          String[] split = docId.split(":");
          String type = split[0];
          if ("primary".equalsIgnoreCase(type)) {
            if (split.length > 1) {
              return Environment.getExternalStorageDirectory() + "/" + split[1];
            } else {
              return Environment.getExternalStorageDirectory() + "/";
            }
            // This is for checking SD Card
          }
        } else if (isDownloadsDocument(uri)) {
          //下载内容提供者时应当判断下载管理器是否被禁用
          int stateCode = context.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");
          if (stateCode != 0 && stateCode != 1) {
            return null;
          }
          String id = DocumentsContract.getDocumentId(uri);
          // 如果出现这个RAW地址,我们则可以直接返回!
          if (id.startsWith("raw:")) {
            return id.replaceFirst("raw:", "");
          }
          if (id.contains(":")) {
            String[] tmp = id.split(":");
            if (tmp.length > 1) {
              id = tmp[1];
            }
          }
          Uri contentUri = Uri.parse("content://downloads/public_downloads");
          LogUtils.d("测试打印Uri: " + uri);
          try {
            contentUri = ContentUris.withAppendedId(contentUri, Long.parseLong(id));
          } catch (Exception e) {
            e.printStackTrace();
          }
          String path = getDataColumn(contentUri, null, null);
          if (path != null) return path;
          // 兼容某些特殊情况下的文件管理器!
          String fileName = getFileNameByUri(uri);
          if (fileName != null) {
            path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName;
            return path;
          }
        } else if (isMediaDocument(uri)) {
          // MediaProvider
          String docId = DocumentsContract.getDocumentId(uri);
          String[] split = docId.split(":");
          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;
          }
          String selection = "_id=?";
          String[] selectionArgs = new String[]{split[1]};
          return getDataColumn(contentUri, selection, selectionArgs);
        }
      }
    }
    return null;
  }

  private String getFileNameByUri(Uri uri) {
    String relativePath = getFileRelativePathByUri_API18(uri);
    if (relativePath == null) relativePath = "";
    final String[] projection = {
        MediaStore.MediaColumns.DISPLAY_NAME
    };
    try (Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null)) {
      if (cursor != null && cursor.moveToFirst()) {
        int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
        return relativePath + cursor.getString(index);
      }
    }
    return null;
  }

  private String getFileRelativePathByUri_API18(Uri uri) {
    final String[] projection;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
      projection = new String[]{
          MediaStore.MediaColumns.RELATIVE_PATH
      };
      try (Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null)) {
        if (cursor != null && cursor.moveToFirst()) {
          int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.RELATIVE_PATH);
          return cursor.getString(index);
        }
      }
    }
    return null;
  }

  private String getDataColumn(Uri uri, String selection, String[] selectionArgs) {
    final String column = MediaStore.Images.Media.DATA;
    final String[] projection = {column};
    try (Cursor 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);
      }
    } catch (IllegalArgumentException iae) {
      iae.printStackTrace();
    }
    return null;
  }

  private boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
  }

  private boolean isOtherDocument(Uri uri) {
    // 以/storage开头的也直接返回
    if (uri != null && uri.getPath() != null) {
      String path = uri.getPath();
      if (path.startsWith("/storage")) {
        return true;
      }
      if (path.startsWith("/external_files")) {
        return true;
      }
    }
    return false;
  }

  private boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
  }

  private boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri.getAuthority());
  }
}

调用 getFilePathByUri(Uri uri) 即可获得最终的路径。

到此这篇关于Android7.0以上Uri转路径的方法实现(已验证)的文章就介绍到这了,更多相关Android7 Uri转路径内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android如何通过URI获取文件路径示例代码

    前言 最近在工作的过程中,遇到不同 Android 版本下 URI 采用不同方式来获取文件路径的问题. 因为需求的原因,要求拍照上传或者从相册中选择图片上传,而且图片是需要经过压缩的,大小不能超过2M. 很快,拍照的这部分就搞定了.那么相册中选择图片的也是一样的道理,应该也是轻松解决了. 至于选择图片的代码,如下所示: intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENAB

  • Android Uri和文件路径互相转换的实例代码

    在项目中需要用到将Uri转换为绝对路径,在网上找到一个方法,做个笔记 网上有不少方法,但是有的对4.4后的版本无效,这里的方法可以在4.4之后的版本将Uri转换为绝对路径 public class GetPathFromUri { /** * 专为Android4.4设计的从Uri获取文件绝对路径 */ public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Bui

  • android实现Uri获取真实路径转换成File的方法

    本文实例讲述了android实现Uri获取真实路径转换成File的方法.分享给大家供大家参考.具体实现方法如下: Uri uri = data.getData(); String[] proj = { MediaStore.Images.Media.DATA }; Cursor actualimagecursor = managedQuery(uri,proj,null,null,null); int actual_image_column_index = actualimagecursor.g

  • android图片文件的路径地址与Uri的相互转换方法

    一个android文件的Uri地址一般如下: content://media/external/images/media/62026 这是一张图片的Uri,那么我们如何根据这个Uri获得其在文件系统中的路径呢? 其实很简单,直接上代码: public static String getRealFilePath( final Context context, final Uri uri ) { if ( null == uri ) return null; final String scheme

  • Android7.0以上Uri转路径的方法实现(已验证)

    网络上看到过很多种Uri转路径的方法,可基本上都只适用于很少的Uri值,可能没有结果(例如,对于由MediaStore索引的非本地文件),也可能没有可用的结果(例如,对于可移动存储上的文件). 解决方法 使用ContentResolver和openInputStream()在Uri标识的内容上获取InputStream.在控制的文件上使用InputStream和FileOutputStream复制内容,然后使用该文件. 代码如下: private static String getFilePat

  • 适配android7.0获取文件的Uri的方法

    前言# Android 7.0已经发布很久了,虽然市场份额还不是很高,但是流行起来都是早晚的事,所以适配Android 7.0刻不容缓. Android 7.0 对系统进行了很多的优化:例如文件访问权限,省电,网络,后台等等,其中最突出的就是应用外的Uri访问. 什么时候会用到Uri的应用外访问呢?举一个简单的例子,下载apk更新,这个时候会调用系统功能来安装这个apk,这就是应用外访问文件,需要传入文件的Uri. 但是这样可能会显得不太安全,万一是什么非常重要的文件就糟糕了,所以Android

  • Android圆形头像拍照后“无法加载此图片”的问题解决方法(适配Android7.0)

    Feature: 点击选择拍照或者打开相册,选取图片进行裁剪最后设置为圆形头像. Problem: 拍好照片,点击裁剪,弹Toast"无法加载此图片". Solution: 在裁剪的class里加两行代码 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 主要代码如下: public static final S

  • Android7.0上某些PopuWindow出现显示位置不正确问题的解决方法

    本文实例讲述了Android7.0上某些PopuWindow出现显示位置不正确问题的解决方法.分享给大家供大家参考,具体如下: 情景描述: 在andorid7.0及以上系统,点击某个view,本来期待有一个Popuwindow在该view下面弹出(调用PopuWindow.showAsDropDown(view)方法)但结果PopuWindow却弹出在view上方,顶在系统状态栏下面. 原因分析: 在android7.0上,如果不主动约束PopuWindow的大小,比如,设置布局大小为 MATC

  • Android7.0开发实现Launcher3去掉应用抽屉的方法详解

    本文实例讲述了Android7.0开发实现Launcher3去掉应用抽屉的方法.分享给大家供大家参考,具体如下: 年初做过一个项目,有一个需求就是需要将桌面变为单层不需要二级菜单.最近几次有小伙伴有这个问我这个解决办法.现在我将分享给大家. 先上效果图:   功能分解 1. 去除Allapp键,调整HotSeat布局 2. 将所有应用摆在launcher第一层 3. 去掉长按时删除选项 解决方案 一.设置总开关 按照6.0 Launcher3 的模式,添加一个开关,控制是否去掉抽屉. Launc

  • spring boot2.0图片上传至本地或服务器并配置虚拟路径的方法

    最近写了关于图片上传至本地文件夹或服务器,上传路径到数据库,并在上传时预览图片.使用到的工具如下: 框架:spring boot 2.0 前端模板:thymeleaf 图片预览:js 首先,上传以及预览,js以及<input type="file">,以及预览图片的JS function Img(obj){ var imgFile = obj.files[0]; console.log(imgFile); var img = new Image(); var fr = ne

  • Android7.0版本影响开发的改进分析

    本文总结分析了Android7.0版本影响开发的改进.分享给大家供大家参考,具体如下: 低电耗模式 会对闹铃.GPS 和 Wi-Fi 扫描 产生限制. 可参考Optimizing for Doze and App Standby 使用GCM来发送和接受消息 后台优化 Android N 删除了三项隐式广播,隐式广播会在后台频繁启动已注册侦听这些广播的应用. 删除这些广播可以显著提升设备性能和用户体验. 侦听网络变化的主线程广播改为: CONNECTIVITY_CHANGE. 对所有应用都无法 发

  • 基于JAVA文件中获取路径及WEB应用程序获取路径的方法

    1. 基本概念的理解 `绝对路径`:你应用上的文件或目录在硬盘上真正的路径,如:URL.物理路径 例如: c:/xyz/test.txt代表了test.txt文件的绝对路径: http://www.sun.com/index.htm也代表了一个URL绝对路径: `相对路径`:相对与某个基准目录的路径,包含Web的相对路径(HTML中的相对目录). 例如: 在Servlet中,"/"代表Web应用的根目录,和物理路径的相对表示. 例如: "./"代表当前目录,&quo

  • Android7.0自动更新适配 包解析异常

    在Android7.0的手机上,自动更新的时候出现包解析异常,在其他的手机上没有这个问题. 原因: Android7.0引入私有目录被限制访问和StrictMode API .私有目录被限制访问是指在Android7.0中为了提高应用的安全性,在7.0上应用私有目录将被限制访问.StrictMode API是指禁止向你的应用外公开 file:// URI. 如果一项包含文件 file:// URI类型 的 Intent 离开你的应用,则会报出异常. 解决办法: 第一步:在AndroidManif

  • 解决Android7.0更新后无法安装的问题

    最近在我们的应用中加入更新功能,按照往常一样加入代码 if (!apkfile.exists()) { Toast.makeText(mContext, "下载的安装包不存在", Toast.LENGTH_SHORT).show(); return; } Intent install = new Intent(Intent.ACTION_VIEW); install.setDataAndType(Uri.fromFile(apkfile), "application/vnd.

随机推荐