Android 4.4'teki Android Galerisi (KitKat), Niyet için farklı URI döndürür.ACTION_GET_CONTENT


214

KitKat'tan önce (veya yeni Galeri'den önce) Intent.ACTION_GET_CONTENTböyle bir URI döndürdü

İçerik: // medya / harici / images / media / 3951.

İçin ContentResolverve sorgularını kullanmak MediaStore.Images.Media.DATAdosya URL'sini döndürdü.

Ancak KitKat'ta Galeri şu şekilde bir URI döndürür ("Son" aracılığıyla):

İçerik: //com.android.providers.media.documents/document/image: 3951

Bunu nasıl halledebilirim?


21
Manşetten, dosyaya doğrudan erişim gerektirmeyen içeriği kullanmanın yollarını bulurdum. Örneğin, Uriüzerinden akış olarak açılabilir olmalıdır ContentResolver. Uzun zamandır content:// Uribir dosyayı temsil eden bir dosyanın her zaman bir dönüştürülebileceğini varsayan uygulamalar konusunda endişeliyim File.
CommonsWare

1
@ CommonsWare, Daha sonra açabilmek için bir resim yolunu sqlite db'ye kaydetmek isterseniz, URI veya mutlak dosya yolunu kaydetmeli miyim?
Yılan

2
@CommonsWare Gerginliğinize katılıyorum. :-) Ancak, yerel kod için bir dosya adı (bir görüntü için) geçmek gerekir. Bir çözüm, InputStreamon kullanılarak elde edilen verilerin ContentResolverönceden belirlenmiş bir yere kopyalanması ve böylece bilinen bir dosya adına sahip olmasıdır. Ancak, bu benim için israfa benziyor. Başka öneriniz var mı?
darrenp

2
@darrenp: Ummm ..., bir InputStreamJNI üzerinde çalışmak için yerel kodu yeniden yaz ? Ne yazık ki, sizin için o kadar çok seçenek yok.
CommonsWare

1
Bunu bilmek faydalı. Yanıtınız için teşekkürler. O zamandan beri artık görüntüyü bir dosya yerine bellekte C ++ 'a geçirdiğini öğrendim, böylece artık InputStreambir dosya yerine (ki harika) kullanabiliyoruz. Sadece EXIF ​​etiket okuması biraz zor ve Drew Noakes kütüphanesini gerektiriyor . Yorumlarınız için çok teşekkürler.
darrenp

Yanıtlar:


108

Bunu dene:

if (Build.VERSION.SDK_INT <19){
    Intent intent = new Intent(); 
    intent.setType("image/jpeg");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.select_picture)),GALLERY_INTENT_CALLED);
} else {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("image/jpeg");
    startActivityForResult(intent, GALLERY_KITKAT_INTENT_CALLED);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) return;
    if (null == data) return;
    Uri originalUri = null;
    if (requestCode == GALLERY_INTENT_CALLED) {
        originalUri = data.getData();
    } else if (requestCode == GALLERY_KITKAT_INTENT_CALLED) {
        originalUri = data.getData();
        final int takeFlags = data.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        // Check for the freshest data.
        getContentResolver().takePersistableUriPermission(originalUri, takeFlags);
    }

    loadSomeStreamAsynkTask(originalUri);

}

Muhtemelen ihtiyaç

@SuppressLint ( "NewApi")

için

takePersistableUriPermission


1
KitKat kodunun ne yaptığını açıklamak ister misiniz? Bu yeni bir izin gerektiriyor mu? KitKat öncesi kodu KitKat'ta da benim için çalışıyor. Öyleyse neden KitKat için farklı bir kod kullanmayı seçeyim? Teşekkürler.
Michael Greifeneder

67
yeni sdks uri'den yol alamıyoruz gibi görünüyor. Ayrıca google bu tür bir değişiklik uygun belge ve duyuru olmadan yapılan bir utanç.
user65721

1
Dosya URL'sini nasıl alacağınızı açıklayabilir misiniz? SDCard'da gerçek yolu elde etmek istiyorum. Örneğin, bir resim ise, Uri belgesi yerine /storage/sdcard0/DCIM/Camera/IMG_20131118_153817_119.jpg yolunu elde etmek istiyorum.
Álvaro

4
KitKat belgelerine ( developer.android.com/about/versions/… ) dayanarak , diğer uygulamaların sahip olduğu belgeleri kullanma / düzenleme niyetinde olmadıkça OP'nin ihtiyaç duyduğu şey bu olmayabilir. Eğer OP daha eski versiyonlarla tutarlı bir şekilde bir kopyasını yapmak ya da bir şeyler yapmak isterse, @voytez'in cevabı daha uygun olacaktır.
Colin M.

8
Bu benim için çalışmıyor. Aşağıdaki özel durumu alıyorum (stok 4.4.2): E / AndroidRuntime (29204): Neden: java.lang.SecurityException: İstenen bayraklar 0x1, ancak yalnızca 0x0'a izin verilir
Russell Stewart

