Как получить путь к файлу из URI в Android Oreo (8.1) или выше

Ожидаемое поведение

Когда я выбираю файл, который хранится внутри «Загрузки», он должен получить возможность получить имя и путь к файлу.

Фактическое поведение

Когда я выбираю файл, который хранится внутри «Download», он возвращает null.

Шаги по воспроизведению проблемы

  1. Когда вызывается метод выбора файла, он отображает папки в Android
  2. Перейти к загрузкам, выбрать файл
  3. Возвращает null при получении реального пути из URI

Here is the code what i implemented

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.

Пожалуйста, порекомендуйте. Спасибо всем :)

10
0
9 490
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

На данный момент лучший способ получить путь:

Получение физического файла из 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();

Here is the method to write temporary file into cached storage

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

Но он по-прежнему не дает вам реального пути к файлу.

Kirill Karmazin 08.02.2019 13:42

Это спасительное решение. Спасибо. Но мне все еще интересно, почему ребята из Google не могут сделать это простым. Я думаю, что просто получить путь к файлу - это большая работа.

Gordon developer 25.02.2019 09:54

@ryosu просто оставил ответ, который менее сложен - и возвращает фактический путь.

Martin Zeitler 11.04.2019 19:48

@ryosu Он работает нормально до Android N, но в Android P он не работает при выборе изображений из папки «Загрузки» и сбоях. Можете ли вы предложить какой-либо другой способ.

Anupriya 06.10.2020 09:28

Вот еще одно решение, которое я нашел из этой сути. 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));
}

Я не знаю, зачем давать «ответ неполный» для этого ответа, но это полезно для меня, спасибо @ nimrod-mania

abbasalim 06.08.2019 07:07

Не работает в Android 30

Arun PK 27.03.2021 05:35

Это то, что я только что придумал (он протестирован на 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;
}

this is activity instance variable

*************************************************************************
*************************************************************************  

    Uri filePath;
    String strAttachmentFileName = "",//attached file name
            strAttachmentCoded = "";//attached file in byte code Base64
    int PICK_REQUEST =1;
*************************************************************************
*************************************************************************  

this is in activity method

*************************************************************************
*************************************************************************  

Button buttonChoose.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setType("file/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent, "Select any file"), PICK_REQUEST );
            }
        });
*************************************************************************
*************************************************************************  

this is overrride activity method

*************************************************************************
*************************************************************************



    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PICK_REQUEST && resultCode == Activity.RESULT_OK && data != null && data.getData() != null) {
            filePath = data.getData();
            File uploadFile = new File(FileUtils.getRealPath(activity.this, filePath));
            try {
                if (uploadFile != null) {
                    strAttachmentFileName = uploadFile.getName();
                    FileInputStream objFileIS = new FileInputStream(uploadFile);
                    ByteArrayOutputStream objByteArrayOS = new ByteArrayOutputStream();
                    byte[] byteBufferString = new byte[1024];
                    int readNum;
                    readNum = objFileIS.read(byteBufferString);
                    while (readNum != -1) {
                        objByteArrayOS.write(byteBufferString, 0, readNum);
                        readNum = objFileIS.read(byteBufferString);
                    }
                    strAttachmentCoded  = Base64.encodeToString(objByteArrayOS.toByteArray(), Base64.DEFAULT);

                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

*************************************************************************
*************************************************************************  

Please create file FileUtils.java as below

ссылка HBiSoft



Для любого типа файла в .Net ---
byte[] p = Convert.FromBase64String("byte string");

MemoryStream ms = new MemoryStream(p);
FileStream fs = new FileStream
        (System.Web.Hosting.HostingEnvironment.MapPath("~/ComplaintDetailsFile/") +
                item.FileName, FileMode.Create);
ms.WriteTo(fs);
ms.Close();
fs.Close();
fs.Dispose();

идеальное решение

Ashish Choudhary 06.03.2020 06:31

Ниже будет полезен API, нам нужно создать файл в кеше нашего приложения и вернуть тот же путь из-за ограничений, то есть Ограниченное хранилище.

fun createFileAndGetPathFromCacheFolder(context: Context, uri: Uri): String? {
    var inputStream: FileInputStream? = null
    var outputStream: FileOutputStream? = null
    try {
        val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, "r", null)
        inputStream = FileInputStream(parcelFileDescriptor?.fileDescriptor)
        val file = File(context.cacheDir, context.contentResolver.getFileName(uri))
        outputStream = FileOutputStream(file)
        val buffer = ByteArray(1024)
        var len: Int
        while (inputStream.read(buffer).also { len = it } != -1) {
            outputStream.write(buffer, 0, len)
        }
        return file.absolutePath
    } catch (e: Exception) {

    } finally {
        inputStream?.close()
        outputStream?.close()
    }
    return " "
}

Другие вопросы по теме