Intent.ACTION_GET_CONTENT'ten döndürülen URI'den dosya adı nasıl ayıklanır?


105

Dosya sisteminden bir dosya (benim durumumda PDF) seçmek için 3. taraf dosya yöneticisi kullanıyorum.

Aktiviteyi şu şekilde başlatıyorum:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType(getString(R.string.app_pdf_mime_type));
intent.addCategory(Intent.CATEGORY_OPENABLE);

String chooserName = getString(R.string.Browse);
Intent chooser = Intent.createChooser(intent, chooserName);

startActivityForResult(chooser, ActivityRequests.BROWSE);

İçinde bulunduğum şey bu onActivityResult:

Uri uri = data.getData();
if (uri != null) {
    if (uri.toString().startsWith("file:")) {
        fileName = uri.getPath();
    } else { // uri.startsWith("content:")

        Cursor c = getContentResolver().query(uri, null, null, null, null);

        if (c != null && c.moveToFirst()) {

            int id = c.getColumnIndex(Images.Media.DATA);
            if (id != -1) {
                fileName = c.getString(id);
            }
        }
    }
}

Kod parçacığı, burada bulunan Open Intents File Manager talimatlarından ödünç alınmıştır :
http://www.openintents.org/en/node/829

Bunun amacı if-elsegeriye dönük uyumluluktur. Diğer dosya yöneticilerinin her türlü şeyi döndürdüğünü bulduğum için dosya adını almanın en iyi yolu bu mu merak ediyorum.

Örneğin, Documents ToGo aşağıdaki gibi bir şey döndürür:

content://com.dataviz.dxtg.documentprovider/document/file%3A%2F%2F%2Fsdcard%2Fdropbox%2FTransfer%2Fconsent.pdf

hangi getContentResolver().query()döner null.

İşleri daha ilginç hale getirmek için, adsız dosya yöneticisi (bu URI'yi istemci günlüğünden aldım) şöyle bir şey döndürdü:

/./sdcard/downloads/.bin


Dosya adını URI'den çıkarmanın tercih edilen bir yolu var mı yoksa dize ayrıştırmaya mı başvurulmalı?


Belki daha iyi cevapla aynı soru: stackoverflow.com/questions/8646246/…
Rémi

Yanıtlar:


170

developer.android.com'un bunun için güzel bir örnek kodu var: https://developer.android.com/guide/topics/providers/document-provider.html

Yalnızca dosya adını çıkarmak için yoğunlaştırılmış bir sürüm ("bu" nun bir Etkinlik olduğu varsayılarak):

public String getFileName(Uri uri) {
  String result = null;
  if (uri.getScheme().equals("content")) {
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    try {
      if (cursor != null && cursor.moveToFirst()) {
        result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
      }
    } finally {
      cursor.close();
    }
  }
  if (result == null) {
    result = uri.getPath();
    int cut = result.lastIndexOf('/');
    if (cut != -1) {
      result = result.substring(cut + 1);
    }
  }
  return result;
}

durumda öncelikle dönüşüm gerçekleştirecek MediaScanner bildirmek zorunda kameradan dosyanın MIMETYPE veya fileName okumak çalıştığınızda URI adresinin yeni oluşturulan dosyanın file://için content://de onScanCompleted(String path, Uri uri)yöntem stackoverflow.com/a/5815005/2163045
Murt

10
new String[]{OpenableColumns.DISPLAY_NAME}Sorguya ikinci bir parametre olarak eklemek , daha verimli bir istek için sütunları filtreleyecektir.
JM Lord

OpenableColumns.DISPLAY_NAMEbenim için çalışmadı, MediaStore.Files.FileColumns.TITLEonun yerine kullandım .
Dmitry Kopytov

Bu java.lang.IllegalArgumentException: Invalid column latitudemaalesef imleci oluştururken videolar için çökecektir. Yine de fotoğraflar için mükemmel çalışıyor!
Lucas P.

Geri dönüşünüzde, uri.getLastPathSegment()kendiniz ayrıştırmak yerine arayabilirsiniz .
Grishka

45

Bunun gibi bir şey kullanıyorum:

String scheme = uri.getScheme();
if (scheme.equals("file")) {
    fileName = uri.getLastPathSegment();
}
else if (scheme.equals("content")) {
    String[] proj = { MediaStore.Images.Media.TITLE };
    Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
    if (cursor != null && cursor.getCount() != 0) {
        int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE);
        cursor.moveToFirst();
        fileName = cursor.getString(columnIndex);
    }
    if (cursor != null) {
        cursor.close();
    }
}