177

Bu özel bir izin gerektirmez ve Depolama Erişimi Çerçevesi'nin yanı sıra gayri resmi ContentProviderkalıpla ( _dataalandaki dosya yolu ) çalışır.

/**
 * Get a file path from a Uri. This will get the the path for Storage Access
 * Framework Documents, as well as the _data field for the MediaStore and
 * other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @author paulburke
 */
public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }

            // TODO handle non-primary volumes
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] {
                    split[1]
            };

            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @param selection (Optional) Filter used in the query.
 * @param selectionArgs (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
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;
}


/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}

Bu yöntemin güncel bir sürümüne buradan bakın .


2
Bu, standart galeri uygulamalarını kullanarak 4.4 Nexus 5 Documents UI ve diğer bazı tercih KitKat cihazlarında fevkalade çalıştı, teşekkürler Paul!
Josh

1
Bunun için teşekkürler, sdk 19 ile bu kadar almak için yaşlarımı aldı! Sorunum, cihazımın Google sürücüsünü dosya tarayıcısı olarak kullanması. Dosya aygıt üzerindeyse, görüntü yolu iyi durumdadır, ancak dosya sürücüde ise açılmaz. Belki de sadece Google Drive'dan görüntü açma ile ilgilenmem gerekiyor. Şey benim app dosya yolu kullanmak ve örnekleme kullanarak görüntü almak için yazılmıştır ...
RuAware

2
@RuAware Bir Drive dosyası seçtiğinizde, dosya geri verir Authority: com.google.android.apps.docs.storageve Segments: [document, acc=1;doc=667]. Emin değilim, ancak docdeğerin Urisorgulayabileceğiniz kimlik olduğunu varsayalım . Muhtemelen şu adresteki "Uygulamanızı Android'de yetkilendirme" bölümünde ayrıntılı olarak ayarlanması için izinlere ihtiyacınız olacaktır: developers.google.com/drive/integrate-android-ui . Bunu anlarsanız lütfen buradan güncelleyin.
Paul Burke

30
bu kesinlikle korkunç! böyle "hileler" kod yaymaya devam etmemelisiniz. yalnızca modelini bildiğiniz kaynak uygulamaları destekler ve belge sağlayıcı modelinin tüm amacı keyfi kaynakları desteklemektir
j__m

2
_dataContentProvider bunu desteklemiyorsa zaman işe yaramaz. @CommonsWare talimatlarını izlemeniz ve artık tam dosya yolunu kullanmamanız tavsiye edilir , çünkü gerçek bir dosya yerine Dropbox bulutunda bir dosya olabilir.
soshial

67

Aynı sorun vardı, yukarıdaki çözümü denedim ama genel olarak çalıştı, ancak bir nedenden dolayı bazı görüntüler için Uri içerik sağlayıcısı üzerinde izin reddi alıyordum. android.permission.MANAGE_DOCUMENTS izin düzgün eklenmiş .

Her neyse, KITKAT belgeleri görünümü yerine resim galerisini açmaya zorlayan başka bir çözüm buldu:

// KITKAT

i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(i, CHOOSE_IMAGE_REQUEST);

ve sonra görüntüyü yükleyin:

Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
                BitmapFactory.decodeStream(input , null, opts);

DÜZENLE

ACTION_OPEN_DOCUMENT izin bayraklarını vb. saklamanızı gerektirebilir ve genellikle Güvenlik İstisnaları ile sonuçlanır ...

Diğer çözüm, hem KK öncesi hem de KK üzerinde çalışacak olan ACTION_GET_CONTENTkombine kullanmaktır c.getContentResolver().openInputStream(selectedImageURI). Kitkat o zaman yeni belgeler görünümünü kullanacak ve bu çözüm Fotoğraflar, Galeri, Dosya Gezgini, Dropbox, Google Drive vb. Gibi tüm uygulamalarla çalışacaktır ... ancak bu çözümü kullanırken, içinde resim oluşturup onActivityResult()saklamanız gerektiğini unutmayın. Örneğin SD Kart. Bir sonraki uygulama başlangıcında bu görüntüyü kaydedilmiş uri'den yeniden oluşturmak, Google API dokümanlarında açıklandığı gibi izin bayrakları ekleseniz bile içerik çözücüye Güvenlik İstisnası atar (bazı testler yaptığımda böyle oldu)

Ayrıca Android Geliştirici API'sı Yönergeleri şunları önerir:

ACTION_OPEN_DOCUMENT, ACTION_GET_CONTENT yerine geçecek şekilde tasarlanmamıştır. Kullanmanız gereken uygulama uygulamanızın gereksinimlerine bağlıdır:

Uygulamanızın verileri basitçe okumasını / içe aktarmasını istiyorsanız ACTION_GET_CONTENT kullanın. Bu yaklaşımla uygulama, görüntü dosyası gibi verilerin bir kopyasını içe aktarır.

