Làm cách nào để trích xuất tên tệp từ URI được trả về từ Intent.ACTION_GET_CONTENT?


98

Tôi đang sử dụng trình quản lý tệp của bên thứ ba để chọn tệp (trong trường hợp của tôi là PDF) từ hệ thống tệp.

Đây là cách tôi khởi chạy hoạt động:

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

Đây là những gì tôi có trong 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);
            }
        }
    }
}

Đoạn mã được mượn từ hướng dẫn Open Intents File Manager có sẵn tại đây:
http://www.openintents.org/en/node/829

Mục đích của if-elselà khả năng tương thích ngược. Tôi tự hỏi liệu đây có phải là cách tốt nhất để lấy tên tệp không vì tôi đã phát hiện ra rằng các trình quản lý tệp khác trả lại tất cả mọi thứ.

Ví dụ, Documents ToGo trả về một cái gì đó như sau:

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

trên đó getContentResolver().query()trả về null.

Để làm cho mọi thứ thú vị hơn, trình quản lý tệp không tên (tôi nhận được URI này từ nhật ký ứng dụng khách) đã trả lại một cái gì đó như:

/./sdcard/downloads/.bin


Có cách nào ưu tiên hơn để trích xuất tên tệp từ URI hay cách nào nên dùng đến phân tích cú pháp chuỗi không?


Cùng một câu hỏi với câu trả lời có thể tốt hơn: stackoverflow.com/questions/8646246/…
Rémi

Câu trả lời:


159

developer.android.com có ​​mã ví dụ đẹp cho điều này: https://developer.android.com/guide/topics/providers/document-provider.html

Một phiên bản cô đọng để chỉ trích xuất tên tệp (giả sử "đây" là một Hoạt động):

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

trong trường hợp khi bạn cố gắng đọc MIMETYPE hoặc fileName của tập tin từ máy ảnh, trước hết bạn phải thông báo cho MediaScanner rằng sẽ chuyển đổi URI của tập tin vừa tạo của bạn từ file://đến content://trong onScanCompleted(String path, Uri uri)phương pháp stackoverflow.com/a/5815005/2163045
murt

10
Việc thêm new String[]{OpenableColumns.DISPLAY_NAME}dưới dạng tham số thứ hai vào truy vấn sẽ lọc các cột để có yêu cầu hiệu quả hơn.
JM Lord

OpenableColumns.DISPLAY_NAMEkhông làm việc cho tôi, tôi đã sử dụng MediaStore.Files.FileColumns.TITLEthay thế.
Dmitry Kopytov

Điều này sẽ gây ra lỗi cho video khi java.lang.IllegalArgumentException: Invalid column latitudekhông may tạo con trỏ . Mặc dù hoạt động hoàn hảo cho ảnh!
Lucas P.

44

Tôi đang sử dụng một cái gì đó như thế này:

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
Tôi đã chạy một số thử nghiệm trên mã của bạn. Trên URI do OI File Manager trả về, IllegalArgumentException được ném ra vì cột 'title' không tồn tại. Trên URI được kiểm tra bởi con trỏ Documents ToGo là trống. Trên URI được trả về bởi lược đồ trình quản lý tệp không xác định là (rõ ràng) là null.
Viktor Brešan

2
Hmm, thú vị. Vâng, thử nghiệm cho chương trình! = Null là một ý kiến ​​hay. Thực sự tôi không nghĩ TITLE là những gì bạn muốn. Tôi đang sử dụng phương tiện đó vì một số loại phương tiện nhất định trong Android (như các bài hát được chọn thông qua bộ chọn nhạc) có URI như nội dung: // media / external / audio / media / 78 và tôi muốn hiển thị nội dung nào đó phù hợp hơn số ID. Bạn chỉ có thể sử dụng uri.getLastPathSegment () như sử dụng mã của tôi cho file: // URI nếu bạn có một URI như nội dung: //...somefile.pdf
Ken dịch Fehling

1
uri.getLastPathSegment (); - bạn đã cứu ngày của tôi :)
Lumis

@Ken Fehling: Điều này không hiệu quả với tôi. Nó hoạt động khi tôi nhấp vào tệp trong trình duyệt tệp, nhưng khi tôi nhấp vào tệp đính kèm email, tôi vẫn nhận được nội dung: // ... thing. Tôi đã thử tất cả các đề xuất ở đây mà không gặp may. Bất kỳ ý tưởng tại sao?
Luis A. Florit

1
Chào, tại sao cursorkhông phải là close()d?
fikr4n

33

Lấy từ thông tin về tệp tin | Nhà phát triển Android

Truy xuất tên tệp.

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
Tôi đã tìm kiếm điều này quá lâu rồi!
dasfima

7
Cảm ơn bạn. Lạy chúa tại sao họ phải làm một chuyện tầm thường khó chịu như vậy?
TylerJames

1
Nó chỉ hoạt động với các tệp nội dung. Xem stackoverflow.com/a/25005243/6325722 cho phương pháp đầy đủ
Johnny Năm

17