4
Kodunuz üzerinde bazı testler yaptım. OI Dosya Yöneticisi tarafından döndürülen URI üzerinde IllegalArgumentException 'başlık' sütunu mevcut olmadığından atılır. Documents ToGo imleci tarafından döndürülen URI'de null. Bilinmeyen dosya yöneticisi tarafından döndürülen URI'de (tabii ki) boş.
Viktor Brešan

2
Hmm, ilginç. Evet, şema için test etmek! = Null iyi bir fikirdir. Aslında TITLE ne istediğinizi sanmıyorum. Bunu kullanıyordum çünkü Android'de (müzik seçici aracılığıyla seçilen şarkılar gibi) içerik gibi URI'ler var: // medya / harici / ses / medya / 78 ve kimlik numarasından daha alakalı bir şey görüntülemek istedim. Uri.getLastPathSegment () 'i kodumun file: // URI'ler için kullandığı gibi, content: //...somefile.pdf
Ken Fehling

1
uri.getLastPathSegment (); - günümü kurtardın :)
Lumis

@Ken Fehling: Bu benim için işe yaramadı. Dosya tarayıcısında bir dosyaya tıkladığımda çalışıyor, ancak bir e-posta ekine tıkladığımda hala içeriği: // ... şeyini alıyorum. Buradaki tüm önerileri şanssız denedim. Herhangi bir fikrin neden?
Luis A. Florit

1
Merhaba, neden d cursordeğil close()?
fikr4n

35

Alındığı alınıyor Dosya bilgisi | Android geliştiricileri

Bir Dosyanın adını almak.

private String queryName(ContentResolver resolver, Uri uri) {
    Cursor returnCursor =
            resolver.query(uri, null, null, null, null);
    assert returnCursor != null;
    int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
    returnCursor.moveToFirst();
    String name = returnCursor.getString(nameIndex);
    returnCursor.close();
    return name;
}

1
Bunu çok uzun zamandır arıyordum!
dasfima

7
Teşekkür ederim. Tanrım, neden bu kadar önemsiz bir şeyi bu kadar sinir bozucu yapmak zorundalar?
TylerJames

1
Yalnızca içerik dosyalarıyla çalışır. Tam yöntem için stackoverflow.com/a/25005243/6325722 sayfasına bakın
Johnny Five

18

Dosya adını almanın en kolay yolları:

val fileName = File(uri.path).name
// or
val fileName = uri.pathSegments.last()

Size doğru adı vermezlerse kullanmalısınız:

fun Uri.getName(context: Context): String {
    val returnCursor = context.contentResolver.query(this, null, null, null, null)
    val nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
    returnCursor.moveToFirst()
    val fileName = returnCursor.getString(nameIndex)
    returnCursor.close()
    return fileName
}

Sadece File(uri.path).nameçalışıyor, teşekkürler!
Sam Chen

17

Kısa olmasını istiyorsanız bu işe yaramalı.

Uri uri= data.getData();
File file= new File(uri.getPath());
file.getName();

1
File.getName () ile bir isim alıyorum ama gerçek adı değil
Tushar Thakur

1
My fileName user.jpg ama yanıt olarak "6550" alıyorum
Tushar Thakur

1
Gerçek dosya adını döndürmez. Aslında, kaynak URL'nizin son bölümünü döndürür.
Emdadul Sawon

Bu bir dosya olarak çalışmıyor! Çoğunlukla URI'lerin bugün MediaStore tarafından kodunun çözülmesi gerekir. Ben ve diğerlerinin rakamları almasının nedeni. Çünkü o dosyaların kimlikleri bunlar.
sud007