Uygulamanızın bir doküman sağlayıcısına ait dokümanlara uzun vadeli ve kalıcı bir şekilde erişmesini istiyorsanız ACTION_OPEN_DOCUMENT kullanın. Örnek olarak, kullanıcıların bir belge sağlayıcısında depolanan görüntüleri düzenlemesine izin veren bir fotoğraf düzenleme uygulaması verilebilir.


1
Bu cevap benim amacım için doğru bilgiyi içeriyordu. Koşullu olarak KitKat'ta ACTION_PICK ve EXTERNAL_CONTENT_URI kullanılması, yalnızca ACTION_GET_CONTENT kullanarak eski sürümlerde olduğu gibi ContentResolver aracılığıyla galerideki Görüntüler hakkında meta veri elde etmek için aynı yeteneği sağladı.
Colin M.

@voytez, Mesajınız aracılığıyla döndürülen bu URI, resmin tam gerçek yoluna dönüştürülebilir mi?
Yılan

Sanırım bu kod KitKat gibi çalışmalıdır çünkü bu kod KK belgeleri görünümü yerine resim galerisini açmaya zorlar. Ancak, görüntü oluşturmak için onu kullanmak istiyorsanız, bu çözüm, gerçek yola dönüştürmek gereksiz bir adım olduğundan daha iyidir.
voytez

4
Bunun yerine benim için de çalıştı Intent.ACTION_GET_CONTENT. Her neyse , kullanıcının göz atmak için uygulamayı seçmesine izin vermek için Intent.createChooser()sarıcıyı yeni tuttum ve Intentbeklendiği gibi çalıştım. Birisi bu çözümün dezavantajlarını görebilir mi?
lorenzo-s


38

Commonsware'in de belirttiği gibi, aldığınız akışın ContentResolverdosyaya dönüştürülebilir olduğunu varsaymamalısınız .

Ne gerçekten yapmalıyım açmaktır InputStreamdan ContentProvidersonra bunun dışında bir Bitmap oluşturmak. Ve 4.4 ve daha önceki sürümlerde de çalışır, düşünmeye gerek yoktur.

    //cxt -> current context

    InputStream input;
    Bitmap bmp;
    try {
        input = cxt.getContentResolver().openInputStream(fileUri);
        bmp = BitmapFactory.decodeStream(input);
    } catch (FileNotFoundException e1) {

    }

Elbette büyük resimleri işlerseniz, bunları uygun bir şekilde yüklemelisiniz inSampleSize: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html . Ama bu başka bir konu.


Bu benim için bir Nexus 4
Kitkat

@ Dan2552 hangi bölüm çalışmıyor? İstisna var mı?
Michał K

Galeriye yanlış niyet çağrısı kullandığım ortaya çıktı. Her türlü belge için bir dosya kullanıyordum, ancak bir dosya uzantısı filtresi ile.
Dan2552

2
Ne kadar basit bir cevap, teşekkürler! Bu cevabı izleyen diğerleri için 'cxt' mevcut bağlamı ifade eder ve genellikle 'bu' olur.
thomasforth

1
Bu muhtemelen dosyanın orada olmadığı anlamına gelir. URI iyi görünüyor.
Michał K

33

Önceden gönderilen yanıtların insanları doğru yönde yönlendirmesi gerektiğine inanıyorum. Ancak burada ben güncelleme eski kod için mantıklı ne yaptı. Eski kod, görüntüleri değiştirmek ve kaydetmek için galerideki URI'yi kullanıyordu.

4.4'ten (ve Google Drive'dan) önce URI'ler şöyle görünür: içerik: // medya / harici / resimler / medya / 41

Soruda belirtildiği gibi, daha sık şöyle görünürler: Soruda içerik: //com.android.providers.media.documents/document/image: 3951

Görüntüleri kaydetme ve mevcut kodu rahatsız etmeme yeteneğine ihtiyacım olduğundan, URI'yi galeriden uygulamanın veri klasörüne kopyaladım. Daha sonra veri klasöründeki kayıtlı görüntü dosyasından yeni bir URI kaynaklandı.

İşte fikir:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent), CHOOSE_IMAGE_REQUEST);

public void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    File tempFile = new File(this.getFilesDir().getAbsolutePath(), "temp_image");

    //Copy URI contents into temporary file.
    try {
        tempFile.createNewFile();
        copyAndClose(this.getContentResolver().openInputStream(data.getData()),new FileOutputStream(tempFile));
    }
    catch (IOException e) {
        //Log Error
    }

    //Now fetch the new URI
    Uri newUri = Uri.fromFile(tempFile);

    /* Use new URI object just like you used to */
 }

Not - copyAndClose (), InputStream öğesini bir FileOutputStream öğesine kopyalamak için G / Ç dosyası yapar. Kod gönderilmez.


oldukça zeki. ben de gerçek dosya uri gerekli
biggerKing

sen benim kahramanımsın, tam da ihtiyacım olan şey bu! Google Drive dosyaları için de harika çalışıyor
mishkin

