Android: Bir içerik URI'sinden bir dosya URI'si mi alıyorsunuz?


133

Benim uygulamamda kullanıcı, uygulamanın işleyeceği bir ses dosyası seçecek. Sorun şu ki, uygulamanın ses dosyalarıyla yapmasını istediğim şeyi yapması için URI'nin dosya biçiminde olmasına ihtiyacım var. Uygulamadaki ses dosyasına göz atmak için Android'in yerel müzik oynatıcısını kullandığımda, URI şuna benzeyen bir içerik URI'sıdır:

content://media/external/audio/media/710

Ancak, popüler dosya yöneticisi uygulaması Astro'yu kullanarak aşağıdakileri elde ediyorum:

file:///sdcard/media/audio/ringtones/GetupGetOut.mp3

İkincisi, benim için çok daha erişilebilir, ancak elbette uygulamanın, koleksiyonlarına göz atmak için kullandıkları programdan bağımsız olarak kullanıcının seçtiği ses dosyasıyla işlevselliğe sahip olmasını istiyorum. Öyleyse sorum şu, content://stil URI'yi bir file://URI'ye dönüştürmenin bir yolu var mı? Aksi takdirde, bu sorunu çözmem için bana ne önerirsiniz? Referans için seçiciyi çağıran kod:

Intent ringIntent = new Intent();
ringIntent.setType("audio/mp3");
ringIntent.setAction(Intent.ACTION_GET_CONTENT);
ringIntent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(ringIntent, "Select Ringtone"), SELECT_RINGTONE);

İçerik URI'si ile aşağıdakileri yapıyorum:

m_ringerPath = m_ringtoneUri.getPath();
File file = new File(m_ringerPath);

Ardından, söz konusu dosya ile bazı FileInputStream şeyler yapın.


1
İçerik URI'lerini sevmeyen hangi aramaları kullanıyorsunuz?
Phil Lello

1
Dosya yolunu alamadığınız çok fazla içerik Uris var , çünkü tüm içerik Uris'in dosya yolları yok . Dosya yollarını kullanmayın.
Mooing Duck

Yanıtlar:


153

Sadece bir URI'den getContentResolver().openInputStream(uri)almak için kullanın InputStream.

http://developer.android.com/reference/android/content/ContentResolver.html#openInputStream(android.net.Uri)


12
Seçici etkinliğinden size döndürülen URI'nin şemasını kontrol edin. Uri.getScheme.equals ("içerik") ise, bir içerik çözümleyiciyle açın. Uri.Scheme.equals ("dosya") ise, normal dosya yöntemlerini kullanarak açın. Her iki durumda da, ortak kodu kullanarak işleyebileceğiniz bir InputStream elde edersiniz.
Jason LeBrun

16
Aslında, getContentResolver (). OpenInputStream () için dokümanları yeniden okudum ve "içerik" veya "dosya" şemaları için otomatik olarak çalışır, böylece şemayı kontrol etmenize gerek kalmaz ... güvenli bir şekilde yapabilirseniz her zaman content: // veya file: // olacağını varsayın, sonra openInputStream () her zaman çalışacaktır.
Jason LeBrun

57
InputStream yerine Dosyayı almanın bir yolu var mı (içerikten: ...)?
AlikElzin-kilaka

4
@kilaka Dosya yolunu alabilirsiniz ama bu acı verici. Bkz stackoverflow.com/a/20559418/294855
Danyal Aytekin

7
Bu yanıt, FileStreams yerine Files'a dayanan kapalı kaynaklı bir API kullanan, ancak yine de kullanıcının dosyayı seçmesine izin vermek için işletim sistemini kullanmak isteyen biri için yetersizdir. @DanyalAytekin'in başvurduğu cevap tam olarak ihtiyacım olan şeydi (ve aslında, ne tür dosyalarla çalıştığımı tam olarak bildiğim için çok fazla yağı kesebildim).
monkey0506

45

Bu, bazı belirli içerik çözücü sorun noktalarının üstesinden gelmenin kullanımdan kaldırılmış ve hilekâr bir yöntemiyle eski bir cevaptır. Biraz büyük tuz taneleri ile alın ve mümkünse uygun openInputStream API'sini kullanın.

URI'den bir file://yol almak için İçerik Çözümleyiciyi kullanabilirsiniz content://:

