安卓如何通过Uri.getPath()获取真实路径

119

我正在尝试从相册获取图片。

Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select picture"), resultCode );

我从这个活动返回后,有一个包含Uri的数据。它看起来像这样:

content://media/external/images/1

如何将此路径转换为实际路径(就像 '/sdcard/image.png' 一样)?

谢谢


1
这显然是一个非常晚的评论,但我只想指出startActivityForResultCode方法需要一个请求代码作为参数,而不是结果代码。 - Tianxiang Xiong
uri.getPath() 没有给出真实路径吗? - Darpan
#试试这个 如果您仍然无法获取真实路径的问题,可以尝试我的答案。上面的答案没有帮助我。 解释:此方法获取URI,然后检查Android设备的API级别,根据API级别生成真实路径。 根据API级别生成真实路径的代码是不同的。 这是我的答案 - Sunil
9个回答

207

这就是我的做法:

Uri selectedImageURI = data.getData();
imageFile = new File(getRealPathFromURI(selectedImageURI));

并且:

private String getRealPathFromURI(Uri contentURI) {
    String result;
    Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
    if (cursor == null) { // Source is Dropbox or other similar local file path
        result = contentURI.getPath();
    } else { 
        cursor.moveToFirst(); 
        int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); 
        result = cursor.getString(idx);
        cursor.close();
    }
    return result;
}

注意: managedQuery() 方法已被弃用,因此我不使用它。

最后编辑:改进。我们应该关闭游标(cursor)!!


6
嗨 @m3n0R,在Moto G上,MediaStore.Images.ImageColumns.DATA列根本不存在。如何在没有此列的情况下获取图像? - seema
25
在访问光标数据之前,请确保正确初始化了光标。检查 API 19。 - Mayur R. Amipara
5
在从CursorWindow访问数据之前,请确保正确初始化Cursor,否则无法读取第0行,第-1列的数据。 - Maveňツ
5
崩溃:java.lang.IllegalStateException: 无法从CursorWindow读取第0行,第-1列。在访问数据之前,请确保正确初始化Cursor。 - hkchakladar
5
MediaStore.Images.ImageColumns.DATA 已经过时。 - valerybodak
显示剩余11条评论

58

你真的需要获取物理路径吗?
例如,ImageView.setImageURI()ContentResolver.openInputStream()可以让你在不知道其真实路径的情况下访问文件内容。


这正是我一直在寻找但却找不到的东西。谢谢。 - davs
9
抱歉,如果我想将这个图片转换为文件,但不获取真实路径,该怎么做? - Chlebta
12
物理路径可访问文件名和扩展名。在文件上传的情况下。 - Clocker
1
如何存储到数据库中? - ruif3r

28

@Rene Juuse - 在评论中提供的链接非常感谢!

由于不同SDK的代码获取真实路径有所不同,因此以下有三种处理不同SDK的方法。

getRealPathFromURI_API19():返回API 19(或以上版本但未经测试)的真实路径 getRealPathFromURI_API11to18():返回API 11到API 18的真实路径 getRealPathFromURI_below11():返回低于API 11的真实路径

public class RealPathUtil {

@SuppressLint("NewApi")
public static String getRealPathFromURI_API19(Context context, Uri uri){
    String filePath = "";
    String wholeID = DocumentsContract.getDocumentId(uri);

     // Split at colon, use second item in the array
     String id = wholeID.split(":")[1];

     String[] column = { MediaStore.Images.Media.DATA };     

     // where id is equal to             
     String sel = MediaStore.Images.Media._ID + "=?";

     Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
                               column, sel, new String[]{ id }, null);

     int columnIndex = cursor.getColumnIndex(column[0]);

     if (cursor.moveToFirst()) {
         filePath = cursor.getString(columnIndex);
     }   
     cursor.close();
     return filePath;
}


@SuppressLint("NewApi")
public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) {
      String[] proj = { MediaStore.Images.Media.DATA };
      String result = null;

      CursorLoader cursorLoader = new CursorLoader(
              context, 
        contentUri, proj, 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);
      }
      return result;  
}

