如何从 Android Oreo (8.1) 或更高版本的 URI 获取文件路径

How to Get File Path from URI in Android Oreo (8.1) or above

预期行为

当我select读取存储在"Download"中的文件时,它应该能够检索其文件名和路径

实际行为

当我 select 访问存储在 "Download" 中的文件时,它 returns 为空。

重现问题的步骤

  1. 调用picking file方法时,显示Android
  2. 中的文件夹
  3. 去下载,select一个文件
  4. 从 URI 获取真实路径returns null

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

但是,当 select 从 Android 设备中的其他文件夹中读取文件时,它可以正常工作

请指教。谢谢大家:)

目前,获取路径的最佳方法是:

从 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

这是我从这个要点中找到的另一个解决方案。 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));
}

这是我刚刚想出的(它在 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

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

下面 API 将 useful.We 需要在我们的应用程序缓存中创建文件,并且 return 相同的路径因为限制,即 Scoped 存储。

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