Bu 4 yıl önce verilen bir cevaptı. Android açıkça her sürümü değiştiriyor.
Samuel

7

Projemde Uri'den Dosya Adı ve Dosya Boyutu almak için aşağıdaki kodu kullanıyorum.

/**
 * Used to get file detail from uri.
 * <p>
 * 1. Used to get file detail (name & size) from uri.
 * 2. Getting file details from uri is different for different uri scheme,
 * 2.a. For "File Uri Scheme" - We will get file from uri & then get its details.
 * 2.b. For "Content Uri Scheme" - We will get the file details by querying content resolver.
 *
 * @param uri Uri.
 * @return file detail.
 */
public static FileDetail getFileDetailFromUri(final Context context, final Uri uri) {
    FileDetail fileDetail = null;
    if (uri != null) {
        fileDetail = new FileDetail();
        // File Scheme.
        if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
            File file = new File(uri.getPath());
            fileDetail.fileName = file.getName();
            fileDetail.fileSize = file.length();
        }
        // 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);
                int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
                fileDetail.fileName = returnCursor.getString(nameIndex);
                fileDetail.fileSize = returnCursor.getLong(sizeIndex);
                returnCursor.close();
            }
        }
    }
    return fileDetail;
}

/**
 * File Detail.
 * <p>
 * 1. Model used to hold file details.
 */
public static class FileDetail {

    // fileSize.
    public String fileName;

    // fileSize in bytes.
    public long fileSize;

    /**
     * Constructor.
     */
    public FileDetail() {

    }
}

Harici veya dahili verilerden dosya adları yükleme sorunumu çözmeye yardımcı oldu! Çok faydalı, teşekkürler!
Brandon

6

En yoğun versiyon:

public String getNameFromURI(Uri uri) {
    Cursor c = getContentResolver().query(uri, null, null, null, null);
    c.moveToFirst();
    return c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}

1
imleci kapatmayı unutmayın :)
BekaBot

5

Kotlin için şunun gibi bir şey kullanabilirsiniz:

object FileUtils {

   fun Context.getFileName(uri: Uri): String?
        = when (uri.scheme) {
            ContentResolver.SCHEME_FILE -> File(uri.path).name
            ContentResolver.SCHEME_CONTENT -> getCursorContent(uri)
            else -> null
        }

    private fun Context.getCursorContent(uri: Uri): String? 
        = try {
            contentResolver.query(uri, null, null, null, null)?.let { cursor ->
                cursor.run {
                    if (moveToFirst()) getString(getColumnIndex(OpenableColumns.DISPLAY_NAME))
                    else null
                }.also { cursor.close() }
            }
        } catch (e : Exception) { null }

İyi cevap, ancak bence bu eğlenceleri (Context.getFileName ve Context.getCursorContent) 'Context' adlı bir dosyaya eklemek, FileUtils sözcük nesnesini silmek ve uzantı olarak kullanmak daha iyidir, örneğin: val txtFileName = context.getFileName (uri)
Alexey Simchenko

@LumisD bu uzantıları herhangi bir dosyaya aktarabilirsiniz, öneriniz doğru ancak yöntemlerin küresel olmasından nefret ediyorum. Her zaman değil, yalnızca içe aktarırsam belirli yöntemleri görmek istiyorum. Ve bazen aynı yöntem adlarına ve farklı kaynaklara sahip olmayı tercih ederim, bu yüzden ihtiyacım olanları doğru yere ithal ediyorum :)
Tamim Attafi

Bu şekilde içe aktarabilirsiniz: com.example.FileUtils.getFileName'i içe aktarın veya sadece context.getFileName (uri) yazın ve Alt + Enter'a basın ve
IDE'niz

4
public String getFilename() 
{
/*  Intent intent = getIntent();
    String name = intent.getData().getLastPathSegment();
    return name;*/
    Uri uri=getIntent().getData();
    String fileName = null;
    Context context=getApplicationContext();
    String scheme = uri.getScheme();
    if (scheme.equals("file")) {
        fileName = uri.getLastPathSegment();
    }
    else if (scheme.equals("content")) {
        String[] proj = { MediaStore.Video.Media.TITLE };
        Uri contentUri = null;
        Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
        if (cursor != null && cursor.getCount() != 0) {
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE);
            cursor.moveToFirst();
            fileName = cursor.getString(columnIndex);
        }
    }
    return fileName;
}