public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri){
           String[] proj = { MediaStore.Images.Media.DATA };
           Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
           int column_index
      = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
           cursor.moveToFirst();
           return cursor.getString(column_index);
}

字体:http://hmkcode.com/android-display-selected-image-and-its-real-path/


2016年3月更新

为了解决所有有关图像路径的问题,我尝试创建一个自定义图库,就像Facebook和其他应用程序一样。这是因为您只能使用本地文件(真实文件,而不是虚拟或临时文件),我使用了这个库来解决所有问题。

https://github.com/nohana/Laevatein(此库用于从相机拍摄照片或从图库选择照片,如果您从图库中选择,则会显示一个带有相册的抽屉并仅显示本地文件)


@Clocker 在三星S4上也不行。 - Ricardo
在Android中管理所有类型的源图像确实很困难。如果您上传或从图库中选择,最好的解决方案是从Facebook创建一个相册。这是一个自定义相册,只有设备上的真实图像,没有虚拟或临时图像。我使用这个库解决了我的应用程序中的所有问题。https://github.com/nohana/Laevatein 这是一个非常好的库。要自定义不简单,但您可以打开代码并进行更改。我希望这可以帮助到您。 - luizfelipetx
在我的三星S2上出现了故障,系统版本为4.4.1。 - Oliver Dixon
2
太棒了!终于有真正好用的东西了。我已经寻找很久了。谢谢。 - Javatar
将以下代码添加到类中: public static String getRealPath(Context context, Uri uri){ int versionCode = 0; try { versionCode = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } if(versionCode >= Build.VERSION_CODES.KITKAT){ return getRealPathFromURI_API19(context, uri); }else{ return getRealPathFromURI_API11to18(context, uri); }} - Moti
显示剩余3条评论

18
注意:这是 @user3516549 的回答中的改进,我已在安卓 6.0.1 的 Moto G3 上测试过了。
我遇到了同样的问题,尝试了 @user3516549 的回答,但在某些情况下无法正常工作。 我发现在安卓 6.0(或更高版本)中,当我们启动相册图片选择意图时,会打开一个显示最近图片的屏幕,当用户从此列表中选择图片时,我们将得到以下 uri:
content://com.android.providers.media.documents/document/image%3A52530

如果用户从滑动抽屉中选择图库而不是最近的照片,则我们将获得以下URI:

如果用户从侧边栏抽屉选择画廊而不是最近,则我们将获取URI。

content://media/external/images/media/52530

所以我需要在getRealPathFromURI_API19()中处理它。

public static String getRealPathFromURI_API19(Context context, Uri uri) {
    String filePath = "";
    if (uri.getHost().contains("com.android.providers.media")) {
        // Image pick from recent 
        String wholeID = DocumentsContract.getDocumentId(uri);

        // Split at colon, use second item in the array
        String id = wholeID.split(":")[1];

        String[] column = {MediaStore.Images.Media.DATA};

        // where id is equal to
        String sel = MediaStore.Images.Media._ID + "=?";

        Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                column, sel, new String[]{id}, null);

        int columnIndex = cursor.getColumnIndex(column[0]);

        if (cursor.moveToFirst()) {
            filePath = cursor.getString(columnIndex);
        }
        cursor.close();
        return filePath;
    } else {
        // image pick from gallery 
       return  getRealPathFromURI_BelowAPI11(context,uri)
    }
}

编辑1: 如果您正在尝试获取外部SD卡中文件的图像路径且版本较高,请查看我的问题

编辑2: 这里是完整的代码,可处理虚拟文件并主机不同于com.android.providers。我已经测试过这种方法与content://com.adobe.scan.android.documents/document/一起使用