String filePath = null;
Uri _uri = data.getData();
Log.d("","URI = "+ _uri);                                       
if (_uri != null && "content".equals(_uri.getScheme())) {
    Cursor cursor = this.getContentResolver().query(_uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
    cursor.moveToFirst();   
    filePath = cursor.getString(0);
    cursor.close();
} else {
    filePath = _uri.getPath();
}
Log.d("","Chosen path = "+ filePath);

1
Teşekkürler, bu mükemmel çalıştı. Kabul edilen yanıtın önerdiği gibi bir InputStream kullanamadım.
ldam

4
Bu yalnızca yerel dosyalar için çalışır, örneğin Google Drive için çalışmaz
bluewhile

1
Bazen çalışır, bazen var olmayan file: /// storage / emulated / 0 / ... döndürür.
Reza Mohammadi

1
android.provider.MediaStore.Images.ImageColumns.DATAŞema varsa "_data" ( ) sütununun varlığı her zaman garanti edilir content://mi?
Edward Falk

2
Bu, büyük bir anti-modeldir. Bazı ContentProviders bu sütunu sağlar, ancak FileContentResolver'ı atlamaya çalıştığınızda okuma / yazma erişimine sahip olmanız garanti edilmez . Uris üzerinde çalışmak için ContentResolver yöntemlerini kullanın content://; bu, Google mühendisleri tarafından teşvik edilen resmi yaklaşımdır.
user1643723

9

Bir içerik URI'niz content://com.externalstorage...varsa , Android 19 veya üzeri bir klasör veya dosyanın mutlak yolunu almak için bu yöntemi kullanabilirsiniz .

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)) {
        System.out.println("getPath() uri: " + uri.toString());
        System.out.println("getPath() uri authority: " + uri.getAuthority());
        System.out.println("getPath() uri path: " + uri.getPath());

        // ExternalStorageProvider
        if ("com.android.externalstorage.documents".equals(uri.getAuthority())) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];
            System.out.println("getPath() docId: " + docId + ", split: " + split.length + ", type: " + type);

            // This is for checking Main Memory
            if ("primary".equalsIgnoreCase(type)) {
                if (split.length > 1) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1] + "/";
                } else {
                    return Environment.getExternalStorageDirectory() + "/";
                }
                // This is for checking SD Card
            } else {
                return "storage" + "/" + docId.replace(":", "/");
            }

        }
    }
    return null;
}

Uri'nin her parçasının println kullandığını kontrol edebilirsiniz. SD kartım ve cihaz ana belleğim için döndürülen değerler aşağıda listelenmiştir. Dosya hafızadaysa erişebilir ve silebilirsiniz ancak bu yöntemi kullanarak SD karttan dosya silemedim , yalnızca bu mutlak yolu kullanarak görüntüyü okuyun veya açın. Bu yöntemi kullanarak silmek için bir çözüm bulursanız, lütfen paylaşın. HAFIZA KARTI

getPath() uri: content://com.android.externalstorage.documents/tree/612E-B7BF%3A/document/612E-B7BF%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/612E-B7BF:/document/612E-B7BF:
getPath() docId: 612E-B7BF:, split: 1, type: 612E-B7BF

ANA HAFIZA

getPath() uri: content://com.android.externalstorage.documents/tree/primary%3A/document/primary%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/primary:/document/primary:
getPath() docId: primary:, split: 1, type: primary

file:///Yolu kullandıktan sonra Uri almak isterseniz

DocumentFile documentFile = DocumentFile.fromFile(new File(path));
documentFile.getUri() // will return a Uri with file Uri

Bunu bir uygulamada yapmanın doğru yolu olduğunu düşünmüyorum. ne yazık ki bunu hızlı bir projede kullanıyorum
Bondax

@Bondax Evet, dosya yolları veya File Uris yerine Content Uris ile çalışmalısınız. Bu şekilde depolama erişim çerçevesi tanıtılır. Ancak, uri dosyasını almak istiyorsanız, DocumentsContract sınıfını kullandığınız için bu diğer cevapların en doğru yoludur. Github'daki Google örneklerini kontrol ederseniz, bir klasörün alt klasörlerini almak için bu sınıfa kullandıklarını göreceksiniz.
Trakya

Ve DocumentFile sınıfı da api 19'a ve SAF'den uris'i nasıl kullandığınıza yeni bir ek. Doğru yol, uygulamanız için standart bir yol kullanmak ve kullanıcıdan SAF kullanıcı arabirimi aracılığıyla bir klasör için izin vermesini istemektir, Uri dizesini paylaşılan tercihlere kaydetmeniz ve gerektiğinde DocumentFile nesneleriyle klasöre erişmeniz gerektiğinde
Thracian

