Bagaimana cara mengekstrak nama file dari URI yang dikembalikan dari Intent.ACTION_GET_CONTENT?

101

Saya menggunakan pengelola file pihak ketiga untuk memilih file (PDF dalam kasus saya) dari sistem file.

Beginilah cara saya meluncurkan aktivitas:

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

Inilah yang saya miliki 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);
            }
        }
    }
}

Potongan kode dipinjam dari instruksi Open Intents File Manager yang tersedia di sini:
http://www.openintents.org/en/node/829

Tujuannya if-elseadalah kompatibilitas ke belakang. Saya ingin tahu apakah ini cara terbaik untuk mendapatkan nama file karena saya telah menemukan bahwa pengelola file lain mengembalikan semua jenis hal.

Misalnya, Documents ToGo mengembalikan sesuatu seperti berikut:

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

yang getContentResolver().query()kembali null.

Untuk membuatnya lebih menarik, pengelola file tanpa nama (saya mendapatkan URI ini dari log klien) mengembalikan sesuatu seperti:

/./sdcard/downloads/.bin


Apakah ada cara yang disukai untuk mengekstrak nama file dari URI atau harus menggunakan penguraian string?

Viktor Brešan
sumber
Pertanyaan yang sama dengan jawaban yang mungkin lebih baik: stackoverflow.com/questions/8646246/…
Rémi

Jawaban:

163

developer.android.com memiliki kode contoh yang bagus untuk ini: https://developer.android.com/guide/topics/providers/document-provider.html

Versi ringkas untuk mengekstrak nama file (dengan asumsi "ini" adalah Aktivitas):

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;
}
Stefan Haustein
sumber
jika Anda mencoba membaca mimeType atau fileName file dari kamera, pertama-tama Anda harus memberi tahu MediaScanner yang akan mengonversi URI file yang baru Anda buat file://ke content://dalam onScanCompleted(String path, Uri uri)metode stackoverflow.com/a/5815005/2163045
murt
10
Menambahkan new String[]{OpenableColumns.DISPLAY_NAME}sebagai parameter kedua ke kueri akan memfilter kolom untuk permintaan yang lebih efisien.
JM Lord
OpenableColumns.DISPLAY_NAMEtidak berhasil untuk saya, saya gunakan MediaStore.Files.FileColumns.TITLEsebagai gantinya.
Dmitry Kopytov
Ini akan crash untuk video saat membuat kursor dengan java.lang.IllegalArgumentException: Invalid column latitudesayangnya. Bekerja dengan sempurna untuk foto!
Lucas P.
45

Saya menggunakan sesuatu seperti ini:

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();
    }
}
Ken Fehling
sumber
4
Saya telah menjalankan beberapa tes pada kode Anda. Pada URI yang dikembalikan oleh OI File Manager IllegalArgumentException dilempar karena kolom 'judul' tidak ada. Pada URI yang dikembalikan oleh kursor Dokumen ToGo adalah null. Pada URI yang dikembalikan oleh skema pengelola file yang tidak dikenal adalah (jelas) null.
Viktor Brešan
2
Hmm menarik. Ya, pengujian untuk skema! = Null adalah ide yang bagus. Sebenarnya, menurut saya TITLE tidak sesuai dengan keinginan Anda. Saya menggunakan itu karena jenis media tertentu di Android (seperti lagu yang dipilih melalui music picker) memiliki URI seperti content: // media / external / audio / media / 78 dan saya ingin menampilkan sesuatu yang lebih relevan daripada nomor ID. Anda cukup menggunakan uri.getLastPathSegment () seperti yang digunakan kode saya untuk file: // URI jika Anda memiliki URI seperti konten: //...somefile.pdf
Ken Fehling
1
uri.getLastPathSegment (); - Anda telah menyelamatkan hari saya :)
Lumis
@Ken Feh: Ini tidak berhasil untuk saya. Ini berfungsi ketika saya mengklik file di browser file, tetapi ketika saya mengklik lampiran email, saya masih mendapatkan konten: // ... hal. Saya mencoba semua saran di sini tanpa hasil. Tahu kenapa?
Luis A. Florit
1
Hai, kenapa cursorbukan close()d?
fikr4n
34

Diambil dari Mengambil informasi File | Pengembang Android

Mengambil nama File.

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;
}
Bhargav
sumber
1
Aku sudah mencari ini terlalu lama!
dasfima
7
Terima kasih. Ya ampun, mengapa mereka harus membuat hal sepele seperti itu menjengkelkan?
TylerJames
1
Ini hanya bekerja dengan file konten. Lihat stackoverflow.com/a/25005243/6325722 untuk metode lengkap
Johnny Five
18

Cara termudah untuk mendapatkan nama file:

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

Jika mereka tidak memberi Anda nama yang tepat, Anda harus menggunakan:

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
}
Saya juga
sumber
Hanya File(uri.path).nameberfungsi, terima kasih!
Sam Chen
17

Jika Anda menginginkannya singkat, ini harus berhasil.