嗨,朋友,是的,这是真的。但这是因为现在谷歌有一个默认格式,可以将您手机中的所有照片上传到谷歌文档中,并只在您的手机上保存缩略图。如果您从谷歌文档获取此URI,则需要在使用之前下载照片。 总的来说,这不是一个好的解决方案。因此,为了解决所有问题,我现在正在使用这个库。(这个库只使用本地文件,并且通过这个解决方案,您的问题将得到解决),或者您可以从库中提取代码并改进以解决您的问题。 https://github.com/nohana/Laevatein 我希望这可以帮助到您。 - luizfelipetx
1+,它对我来说完美地工作。正如@JaiprakasSoni在他的答案中所说,当我在Moto G4 play上运行我的应用程序时,我遇到了同样的问题,但是当我使用上面的代码时,它对我来说很好。谢谢你。你节省了我的时间。 - Farmer

11

编辑: 请使用此解决方案:https://dev59.com/YmIj5IYBdhLWcg3wnmXA#20559175。完美运作!

首先,感谢您的解决方案 @luizfelipetx。

我稍微修改了您的解决方案。这对我有效:

public static String getRealPathFromDocumentUri(Context context, Uri uri){
    String filePath = "";

    Pattern p = Pattern.compile("(\\d+)$");
    Matcher m = p.matcher(uri.toString());
    if (!m.find()) {
        Log.e(ImageConverter.class.getSimpleName(), "ID for requested image not found: " + uri.toString());
        return filePath;
    }
    String imgId = m.group();

    String[] column = { MediaStore.Images.Media.DATA };
    String sel = MediaStore.Images.Media._ID + "=?";

    Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            column, sel, new String[]{ imgId }, null);

    int columnIndex = cursor.getColumnIndex(column[0]);

    if (cursor.moveToFirst()) {
        filePath = cursor.getString(columnIndex);
    }
    cursor.close();

    return filePath;
}
注意:我们拥有文件和图像,具体取决于图像来自“最近”,“相册”或其他地方。因此,在查找它之前,我首先提取图像ID。

注意:我们拥有文件和图像,具体取决于图像来自“最近”,“相册”或其他地方。因此,在查找之前,我首先提取图像ID。


9

将文件复制到真实路径并获取它们的路径是一种简单且最佳的方法,我在Android API-16到API-30上检查了10个设备,工作正常。

@Nullable
public static String createCopyAndReturnRealPath(
       @NonNull Context context, @NonNull Uri uri) {
    final ContentResolver contentResolver = context.getContentResolver();
    if (contentResolver == null)
        return null;

    // Create file path inside app's data dir
    String filePath = context.getApplicationInfo().dataDir + File.separator + "temp_file";
    File file = new File(filePath);
    try {
        InputStream inputStream = contentResolver.openInputStream(uri);
        if (inputStream == null)
            return null;
        OutputStream outputStream = new FileOutputStream(file);
        byte[] buf = new byte[1024];
        int len;
        while ((len = inputStream.read(buf)) > 0)
            outputStream.write(buf, 0, len);
        outputStream.close();
        inputStream.close();
    } catch (IOException ignore) {
        return null;
    }
    return file.getAbsolutePath();
}

2
目前最优秀的URI转路径工具。对于大文件可能有些麻烦,但对于小文件效果非常好。 - yuroyami
1
这绝对是最好的解决方案。非常适合我的使用情况,我需要获取一个小文件的路径。无需使用文件选择库或其他复杂的解决方案来获取真实路径。最好的部分是,它在我所有的设备上都可以使用。感谢分享。 - DIRTY DAVE
由于某些原因,当我使用它时,文件未在应用程序文件夹中创建,然后我收到了“文件不存在”的错误。 - Yakir Malka
@YakirMalka创建了一个名为temp_file的文件,但没有像jpg、png等扩展名。 - Attaullah
昨天我已经想通了 :) 谢谢! - Yakir Malka

1

1
Hii这里是我从相机或图库获取图片的完整代码
//我的变量声明
protected static final int CAMERA_REQUEST = 0;
    protected static final int GALLERY_REQUEST = 1;
    Bitmap bitmap;
    Uri uri;
    Intent picIntent = null;

//点击事件

if (v.getId()==R.id.image_id){
            startDilog();
        }