Kutunun dışında düşün, değil mi? : D Bu kod tam beklediğim gibi çalışıyor.
WeirdElfB0y

2
copyAndClose kodunu gönderin, yanıt tamamlanmadı
Bartek Pacia

24

Sadece bu cevabın parlak olduğunu söylemek istedim ve uzun süre problemsiz kullanıyorum. Ancak bir süre önce DownloadsProvider'ın URI'leri biçiminde döndürdüğü content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdfve dolayısıyla NumberFormatExceptionuri segmentlerini ayrıştırmak imkansız olduğu için uygulamanın çöktüğü bir sorunla karşılaştım . Ancak raw:segment, başvurulan bir dosyayı almak için kullanılabilen doğrudan uri içerir. Bu yüzden isDownloadsDocument(uri) ifiçeriği aşağıdaki ile değiştirerek düzelttim :

final String id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
    return id.replaceFirst("raw:", "");
}
try {
    final Uri contentUri = ContentUris.withAppendedId(
            Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
    return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
    Log.e("FileUtils", "Downloads provider returned unexpected uri " + uri.toString(), e);
    return null;
}
}

2
Mükemmel çalışıyor! Teşekkür ederim
mikemike396

8

Birden çok yanıtı, dosya yolu ile sonuçlanan tek bir çalışma çözümünde birleştirdim

Mime türü örnek amaç için önemsizdir.

            Intent intent;
            if(Build.VERSION.SDK_INT >= 19){
                intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
                intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
            }else{
                intent = new Intent(Intent.ACTION_GET_CONTENT);
            }
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setType("application/octet-stream");
            if(isAdded()){
                startActivityForResult(intent, RESULT_CODE);
            }

İşleme sonucu

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if( requestCode == RESULT_CODE && resultCode == Activity.RESULT_OK) {
        Uri uri = data.getData();
        if (uri != null && !uri.toString().isEmpty()) {
            if(Build.VERSION.SDK_INT >= 19){
                final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
                //noinspection ResourceType
                getActivity().getContentResolver()
                        .takePersistableUriPermission(uri, takeFlags);
            }
            String filePath = FilePickUtils.getSmartFilePath(getActivity(), uri);
            // do what you need with it...
        }
    }
}

FilePickUtils

import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class FilePickUtils {
    private static String getPathDeprecated(Context ctx, Uri uri) {
        if( uri == null ) {
            return null;
        }
        String[] projection = { MediaStore.Images.Media.DATA };
        Cursor cursor = ctx.getContentResolver().query(uri, projection, null, null, null);
        if( cursor != null ){
            int column_index = cursor
                    .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        }
        return uri.getPath();
    }

    public static String getSmartFilePath(Context ctx, Uri uri){

        if (Build.VERSION.SDK_INT < 19) {
            return getPathDeprecated(ctx, uri);
        }
        return  FilePickUtils.getPath(ctx, uri);
    }

    @SuppressLint("NewApi")
    public static String getPath(final Context context, final Uri uri) {
        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }

                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    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 column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

}

i sorunla karşı karşıyaydı .... uri.getPath () uri ile / dış döndürüyordu ve yol döndürmüyordu. i ("content" .equalsIgnoreCase (uri.getScheme ())) bloğu ve bu şimdi iyi çalışıyorsa bunu başka ekledi .. u ne yaptığını açıklayabilir
FaisalAhmed

içerik: filePath .. Gelen uri null adlı oluyor //com.android.externalstorage.documents/document/799B-1419%3AScreenshot%2FScreenshot_20181117_162826.png
Bhavesh Moradiya

7

Soru

Bir URI'den gerçek bir dosya yolu nasıl alınır

Cevap

Bildiğim kadarıyla, dosya yolunu bir URI'den almamız gerekmez, çünkü çoğu durumda işimizi yapmak için doğrudan URI'yi kullanabiliriz (1. bitmap almak gibi 2. Sunucuya bir dosya gönderme, vb. .)

1. Sunucuya gönderme

Dosyayı yalnızca URI kullanarak doğrudan sunucuya gönderebiliriz.

URI kullanarak, MultiPartEntity kullanarak doğrudan sunucuya gönderebileceğimiz InputStream'i alabiliriz.

Misal

/**
 * Used to form Multi Entity for a URI (URI pointing to some file, which we got from other application).
 *
 * @param uri     URI.
 * @param context Context.
 * @return Multi Part Entity.
 */
public MultipartEntity formMultiPartEntityForUri(final Uri uri, final Context context) {
    MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
    try {
        InputStream inputStream = mContext.getContentResolver().openInputStream(uri);
        if (inputStream != null) {
            ContentBody contentBody = new InputStreamBody(inputStream, getFileNameFromUri(uri, context));
            multipartEntity.addPart("[YOUR_KEY]", contentBody);
        }
    }
    catch (Exception exp) {
        Log.e("TAG", exp.getMessage());
    }
    return multipartEntity;
}

/**
 * Used to get a file name from a URI.
 *
 * @param uri     URI.
 * @param context Context.
 * @return File name from URI.
 */