bu benim durumumda işe yaramadı, ama Vasanth cevabı işe yaradı.
Nabzi

3

Dosya adına sahip olmak istiyorsanız, onu elde etmek için bu işlevi kullanıyorum. Ayrıca Google Drive dosya seçimleriyle de çalışır

public static String getFileName(Uri uri) {
    String result;

    //if uri is content
    if (uri.getScheme() != null && uri.getScheme().equals("content")) {
        Cursor cursor = global.getInstance().context.getContentResolver().query(uri, null, null, null, null);
        try {
            if (cursor != null && cursor.moveToFirst()) {
                //local filesystem
                int index = cursor.getColumnIndex("_data");
                if(index == -1)
                    //google drive
                    index = cursor.getColumnIndex("_display_name");
                result = cursor.getString(index);
                if(result != null)
                    uri = Uri.parse(result);
                else
                    return null;
            }
        } finally {
            cursor.close();
        }
    }

    result = uri.getPath();

    //get filename + ext of path
    int cut = result.lastIndexOf('/');
    if (cut != -1)
        result = result.substring(cut + 1);
    return result;
}

1
Cevabınız için teşekkürler, bu bana yardımcı oldu.
Sakhawat Hossain

2
String Fpath = getPath(this, uri) ;
File file = new File(Fpath);
String filename = file.getName();

4
Kodunuzun ne yaptığını biraz açıklayabilir misiniz? Bu şekilde cevabınız, bu sorunla karşılaşan diğer kullanıcılar için daha faydalı olacaktır. Teşekkürler
wmk

bazı nedenlerden dolayı bu lokumda işe yaramıyor. Sadece "ses ve bir numara" çıkışı
Alex

1

Cevabımın cevabı aslında @Stefan Haustein'a çok benziyor. Cevabı Android Geliştirici sayfasında Dosya Bilgilerini Alma sayfasında buldum ; Buradaki bilgiler, bu özel konu üzerinde Depolama Erişim Çerçevesi kılavuz sitesinden daha da yoğunlaştırılmıştır . Sorgunun sonucunda dosya adını içeren sütun dizini OpenableColumns.DISPLAY_NAME. Sütun dizinlerindeki diğer yanıtların / çözümlerin hiçbiri benim için işe yaramadı. Örnek fonksiyon aşağıdadır:

 /**
 * @param uri uri of file.
 * @param contentResolver access to server app.
 * @return the name of the file.
 */
def extractFileName(uri: Uri, contentResolver: ContentResolver): Option[String] = {

    var fileName: Option[String] = None
    if (uri.getScheme.equals("file")) {

        fileName = Option(uri.getLastPathSegment)
    } else if (uri.getScheme.equals("content")) {

        var cursor: Cursor = null
        try {

            // Query the server app to get the file's display name and size.
            cursor = contentResolver.query(uri, null, null, null, null)

            // Get the column indexes of the data in the Cursor,
            // move to the first row in the Cursor, get the data.
            if (cursor != null && cursor.moveToFirst()) {

                val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                fileName = Option(cursor.getString(nameIndex))
            }

        } finally {

            if (cursor != null) {
                cursor.close()
            }

        }

    }

    fileName
}

1

Öncelikle, URInesnenizi nesneye dönüştürmeniz URLve ardından Filebir dosya adı almak için nesne kullanmanız gerekir:

try
    {
        URL videoUrl = uri.toURL();
        File tempFile = new File(videoUrl.getFile());
        String fileName = tempFile.getName();
    }
    catch (Exception e)
    {

    }

İşte bu, çok kolay.


1

Xamarin / c # için Stefan Haustein işlevi:

