web-dev-qa-db-ja.com

Android Oreo(8.1)以降のURIからファイルパスを取得する方法

予想される動作

「ダウンロード」内に保存されているファイルを選択すると、ファイル名とパスを取得できるはずです

実際の動作

「ダウンロード」内に保存されているファイルを選択すると、nullが返ります。

問題を再現する手順

  1. Picking fileメソッドが呼び出されると、Androidでフォルダーが表示されます
  2. ダウンロードに移動し、ファイルを選択します
  3. URIから実際のパスを取得するときにnullを返します

これが私が実装したコードです

public static String getPath(final Context context, final Uri uri) {
   String id = DocumentsContract.getDocumentId(uri);
   if (!TextUtils.isEmpty(id)) {
      if (id.startsWith("raw:")) {
          return id.replaceFirst("raw:", "");
      }
      try {
         final boolean isOreo = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
         String stringContentURI;
         Uri contentUri;
         if(isOreo){ 
            stringContentURI = "content://downloads/my_downloads";
         }else{
            stringContentURI = "content://downloads/public_downloads";
         }
         contentUri = ContentUris.withAppendedId(
                    Uri.parse(stringContentURI), Long.valueOf(id));
         return getDataColumn(context, contentUri, null, null);
      } catch (NumberFormatException e) {
         return null;
      }
   }
}

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 index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

ただし、Androidデバイス内の他のフォルダーからファイルを選択するときに機能します

お知らせ下さい。みんな、ありがとう :)

6
ryosu

現時点では、パスを取得するための最良の方法は次のとおりです。

URIから物理ファイルをInputStreamとして取得すると、ContentResolver.openInputStream()を使用すると、実際のパスを知らなくてもファイルの内容にアクセスできます。

String id = DocumentsContract.getDocumentId(uri);
InputStream inputStream = getContentResolver().openInputStream(uri);

次に、それを一時ファイルとしてキャッシュストレージに書き込みます

File file = new File(getCacheDir().getAbsolutePath()+"/"+id);
writeFile(inputStream, file);
String filePath = file.getAbsolutePath();

一時ファイルをキャッシュされたストレージに書き込む方法は次のとおりです

void writeFile(InputStream in, File file) {
     OutputStream out = null;
     try {
          out = new FileOutputStream(file);
          byte[] buf = new byte[1024];
          int len;
          while((len=in.read(buf))>0){
            out.write(buf,0,len);
          }
     } catch (Exception e) {
          e.printStackTrace();
     }
     finally {
          try {
            if ( out != null ) {
                 out.close();
            }
            in.close();
          } catch ( IOException e ) {
               e.printStackTrace();
          }
     }
}

それが最善の方法であるかどうかはわかりませんが、コードは正しく機能しています:D

6
ryosu

これが、この要旨から見つけた別の解決策です。 FileUtils.Java

使い方は次のとおりです。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Uri uri = data.getData();
    File originalFile = new File(FileUtils.getRealPath(this,uri));
}
0
NIMROD MAINA

これは私が思いついたものです(Android Pieでテストされており、SDカードを想定しています):

private String getParentDirectory(@NonNull Uri uri) {
    String uriPath = uri.getPath();
    String filePath = Environment.getExternalStorageDirectory().getAbsolutePath();
    if(uriPath != null) {filePath = new File(filePath.concat("/" + uriPath.split(":")[1])).getParent();}
    return filePath;
}

private String getAbsolutePath(@NonNull Uri uri) {
    String uriPath = uri.getPath();
    String filePath = Environment.getExternalStorageDirectory().getAbsolutePath();
    if(uriPath != null) {filePath = filePath.concat("/" + uriPath.split(":")[1]);}
    return filePath;
}
0
Martin Zeitler