7

URI'yi content: // şemasıyla işlemeyi çağırarak işlemeye çalışmak ContentResolver.query()iyi bir çözüm değildir. 4.2.2 çalıştıran HTC Desire'da sorgu sonucu olarak NULL alabilirsiniz.

Neden bunun yerine ContentResolver kullanılmasın? https://stackoverflow.com/a/29141800/3205334


Ama bazen sadece yola ihtiyacımız var. Dosyayı gerçekten belleğe yüklememiz gerekmez.
Kimi Chiu

2
Erişim haklarına sahip değilseniz "yol" işe yaramaz. Örneğin, bir uygulama size content://özel dahili dizinindeki dosyaya karşılık gelen bir uri verirse , bu uri'yi Fileyeni Android sürümlerinde API'lerle kullanamazsınız . ContentResolver, bu tür güvenlik sınırlamalarının üstesinden gelmek için tasarlanmıştır. URI'yi ContentResolver'dan aldıysanız, sadece çalışmasını bekleyebilirsiniz.
user1643723

5

İlham veren cevaplar Jason LaBrun ve Darth Raven'dır . Zaten cevaplanmış yaklaşımları denemek beni çoğunlukla imleç boş durumlarını ve içerik: //' den dosyaya: // dönüştürmeyi kapsayabilecek aşağıdaki çözüme götürdü

Dosyayı dönüştürmek için, dosyayı kazanılan uri'den okuyun ve yazın

public static Uri getFilePathFromUri(Uri uri) throws IOException {
    String fileName = getFileName(uri);
    File file = new File(myContext.getExternalCacheDir(), fileName);
    file.createNewFile();
    try (OutputStream outputStream = new FileOutputStream(file);
         InputStream inputStream = myContext.getContentResolver().openInputStream(uri)) {
        FileUtil.copyStream(inputStream, outputStream); //Simply reads input to output stream
        outputStream.flush();
    }
    return Uri.fromFile(file);
}

Dosya adı kullanımı elde etmek için, imlecin boş durumunu kapsayacaktır.

public static String getFileName(Uri uri) {
    String fileName = getFileNameFromCursor(uri);
    if (fileName == null) {
        String fileExtension = getFileExtension(uri);
        fileName = "temp_file" + (fileExtension != null ? "." + fileExtension : "");
    } else if (!fileName.contains(".")) {
        String fileExtension = getFileExtension(uri);
        fileName = fileName + "." + fileExtension;
    }
    return fileName;
}

Mime türünden dosya uzantısına dönüştürmek için iyi bir seçenek var

 public static String getFileExtension(Uri uri) {
    String fileType = myContext.getContentResolver().getType(uri);
    return MimeTypeMap.getSingleton().getExtensionFromMimeType(fileType);
}

Dosyanın adını almak için imleç

public static String getFileNameFromCursor(Uri uri) {
    Cursor fileCursor = myContext.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
    String fileName = null;
    if (fileCursor != null && fileCursor.moveToFirst()) {
        int cIndex = fileCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        if (cIndex != -1) {
            fileName = fileCursor.getString(cIndex);
        }
    }
    return fileName;
}

Teşekkürler, bir haftadır buna bakıyordum. Bunun için bir dosyayı kopyalamaktan hoşlanmıyorum ama işe yarıyor.
justdan0227

3

Cevaplamak için biraz geç kaldım ama kodum test edildi

uri'den kontrol şeması:

 byte[] videoBytes;

if (uri.getScheme().equals("content")){
        InputStream iStream =   context.getContentResolver().openInputStream(uri);
            videoBytes = getBytes(iStream);
        }else{
            File file = new File(uri.getPath());
            FileInputStream fileInputStream = new FileInputStream(file);     
            videoBytes = getBytes(fileInputStream);
        }

Yukarıdaki cevapta video uri'yi bayt dizisine dönüştürdüm, ancak bu soru ile ilgili değil, sadece kodumun kullanımını göstermek için tam kodumu kopyaladım FileInputStreamve InputStreamher ikisi de kodumda aynı şekilde çalışıyor.

Parçamda getActivity () olan değişken bağlamını kullandım ve Activity'de sadece ActivityName.this

context=getActivity(); // Parçada

context=ActivityName.this;// faaliyette


Soruyla ilgili olmadığını biliyorum, ama o zaman nasıl kullanılır byte[] videoBytes;? Çoğu yanıt yalnızca InputStreambir resimle nasıl kullanılacağını gösterir .
KRK
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.