public string GetFilenameFromURI(Android.Net.Uri uri)
        {
            string result = null;
            if (uri.Scheme == "content")
            {
                using (var cursor = Application.Context.ContentResolver.Query(uri, null, null, null, null))
                {
                    try
                    {
                        if (cursor != null && cursor.MoveToFirst())
                        {
                            result = cursor.GetString(cursor.GetColumnIndex(OpenableColumns.DisplayName));
                        }
                    }
                    finally
                    {
                        cursor.Close();
                    }
                }
            }
            if (result == null)
            {
                result = uri.Path;
                int cut = result.LastIndexOf('/');
                if (cut != -1)
                {
                    result = result.Substring(cut + 1);
                }
            }
            return result;
        }

1

Bu aslında benim için çalıştı:

private String uri2filename() {

    String ret;
    String scheme = uri.getScheme();

    if (scheme.equals("file")) {
        ret = uri.getLastPathSegment();
    }
    else if (scheme.equals("content")) {
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            ret = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
        }
   }
   return ret;
}

0

Lütfen şunu deneyin:

  private String displayName(Uri uri) {

             Cursor mCursor =
                     getApplicationContext().getContentResolver().query(uri, null, null, null, null);
             int indexedname = mCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
             mCursor.moveToFirst();
             String filename = mCursor.getString(indexedname);
             mCursor.close();
             return filename;
 }

0

Tüm cevapların kombinasyonu

Burada sunulan tüm yanıtların yanı sıra bazı Airgram'ın SDK'larında yaptıklarını okuduktan sonra ulaştığım şey: - Github'da açık kaynaklı bir yardımcı program:

https://github.com/mankum93/UriUtilsAndroid/tree/master/app/src/main/java/com/androiduriutils

Kullanım

Aramak kadar basit UriUtils.getDisplayNameSize(). İçeriğin hem adını hem de boyutunu sağlar.

Not: Yalnızca bir içerikle çalışır: // Uri

İşte koda bir bakış:

/**
 * References:
 * - https://www.programcreek.com/java-api-examples/?code=MLNO/airgram/airgram-master/TMessagesProj/src/main/java/ir/hamzad/telegram/MediaController.java
 * - /programming/5568874/how-to-extract-the-file-name-from-uri-returned-from-intent-action-get-content
 *
 * @author Manish@bit.ly/2HjxA0C
 * Created on: 03-07-2020
 */
public final class UriUtils {


    public static final int CONTENT_SIZE_INVALID = -1;

    /**
     * @param context context
     * @param contentUri content Uri, i.e, of the scheme <code>content://</code>
     * @return The Display name and size for content. In case of non-determination, display name
     * would be null and content size would be {@link #CONTENT_SIZE_INVALID}
     */
    @NonNull
    public static DisplayNameAndSize getDisplayNameSize(@NonNull Context context, @NonNull Uri contentUri){

        final String scheme = contentUri.getScheme();
        if(scheme == null || !scheme.equals(ContentResolver.SCHEME_CONTENT)){
            throw new RuntimeException("Only scheme content:// is accepted");
        }

        final DisplayNameAndSize displayNameAndSize = new DisplayNameAndSize();
        displayNameAndSize.size = CONTENT_SIZE_INVALID;

        String[] projection = new String[]{MediaStore.Images.Media.DATA, OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
        Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null);
        try {
            if (cursor != null && cursor.moveToFirst()) {

                // Try extracting content size

                int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
                if (sizeIndex != -1) {
                    displayNameAndSize.size = cursor.getLong(sizeIndex);
                }

                // Try extracting display name
                String name = null;

                // Strategy: The column name is NOT guaranteed to be indexed by DISPLAY_NAME
                // so, we try two methods
                int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                if (nameIndex != -1) {
                    name = cursor.getString(nameIndex);
                }

                if (nameIndex == -1 || name == null) {
                    nameIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
                    if (nameIndex != -1) {
                        name = cursor.getString(nameIndex);
                    }
                }
                displayNameAndSize.displayName = name;
            }
        }
        finally {
            if(cursor != null){
                cursor.close();
            }
        }

        // We tried querying the ContentResolver...didn't work out
        // Try extracting the last path segment
        if(displayNameAndSize.displayName == null){
            displayNameAndSize.displayName = contentUri.getLastPathSegment();
        }

        return displayNameAndSize;
    }
}
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.