Nếu bạn muốn nó ngắn, điều này sẽ hoạt động.

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

1
Tôi nhận được một tên với file.getName () nhưng tên không thực tế của nó
Tushar Thakur

1
My fileName là user.jpg nhưng tôi nhận được "6550" để đáp ứng
Tushar Thakur

1
Nó không trả về tên tệp thực. Trên thực tế, nó trả về phần cuối cùng của url tài nguyên của bạn.
Emdadul Sawon 25/09/17

Điều này không hoạt động như một tệp! Chủ yếu các URI phải được giải mã bởi MediaStore ngày nay. Lý do tại sao tôi và những người khác nhận được các con số. Vì đó là ID của các tệp đó.
sud007

Đây là một phản hồi được đưa ra 4 năm trước. Android rõ ràng đang thay đổi mọi bản phát hành.
Samuel

17

Cách dễ nhất để lấy tên tệp:

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

Nếu họ không cung cấp cho bạn tên phù hợp, bạn nên sử dụng:

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
}

7

Tôi sử dụng mã bên dưới để lấy Tên tệp và Kích thước tệp từ Uri trong dự án của mình.

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

    }
}

Đã giúp giải quyết vấn đề của tôi khi tải tên tệp từ dữ liệu bên ngoài hoặc bên trong! Rất hữu ích cảm ơn bạn!
Brandon

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

điều này không hoạt động trong trường hợp của tôi, nhưng câu trả lời Vasanth đã làm được.
Nabzi 18/09/18

4

Đối với Kotlin, bạn có thể sử dụng một cái gì đó như sau:

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 }

Câu trả lời tốt, nhưng theo ý kiến ​​của tôi, tốt hơn là nên chèn những niềm vui này (Context.getFileName và Context.getCursorContent) trong một tệp có tên 'Context', xóa các từ đối tượng FileUtils và sử dụng nó làm phần mở rộng, ví dụ: val txtFileName = context.getFileName (uri)
Alexey Simchenko

@LumisD bạn có thể nhập các phần mở rộng này vào bất kỳ tệp nào, đề xuất của bạn là đúng nhưng tôi chỉ ghét khi các phương thức là toàn cầu. Tôi chỉ muốn xem một số phương pháp nhất định nếu tôi nhập chúng và không phải lúc nào. Và đôi khi tôi thích có tên cùng một phương pháp và các nguồn khác nhau vì vậy tôi nhập khẩu những cái tôi cần ở đúng nơi :)
Tamim Attafi

Bạn có thể nhập nó theo cách này: import com.example.FileUtils.getFileName hoặc chỉ cần viết context.getFileName (uri) và nhấn Alt + Enter và IDE của bạn sẽ làm điều đó cho bạn :)
Tamim Attafi

4

Phiên bản cô đọng nhất:

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
đừng quên đóng con trỏ :)
BekaBot

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

4
Bạn có thể giải thích một chút mã của bạn làm gì không? Bằng cách này, câu trả lời của bạn sẽ hữu ích hơn cho những người dùng khác gặp phải vấn đề này. Cảm ơn
tuần

vì một số lý do điều này không hoạt động trên marshmellow. Nó chỉ xuất ra "âm thanh và một số số"
Alex

1

Phiên bản câu trả lời của tôi thực sự rất giống với @Stefan Haustein. Tôi đã tìm thấy câu trả lời trên trang Nhà phát triển Android Truy xuất thông tin tệp ; thông tin ở đây thậm chí còn cô đọng hơn về chủ đề cụ thể này hơn là trên trang web hướng dẫn của Storage Access Framework . Trong kết quả từ truy vấn, chỉ mục cột chứa tên tệp là OpenableColumns.DISPLAY_NAME. Không có câu trả lời / giải pháp nào khác cho chỉ mục cột phù hợp với tôi. Dưới đây là chức năng mẫu:

 /**
 * @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

Trước tiên, bạn cần chuyển đổi URIđối tượng của mình thành URLđối tượng, sau đó sử dụng Fileđối tượng để truy xuất tên tệp:

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

    }

Đó là nó, rất dễ dàng.


1

Hàm Stefan Haustein cho 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;
        }

1

Nếu bạn muốn có tên tệp với phần mở rộng, tôi sử dụng chức năng này để lấy nó. Nó cũng hoạt động với các lựa chọn tệp 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;
}

1

Điều này thực sự đã làm việc cho tôi:

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

Hãy thử cái này:

  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

Sự kết hợp của tất cả các câu trả lời

Đây là những gì tôi có được sau khi đọc tất cả các câu trả lời được trình bày ở đây cũng như những gì một số Airgram đã làm trong SDK của họ - Một tiện ích mà tôi đã mở nguồn trên Github:

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

Sử dụng

Đơn giản như cách gọi UriUtils.getDisplayNameSize(),. Nó cung cấp cả tên và kích thước của nội dung.

Lưu ý: Chỉ hoạt động với nội dung: // Uri

Đây là một cái nhìn thoáng qua về mã:

/**
 * 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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.