Android obtiene una ruta real por Uri.getPath ()

107

Estoy tratando de obtener una imagen de la galería.

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

Después de regresar de esta actividad, tengo un dato que contiene Uri. Parece que:

content://media/external/images/1

¿Cómo puedo convertir esta ruta en una real (como ' /sdcard/image.png')?

Gracias

davs
fuente
1
Obviamente, este es un comentario muy tardío, pero solo quería señalar que el método startActivityForResultCode toma un código de solicitud como argumento, no como código de resultado.
Tianxiang Xiong
¿ uri.getPath()No te dio el camino real?
Darpan
# Pruebe esto Aún así, si tiene el problema de obtener el camino real, puede probar mis respuestas. Las respuestas anteriores no me ayudaron. Explicación : - Este método obtiene el URI y luego verifica el nivel de API de su dispositivo Android y luego, de acuerdo con el nivel de API, generará la ruta real. El código para generar una ruta real es diferente según los niveles de API. Aquí está mi respuesta
Sunil

Respuestas:

56

¿Es realmente necesario que obtengas un camino físico?
Por ejemplo, ImageView.setImageURI()y ContentResolver.openInputStream()permitirle acceder al contenido de un archivo sin conocer su ruta real.

molnarm
fuente
Es exactamente lo que busqué, pero no pude encontrar. Gracias.
davs
6
Lo siento y si quiero convertir esta imagen en un archivo, sin obtener la ruta real, ¿cómo hacerlo?
Chlebta
6
La ruta física da acceso al nombre del archivo y la extensión. En caso de cargas de archivos.
Clocker
195

Esto es lo que hago:

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

y:

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

NOTA: el managedQuery()método está obsoleto, por lo que no lo estoy usando.

Última edición: Mejora. ¡¡Deberíamos cerrar el cursor !!

cesar
fuente
1
stackoverflow.com/a/7265235/375093 Esta es otra opción, pero sugerida como mejor que la anterior. Mira esto también
Domingo
6
Hola @ m3n0R, En Moto G, la columna MediaStore.Images.ImageColumns.DATA no existe en absoluto. ¿Cómo obtener una imagen sin esta columna?
seema
16
ERROR Asegúrese de que el Cursor se haya inicializado correctamente antes de acceder a los datos desde él.
Verificando
1
@ReneJuuse: Excelente recurso. Finalmente logré resolver este problema gracias a eso; no me di cuenta de que había tantos cambios desde el inicio de Android. Estaba ejecutando API 21 y cuando usé la implementación de API 19 que se muestra en el sitio que vinculó, finalmente funcionó.
Milos Ivanovic
2
No se pudo leer la fila 0, col -1 de CursorWindow. Asegúrese de que el Cursor esté inicializado correctamente antes de acceder a sus datos.
Maveň ツ
18

@Rene Juuse - arriba en los comentarios ... ¡Gracias por este enlace!

. el código para obtener la ruta real es un poco diferente de un SDK a otro, por lo que a continuación tenemos tres métodos que tratan con diferentes SDK.

getRealPathFromURI_API19 (): devuelve la ruta real para API 19 (o superior pero no probada) getRealPathFromURI_API11to18 (): devuelve la ruta real para API 11 a API 18 getRealPathFromURI_below11 (): devuelve ruta real para API por debajo de 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);
}

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


ACTUALIZACIÓN 2016 marzo

Para solucionar todos los problemas con la ruta de las imágenes, intento crear una galería personalizada como Facebook y otras aplicaciones. Esto se debe a que puede usar solo archivos locales (archivos reales, no virtuales o temporales), resuelvo todos los problemas con esta biblioteca.

https://github.com/nohana/Laevatein (esta biblioteca es para tomar fotos de la cámara o elegir de la galería, si eliges de la galería, tiene un cajón con álbumes y solo muestra archivos locales)

luizfelipetx
fuente
@Clocker en Samsung S4 tampoco
Ricardo
Realmente soy difícil administrar todo tipo de imágenes de origen en Android. Si subes o tomas eliges de la galería, la mejor solución es crearla como una galería de facebook. Es una galería personalizada con solo imágenes reales en el dispositivo, no virtuales ni temporales. Soluciono todos los problemas en mi aplicación usando esta biblioteca. github.com/nohana/Laevatein Es una biblioteca realmente buena. Personalizar no es simple, pero puede abrir el código y realizar los cambios. Espero que esto pueda ayudarte.
luizfelipetx
Roto en mi Samsung S2 con 4.4.1
Oliver Dixon
Necesito obtener los archivos como pdf o doc, no puedo obtener la ruta. ¿Podrías ayudarme?
Anish Kumar
1
¡Increíble! Finalmente algo que realmente funciona. Estuve buscando esto durante mucho tiempo. Gracias.
Javatar
14

Nota: Esta es una mejora en la respuesta de @ user3516549 y la he verificado en Moto G3 con Android 6.0.1
Tengo este problema, así que he intentado la respuesta de @ user3516549 pero en algunos casos no funcionaba correctamente. Descubrí que en Android 6.0 (o superior) cuando comenzamos la intención de selección de imágenes de la galería, se abrirá una pantalla que muestra imágenes recientes cuando el usuario seleccione una imagen de esta lista, obtendremos uri como

content://com.android.providers.media.documents/document/image%3A52530

mientras que si el usuario selecciona la galería del cajón deslizante en lugar de reciente, obtendremos uri como

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

Así que lo he manejado en 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)
        }

    }

EDITAR: si está intentando obtener la ruta de la imagen del archivo en una tarjeta sd externa en una versión superior, consulte mi pregunta

Jaiprakash Soni
fuente
Hola amigo, sí, eso es cierto. pero esto sucede porque ahora Google tiene un formato predeterminado para cargar todas las fotos desde su teléfono a los documentos de Google. Y simplemente guarde thumbinal en su teléfono. Si obtiene este uri de los documentos de Google, debe descargar la foto antes de usarlo. Esto no es bueno en general. Entonces, para solucionar todos los problemas aquí, ahora estoy usando esta biblioteca. (esta biblioteca solo usa archivos locales, y con esta solución se resolverán sus problemas), o puede extraer el código de la biblioteca y mejorar usted mismo para atender su problema. github.com/nohana/Laevatein Espero que esto pueda ayudarte.
luizfelipetx
1+, su trabajo es perfecto para mí. Como dijo @JaiprakasSoni en su respuesta, me he enfrentado al mismo problema cuando ejecuto mi aplicación en Moto G4 Play, pero cuando uso el código anterior, funciona bien para mí. Gracias. Me ahorras tiempo
Shailesh
6

EDITAR: Utilice esta solución aquí: https://stackoverflow.com/a/20559175/2033223 ¡ Funciona perfecto!

En primer lugar, gracias por tu solución @luizfelipetx

Cambié un poco tu solución. Esto funciona para mi:

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

Nota: Entonces obtuvimos documentos e imágenes, dependiendo de si la imagen proviene de 'recientes', 'galería' o lo que sea. Entonces extraigo la ID de la imagen primero antes de buscarla.

Javatar
fuente
1

Hola, aquí está mi código completo para tomar imágenes de la cámara o galeery

// Mi declaración de variable

protected static final int CAMERA_REQUEST = 0;
    protected static final int GALLERY_REQUEST = 1;
    Bitmap bitmap;
    Uri uri;
    Intent picIntent = null;

//Al hacer clic

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

// cuerpo del método

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

// Y resto de cosas

@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);
}
Tanmay Sahoo
fuente
Cómo saber es 100 en esta línea options.inSampleSize = calculateInSampleSize(options, 100, 100);
John Joe