//方法体

private void startDilog() {
    AlertDialog.Builder myAlertDilog = new AlertDialog.Builder(yourActivity.this);
    myAlertDilog.setTitle("Upload picture option..");
    myAlertDilog.setMessage("Where to upload picture????");
    myAlertDilog.setPositiveButton("Gallery", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            picIntent = new Intent(Intent.ACTION_GET_CONTENT,null);
            picIntent.setType("image/*");
            picIntent.putExtra("return_data",true);
            startActivityForResult(picIntent,GALLERY_REQUEST);
        }
    });
    myAlertDilog.setNegativeButton("Camera", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            picIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(picIntent,CAMERA_REQUEST);
        }
    });
    myAlertDilog.show();
}

//和其他事情一起

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode==GALLERY_REQUEST){
        if (resultCode==RESULT_OK){
            if (data!=null) {
                uri = data.getData();
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                try {
                    BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
                    options.inSampleSize = calculateInSampleSize(options, 100, 100);
                    options.inJustDecodeBounds = false;
                    Bitmap image = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
                    imageofpic.setImageBitmap(image);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }else {
                Toast.makeText(getApplicationContext(), "Cancelled",
                        Toast.LENGTH_SHORT).show();
            }
        }else if (resultCode == RESULT_CANCELED) {
            Toast.makeText(getApplicationContext(), "Cancelled",
                    Toast.LENGTH_SHORT).show();
        }
    }else if (requestCode == CAMERA_REQUEST) {
        if (resultCode == RESULT_OK) {
            if (data.hasExtra("data")) {
                bitmap = (Bitmap) data.getExtras().get("data");
                uri = getImageUri(YourActivity.this,bitmap);
                File finalFile = new File(getRealPathFromUri(uri));
                imageofpic.setImageBitmap(bitmap);
            } else if (data.getExtras() == null) {

                Toast.makeText(getApplicationContext(),
                        "No extras to retrieve!", Toast.LENGTH_SHORT)
                        .show();

                BitmapDrawable thumbnail = new BitmapDrawable(
                        getResources(), data.getData().getPath());
                pet_pic.setImageDrawable(thumbnail);

            }

        } else if (resultCode == RESULT_CANCELED) {
            Toast.makeText(getApplicationContext(), "Cancelled",
                    Toast.LENGTH_SHORT).show();
        }
    }
}

private String getRealPathFromUri(Uri tempUri) {
    Cursor cursor = null;
    try {
        String[] proj = { MediaStore.Images.Media.DATA };
        cursor = this.getContentResolver().query(tempUri,  proj, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}
public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

private Uri getImageUri(YourActivity youractivity, Bitmap bitmap) {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
    String path = MediaStore.Images.Media.insertImage(youractivity.getContentResolver(), bitmap, "Title", null);
    return Uri.parse(path);
}

你如何知道这行代码中的100是什么意思:options.inSampleSize = calculateInSampleSize(options, 100, 100); - John Joe

1
这段代码在安卓11和12版本上对我有效。
private static String getRealPathFromURI(Uri uri, Context context) {
                Uri returnUri = uri;
                Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
                int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
                returnCursor.moveToFirst();
                String name = (returnCursor.getString(nameIndex));
                String size = (Long.toString(returnCursor.getLong(sizeIndex)));
                File file = new File(context.getFilesDir(), name);
                try {
                    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 = 1024;
                    int bufferSize = Math.min(bytesAvailable, maxBufferSize);
        
                    final byte[] buffers = new byte[bufferSize];
                    while ((read = inputStream.read(buffers)) != -1) {
                        outputStream.write(buffers, 0, read);
                    }
                    Log.e("File Size", "Size " + file.length());
                    inputStream.close();
                    outputStream.close();
                    Log.e("File Path", "Path " + file.getPath());
                    Log.e("File Size", "Size " + file.length());
                } catch (Exception e) {
                    Log.e("Exception", e.getMessage());
                }
                return file.getPath();
            }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接