public String getFileNameFromUri(final Uri uri, final Context context) {

    String fileName = null;
    if (uri != null) {
        // Get file name.
        // File Scheme.
        if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
            File file = new File(uri.getPath());
            fileName = file.getName();
        }
        // Content Scheme.
        else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
            Cursor returnCursor =
                    context.getContentResolver().query(uri, null, null, null, null);
            if (returnCursor != null && returnCursor.moveToFirst()) {
                int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                fileName = returnCursor.getString(nameIndex);
                returnCursor.close();
            }
        }
    }
    return fileName;
}

2. Bir URI'den BitMap Alma

URI görüntüyü gösteriyorsa, bitmap alırız, aksi takdirde null:

/**
 * Used to create bitmap for the given URI.
 * <p>
 * 1. Convert the given URI to bitmap.
 * 2. Calculate ratio (depending on bitmap size) on how much we need to subSample the original bitmap.
 * 3. Create bitmap bitmap depending on the ration from URI.
 * 4. Reference - http://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri
 *
 * @param context       Context.
 * @param uri           URI to the file.
 * @param bitmapSize Bitmap size required in PX.
 * @return Bitmap bitmap created for the given URI.
 * @throws IOException
 */
public static Bitmap createBitmapFromUri(final Context context, Uri uri, final int bitmapSize) throws IOException {

    // 1. Convert the given URI to bitmap.
    InputStream input = context.getContentResolver().openInputStream(uri);
    BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
    onlyBoundsOptions.inJustDecodeBounds = true;
    onlyBoundsOptions.inDither = true;//optional
    onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
    input.close();
    if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
        return null;
    }

    // 2. Calculate ratio.
    int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
    double ratio = (originalSize > bitmapSize) ? (originalSize / bitmapSize) : 1.0;

    // 3. Create bitmap.
    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
    bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
    bitmapOptions.inDither = true;//optional
    bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    input = context.getContentResolver().openInputStream(uri);
    Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
    input.close();

    return bitmap;
}

/**
 * For Bitmap option inSampleSize - We need to give value in power of two.
 *
 * @param ratio Ratio to be rounded of to power of two.
 * @return Ratio rounded of to nearest power of two.
 */
private static int getPowerOfTwoForSampleRatio(final double ratio) {
    int k = Integer.highestOneBit((int) Math.floor(ratio));
    if (k == 0) return 1;
    else return k;
}

Yorumlar

  1. Android, bir URI'den dosya yolu almak için herhangi bir yöntem sağlamaz ve yukarıdaki yanıtların çoğunda, özellik sürümünde kırılabilecek bazı sabitleri kodladık (özür dilerim, yanlış olabilir).
  2. Bir URI'den dosya alma yolunun çözümüne doğrudan gitmeden önce, kullanım durumunuzu bir URI ve Android varsayılan yöntemleriyle çözüp çözemeyeceğinizi deneyin.

Referans

  1. https://developer.android.com/guide/topics/providers/content-provider-basics.html
  2. https://developer.android.com/reference/android/content/ContentResolver.html
  3. https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/InputStreamBody.html

Teşekkür ederim. Uri ve ContentResolver'ı bu şekilde kullanmak, dosyalarla uğraşırken uygulamamı önemli ölçüde basitleştirdi.
jacksonakj


3

Hala Android SDK sürüm 23 ve üzeri ile @Paul Burke kodunu kullananlar için, projeniz EXTERNAL_PERMISSION eksik olduğunu söyleyen hatayla karşılaştı ve AndroidManifest.xml dosyanıza zaten kullanıcı izni eklediğinizden eminseniz. Bunun nedeni, Android API 23 veya daha yeni bir sürümde olabilirsiniz ve Google, çalışma zamanında dosyaya erişmek için işlem yaparken yeniden izin vermeyi gerekli kılar.

Bunun anlamı: SDK sürümünüz 23 veya üstü ise, resim dosyasını seçerken ve URI'sini bilmek istediğinizde READ & WRITE izniniz istenir.

Paul Burke'ün çözümüne ek olarak kodum da aşağıda. Ben bu kodu eklemek ve benim proje iyi çalışmaya başlar.

private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSINOS_STORAGE = {
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.WRITE_EXTERNAL_STORAGE
};

public static void verifyStoragePermissions(Activity activity) {
    int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(
                activity,
                PERMISSINOS_STORAGE,
                REQUEST_EXTERNAL_STORAGE
        );
    }
}

Ve URI'yi istediğiniz aktivite ve parçanızda:

private void pickPhotoFromGallery() {

    CompatUtils.verifyStoragePermissions(this);
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    // startActivityForResult(intent, REQUEST_PHOTO_LIBRARY);
    startActivityForResult(Intent.createChooser(intent, "选择照片"),
            REQUEST_PHOTO_LIBRARY);
}

Benim durumumda CompatUtils.java, verifyStoragePermissions yöntemini tanımladığım yerdir (diğer türler içinde çağırabileceğim için statik tür olarak).