Uri uri= data.getData();
File file= new File(uri.getPath());
file.getName();
Samuel
sumber
1
Saya mendapatkan nama dengan file.getName () tetapi itu bukan nama sebenarnya
Tushar Thakur
1
FileName saya adalah user.jpg tetapi saya mendapatkan "6550" sebagai tanggapan
Tushar Thakur
1
Itu tidak mengembalikan nama file yang sebenarnya. Sebenarnya itu mengembalikan bagian terakhir dari url sumber daya Anda.
Emdadul Sawon
Ini tidak berfungsi sebagai file! Sebagian besar URI harus diterjemahkan oleh MediaStore hari ini. Alasan mengapa saya dan orang lain mendapatkan nomor tersebut. Karena itu adalah ID dari file-file itu.
sud007
Ini adalah tanggapan yang diberikan 4 tahun lalu. Android jelas berubah setiap rilis.
Samuel
7

Saya menggunakan kode di bawah ini untuk mendapatkan Nama File & Ukuran File dari Uri dalam proyek saya.

/**
 * 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() {

    }
}
Vasanth
sumber
Membantu memecahkan masalah saya dengan memuat nama file dari data eksternal atau internal! Sangat berguna terima kasih!
Brandon
6

Versi paling padat:

public String getNameFromURI(Uri uri) {
    Cursor c = getContentResolver().query(uri, null, null, null, null);
    c.moveToFirst();
    return c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
artdeell
sumber
1
jangan lupa tutup kursor :)
BekaBot
5

Untuk Kotlin, Anda dapat menggunakan sesuatu seperti ini:

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 }
Tamim Attafi
sumber
Jawaban bagus, tapi menurut saya lebih baik memasukkan kesenangan ini (Context.getFileName dan Context.getCursorContent) dalam file dengan nama 'Konteks', hapus kata-kata objek FileUtils dan gunakan sebagai ekstensi, misalnya: val txtFileName = konteks.getFileName (uri)
Alexey Simchenko
@LumisD Anda dapat mengimpor ekstensi ini dalam file apa pun, saran Anda benar tetapi saya benci jika metode bersifat global. Saya ingin melihat metode tertentu hanya jika saya mengimpornya dan tidak selalu. Dan kadang-kadang saya lebih suka memiliki nama metode yang sama dan sumber yang berbeda jadi saya mengimpor yang saya butuhkan di tempat yang tepat :)
Tamim Attafi
Anda dapat mengimpornya dengan cara ini: import com.example.FileUtils.getFileName atau cukup tulis konteks.getFileName (uri) dan tekan Alt + Enter dan IDE Anda akan melakukannya untuk Anda :)
Tamim Attafi
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;
}
Sujith Manjavana
sumber
ini tidak berhasil dalam kasus saya, tetapi jawaban Vasanth berhasil.
Nabzi
2
String Fpath = getPath(this, uri) ;
File file = new File(Fpath);
String filename = file.getName();
pengguna5233139
sumber
4
Bisakah Anda menjelaskan sedikit tentang fungsi kode Anda? Dengan cara ini, jawaban Anda akan lebih berguna bagi pengguna lain yang tersandung masalah ini. Terima kasih
wmk
untuk beberapa alasan ini tidak berhasil pada marshmellow. Itu hanya mengeluarkan "audio dan beberapa nomor"
Alex
1

Versi jawaban saya sebenarnya sangat mirip dengan @Stefan Haustein. Saya menemukan jawabannya di halaman Pengembang Android Mengambil Informasi File ; informasi di sini bahkan lebih ringkas tentang topik khusus ini daripada di situs panduan Storage Access Framework . Dalam hasil dari query indeks kolom yang berisi nama file adalah OpenableColumns.DISPLAY_NAME. Tidak ada jawaban / solusi lain untuk indeks kolom yang berhasil untuk saya. Di bawah ini adalah contoh fungsi:

 /**
 * @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
}
RenatoIvancic
sumber
1

Pertama, Anda perlu mengonversi URIobjek Anda menjadi URLobjek, lalu menggunakan Fileobjek untuk mengambil nama file:

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

    }

Itu dia, sangat mudah.

Ayaz Alifov
sumber
1

Fungsi Stefan Haustein untuk xamarin / c #:

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;
        }
Stefan Michev
sumber
1

Jika Anda ingin memiliki nama file dengan ekstensi, saya menggunakan fungsi ini untuk mendapatkannya. Ia juga bekerja dengan pengambilan file google drive

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;
}
save_jeff
sumber
1

Ini benar-benar berhasil untuk saya:

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;
}
Jose Manuel Gomez Alvarez
sumber
0

Silakan coba ini:

  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;
 }
Mamour Ndiaye
sumber
0

Kombinasi dari semua jawaban

Inilah yang saya dapatkan setelah membaca semua jawaban yang disajikan di sini serta apa yang telah dilakukan beberapa Airgram di SDK mereka - Sebuah utilitas yang saya buka bersumber di Github:

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

Pemakaian

Sesederhana menelepon UriUtils.getDisplayNameSize(),. Ini memberikan nama dan ukuran konten.

Catatan: Hanya bekerja dengan konten: // Uri

Berikut ini sekilas tentang kodenya:

/**
 * 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 [email protected]/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;
    }
}
Manish Kumar Sharma
sumber