Ayrıca, verifyStoragePermissions yöntemini çağırmadan önce geçerli SDK sürümünün 23'ün üstünde olup olmadığını görmek için bir if durumu yaparsanız daha mantıklı olmalıdır.


2

Bu benim işim:

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

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

NOT: managedQuery()yöntem kullanımdan kaldırılmıştır, bu yüzden kullanmıyorum.

Bu cevap m3n0R soru android Uri.getPath () tarafından gerçek yol olsun ve ben hiçbir kredi iddia. Ben sadece bu sorunu henüz çözmemiş insanların bunu kullanabileceğini düşündüm.


2
Bu KitKat yeni Galeri uygulaması (kesinlikle "Medya Belgeleri Sağlayıcısı" uygulaması) bir cevap değildir.
nagoya0

Sorgulayan "Galeri" adlı uygulama muhtemelen kitkat yeni dosya seçici olduğunu. FYI: addictivetips.com/android/…
nagoya0

Ben benzer yapmak ve bu satırda Nexus 5X, Android 6 üzerinde cursor.getString(idx);
IndexOutOfBound var

1

Lütfen takePersistableUriPermission yöntemini kullanmaktan kaçının, çünkü benim için çalışma zamanı istisnasını artırdı. / ** * Galeriden seç. * /

public void selectFromGallery() {
    if (Build.VERSION.SDK_INT < AppConstants.KITKAT_API_VERSION) {

        Intent intent = new Intent(); 
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        ((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED);

    } else {

        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        ((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED);
    }
}

Görüntü verilerini işlemek için sonuç için OnActivity:

@ Korumalı geçersizliği geçersiz kıl onActivityResult (int requestCode, int sonuçKodu, Niyet verileri) {

    //gallery intent result handling before kit-kat version
    if(requestCode==AppConstants.GALLERY_INTENT_CALLED 
            && resultCode == RESULT_OK) {

        Uri selectedImage = data.getData();
        String[] filePathColumn = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null);
        cursor.moveToFirst();
        int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
        String filePath = cursor.getString(columnIndex);
        cursor.close();
        photoFile = new File(filePath);
        mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP);

    }
    //gallery intent result handling after kit-kat version
    else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED 
            && resultCode == RESULT_OK) {

        Uri selectedImage = data.getData();
        InputStream input = null;
        OutputStream output = null;

        try {
            //converting the input stream into file to crop the 
            //selected image from sd-card.
            input = getApplicationContext().getContentResolver().openInputStream(selectedImage);
            try {
                photoFile = mImgCropping.createImageFile();
            } catch (IOException e) {
                e.printStackTrace();
            }catch(Exception e) {
                e.printStackTrace();
            }
            output = new FileOutputStream(photoFile);

            int read = 0;
            byte[] bytes = new byte[1024];

            while ((read = input.read(bytes)) != -1) {
                try {
                    output.write(bytes, 0, read);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

İkinci durumda görüntüyü nerede gösteriyorsunuz?
Quantum_VC

üzgünüm eğer mImgCropping.startCropImage (photoFile, AppConstants.REQUEST_IMAGE_CROP) eğer başka bir kod satırı ekleyemedi; tekrar benim proje ihtiyacına göre imagecropping işlevini aramak gerekir
saranya

PhotoFile ve mImgCropping dosya türü nedir?
Philip BH

1

Eğer ilgilenen varsa, çalışan bir Kotlin versiyonu yaptım ACTION_GET_CONTENT:

var path: String = uri.path // uri = any content Uri
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if (path.contains("/document/image:")) { // files selected from "Documents"
    databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    selection = "_id=?"
    selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
} else { // files selected from all other sources, especially on Samsung devices
    databaseUri = uri
    selection = null
    selectionArgs = null
}
try {
    val projection = arrayOf(MediaStore.Images.Media.DATA,
        MediaStore.Images.Media._ID,
        MediaStore.Images.Media.ORIENTATION,
        MediaStore.Images.Media.DATE_TAKEN) // some example data you can query
    val cursor = context.contentResolver.query(databaseUri,
        projection, selection, selectionArgs, null)
    if (cursor.moveToFirst()) {
        // do whatever you like with the data
    }
    cursor.close()
} catch (e: Exception) {
    Log.e(TAG, e.message, e)
}

Sadece kotlin'in çalışma kodunu istiyorum. Benim için iş. teşekkürler
Harvi Sirja

1

Burada birkaç cevabı denedim ve her seferinde çalışacak ve izinleri de yönetecek bir çözümüm olduğunu düşünüyorum.

LEO'nun akıllı çözümüne dayanmaktadır. Bu yayın, bu işi yapmak için ihtiyacınız olan tüm kodları içermelidir ve herhangi bir telefon ve Android sürümünde çalışmalıdır;)

Bir SD karttan dosya seçebilmeniz için, bildiriminizde buna ihtiyacınız olacak:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Sabitler:

private static final int PICK_IMAGE = 456; // Whatever number you like
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; // Whatever number you like
public static final String FILE_TEMP_NAME = "temp_image"; // Whatever file name you like

İzni kontrol edin ve mümkünseImagePick'i başlatın

if (ContextCompat.checkSelfPermission(getThis(),
        Manifest.permission.READ_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {

    ActivityCompat.requestPermissions(getThis(),
            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            MY_PERMISSIONS_REQUEST_READ_EXTERNAL);
}
else {
    launchImagePick();
}

İzin yanıtı

@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull
                                         String permissions[],
                                       @NonNull
                                         int[] grantResults) {

    if (manageReadExternalPermissionResponse(this, requestCode, grantResults)) {
        launchImagePick();
    }
}

İzin yanıtını yönet

public static boolean manageReadExternalPermissionResponse(final Activity activity, int requestCode, int[] grantResults) {

    if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) {

        // If request is cancelled, the result arrays are empty.

        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            // Permission was granted, yay! Do the
            // contacts-related task you need to do.
            return true;

        } else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {

            boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity,
                    Manifest.permission.READ_EXTERNAL_STORAGE);

            if (!showRationale) {
                // The user also CHECKED "never ask again".
                // You can either enable some fall back,
                // disable features of your app
                // or open another dialog explaining
                // again the permission and directing to
                // the app setting.

            } else {
                // The user did NOT check "never ask again".
                // This is a good place to explain the user
                // why you need the permission and ask if he/she wants
                // to accept it (the rationale).
            }
        } else {
            // Permission denied, boo! Disable the
            // functionality that depends on this permission.
        }
    }
    return false;
}

Görüntü seçimini başlat

private void launchImagePick() {

    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    startActivityForResult(intent, PICK_IMAGE);

    // see onActivityResult
}

Görüntü seçme yanıtını yönet

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE) {

        if (resultCode == Activity.RESULT_OK) {
            if (data != null && data.getData() != null) {

                try {
                     InputStream inputStream = getContentResolver().openInputStream(data.getData())
                     if (inputStream != null) {

                        // No special persmission needed to store the file like that
                        FileOutputStream fos = openFileOutput(FILE_TEMP_NAME, Context.MODE_PRIVATE);

                        final int BUFFER_SIZE = 1 << 10 << 3; // 8 KiB buffer
                        byte[] buffer = new byte[BUFFER_SIZE];
                        int bytesRead = -1;
                        while ((bytesRead = inputStream.read(buffer)) > -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                        inputStream.close();
                        fos.close();

                        File tempImageFile = new File(getFilesDir()+"/"+FILE_TEMP_NAME);

                        // Do whatever you want with the File

                        // Delete when not needed anymore
                        deleteFile(FILE_TEMP_NAME);
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                // Error display
            }
        } else {
            // The user did not select any image
        }
    }
}

Hepsi bu kadar millet; bu benim sahip olduğum tüm telefonlarda benim için çalışıyor.


0

Bu tam bir hack, ama işte yaptığım şey ...

Bu yüzden bir DocumentsProvider kurarak oynarken , örnek kodun ( getDocIdForFile450 satırında,), verdiğiniz belirtilen köke göre dosyanın (benzersiz) yolunu temel alarak seçilen bir belge için benzersiz bir kimlik oluşturduğunu fark ettim. ne ayarladınmBaseDir 96 satırında ).

Böylece URI şöyle görünür:

content://com.example.provider/document/root:path/to/the/file

Dokümanların dediği gibi, sadece tek bir kök varsayıyor (benim durumumda, Environment.getExternalStorageDirectory()ancak başka bir yerde kullanabilirsiniz ... o zaman kökten başlayarak dosya yolunu alır ve " root:" başlayarak benzersiz bir kimlik yapar . yolu "/document/root:"uri.getPath () bölümünden kaldırarak, böyle bir şey yaparak gerçek bir dosya yolu oluşturarak yolu belirleyebilir:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
// check resultcodes and such, then...
uri = data.getData();
if (uri.getAuthority().equals("com.example.provider"))  {
    String path = Environment.getExternalStorageDirectory(0.toString()
                 .concat("/")
                 .concat(uri.getPath().substring("/document/root:".length())));
    doSomethingWithThePath(path); }
else {
    // another provider (maybe a cloud-based service such as GDrive)
    // created this uri.  So handle it, or don't.  You can allow specific
    // local filesystem providers, filter non-filesystem path results, etc.
}

Biliyorum. Utanç verici, ama işe yaradı. Yine, bu , doküman kimliğini oluşturmak için uygulamanızda kendi doküman sağlayıcınızı kullanmanıza dayanır .

(Ayrıca, yol ayırıcı vb. "/" Olduğunu varsaymayan yolu oluşturmanın daha iyi bir yolu vardır.


Daha çılgın bir düşünce ile kendime cevap vermek için - uygulamanız zaten çalışıyorsa file:// harici bir dosya seçiciden amaçlarla ilgileniyorsa, yukarıdaki örnekte olduğu gibi özel sağlayıcınızdan emin olmak için otoriteyi de kontrol edebilirsiniz. ayrıca file://, ayıkladığınız yolu kullanarak yeni bir niyet "oluşturmak" için yolu kullanın StartActivity()ve uygulamanızın bunu almasına izin verin. Biliyorum, korkunç.
fattire

0

Bu benim için iyi çalıştı:

else if(requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK)
{
    Uri uri = data.getData();
    Log.i(TAG, "old uri =  " + uri);
    dumpImageMetaData(uri);

    try {
        ParcelFileDescriptor parcelFileDescriptor =
                getContentResolver().openFileDescriptor(uri, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Log.i(TAG, "File descriptor " + fileDescriptor.toString());

        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);

        options.inSampleSize =
           BitmapHelper.calculateInSampleSize(options,
                                              User.PICTURE_MAX_WIDTH_IN_PIXELS,
                                              User.PICTURE_MAX_HEIGHT_IN_PIXELS);
        options.inJustDecodeBounds = false;

        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
        imageViewPic.setImageBitmap(bitmap);

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        // get byte array here
        byte[] picData = stream.toByteArray();
        ParseFile picFile = new ParseFile(picData);
        user.setProfilePicture(picFile);
    }
    catch(FileNotFoundException exc)
    {
        Log.i(TAG, "File not found: " + exc.toString());
    }
}

dumpImageMetaData'yı (uri) unutun; gereksiz
Rafa

0

Üzerine inşa Paul Burke'ün cevap ben dosyalara çözülmesi alamadım önerdi "yerleşik" işlevleri dönüş yollarını en olarak harici SD kart URI yolunu çözme birçok sorunlarla karşı karşıya.

Ancak, bu benim // TODO birincil olmayan hacimleri ele benim yaklaşımdır .

String resolvedPath = "";
File[] possibleExtSdComposites = context.getExternalFilesDirs(null);
for (File f : possibleExtSdComposites) {
    // Reset final path
    resolvedPath = "";

    // Construct list of folders
    ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/")));

    // Look for folder "<your_application_id>"
    int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID);

    // ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard's root is /storage/0000-0000/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files
    ArrayList<String> hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2));

    // Construct list containing full possible path to the file
    hierarchyList.add(tail);
    String possibleFilePath = TextUtils.join("/", hierarchyList);

    // If file is found --> success
    if (idx != -1 && new File(possibleFilePath).exists()) {
        resolvedPath = possibleFilePath;
        break;
    }
}

if (!resolvedPath.equals("")) {
    return resolvedPath;
} else {
    return null;
}

Her telefon üreticisinde farklı olabilecek hiyerarşiye bağlı olduğunu unutmayın - Hepsini test etmedim (şimdiye kadar Xperia Z3 API 23 ve Samsung Galaxy A3 API 23 üzerinde iyi çalıştı).

Lütfen başka bir yerde iyi performans göstermediğini onaylayın.


-1

@paul burke'un yanıtı, API düzeyi 19 ve üstü için hem kamera hem de galeri resimleri için iyi çalışır, ancak Android projenizin minimum SDK'sı 19'un altına ayarlanmışsa ve yukarıdaki referanslardan bazılarının hem galeri hem de kamera. Eh, 19 seviyesinin altındaki API düzeyi için çalışan @paul burke kodunu değiştirdim. Kod aşağıda.

public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >=
                             Build.VERSION_CODES.KITKAT;
    Log.i("URI",uri+"");
    String result = uri+"";

    // DocumentProvider
    // if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
    if (isKitKat && (result.contains("media.documents"))) {

        String[] ary = result.split("/");
        int length = ary.length;
        String imgary = ary[length-1];
        final String[] dat = imgary.split("%3A");

        final String docId = dat[1];
        final String type = dat[0];

        Uri contentUri = null;
        if ("image".equals(type)) {
            contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        }
        else if ("video".equals(type)) {
        }
        else if ("audio".equals(type)) {
        }

        final String selection = "_id=?";
        final String[] selectionArgs = new String[] {
            dat[1]
        };

        return getDataColumn(context, contentUri, selection, selectionArgs);
    }
    else
    if ("content".equalsIgnoreCase(uri.getScheme())) {
        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

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

Java.lang.IllegalArgumentException alıyorum: Bir Google Doc resmi seçerken istenen sütunların hiçbiri
sağlanamıyor

@dirkoneill Aynı İstisnayı alıyorum. Bir düzeltme buldunuz mu?
Henry

-4

Sorunuzun cevabı, izinlere sahip olmanız gerektiğidir. Manifest.xml dosyanıza aşağıdaki kodu yazın:

<uses-sdk  android:minSdkVersion="8"   android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_OWNER_DATA"></uses-permission>
<uses-permission android:name="android.permission.READ_OWNER_DATA"></uses-permission>`

Benim için çalıştı ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.