如何确定Android中文件的MIME类型?

220
假设我有一个文件的完整路径,如:(/sdcard/tlogo.png)。我想知道它的MIME类型。
我为此创建了一个函数。
public static String getMimeType(File file, Context context)    
{
    Uri uri = Uri.fromFile(file);
    ContentResolver cR = context.getContentResolver();
    MimeTypeMap mime = MimeTypeMap.getSingleton();
    String type = mime.getExtensionFromMimeType(cR.getType(uri));
    return type;
}

但是当我调用它时,它返回null。

File file = new File(filePath);
String fileType=CommonFunctions.getMimeType(file, context);
28个回答

371

首先,您应该考虑调用 MimeTypeMap#getMimeTypeFromExtension() 方法,如下所示:

// url = file path or whatever suitable URL you want.
public static String getMimeType(String url) {
    String type = null;
    String extension = MimeTypeMap.getFileExtensionFromUrl(url);
    if (extension != null) {
        type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
    }
    return type;
}

4
感谢你为我工作,并且我的问题有另一个解决方案:public static String getMimeType(String path, Context context) { String extention = path.substring(path.lastIndexOf(".") ); String mimeTypeMap = MimeTypeMap.getFileExtensionFromUrl(extention); String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(mimeTypeMap); return mimeType; }该函数用于获取指定文件路径的MIME类型,需要传入文件路径和上下文Context对象作为参数。它首先获取文件扩展名并使用MimeTypeMap类来查找该扩展名对应的MIME类型。最后返回MIME类型字符串。 - Sushant Bhatnagar
21
如果文件扩展名不正确怎么办?一个人可以制作一个.mp3文件并将文本放入其中。 - throrin19
2
然后,您需要实际打开文件并检查引导中的魔数(根据格式)-但是,这也假定将txt文件重命名为mp3的人没有在其文本开头写入“ID3”以使您更加困惑。 - Jens
3
这是不可靠的。仅仅依赖文件扩展名来确定文件类型是很糟糕的做法。我无法相信像@throrin19所提到的这样一个重要的事情被忽视了。 - Ankan-Zerob
6
如果您的文件路径为/storage/emulated/0/Download/images (4).jpg,并且您请求mimetype,则会返回null。 - Kalaiselvan
显示剩余9条评论

231

检测任何文件的MIME类型

public String getMimeType(Uri uri) {           
    String mimeType = null;
    if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
        ContentResolver cr = getAppContext().getContentResolver();
        mimeType = cr.getType(uri);
    } else {
        String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
                .toString());
        mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
                fileExtension.toLowerCase());
    }
    return mimeType;
}

6
这应该是正确答案。toLowerCase() 起了作用。 - Rajat Saxena
4
这绝对是最好的答案,但如果文件名包含特殊字符,例如我的文件名是 Don't go baby.mp3 中的一个 apostrophe,它就会失败得很惨!MimeTypeMap.getFileExtensionFromUrl() 内部的 PatternMatcher 无法处理这个文件名,并返回空字符串。结果是 mimetype 字符串为 null! - sud007
下面有一个类似但没有错误的解决方案,由I. Shvedenenko提供。它解决了我上面指出的问题。但是这个答案指向了正确的方向。 - sud007
1
@Abdul,这是因为该路径没有Uri方案。它应该以file://content://开头。 - HB.
为什么这里需要使用ContentResolver?我认为文件扩展名应该在Uri中清晰明了,不是吗? - Minh Nghĩa
显示剩余2条评论

37

上面的MimeTypeMap解决方案在我的使用中返回了null。这种方法行得通,而且更简单:

Uri uri = Uri.fromFile(file);
ContentResolver cR = context.getContentResolver();
String mime = cR.getType(uri);

23
此方法也可能返回 null。 - Mister Smith
1
看起来它可以根据URI返回null或非null。似乎没有人知道为什么 ;-( - Thomas Decaux
14
如果从安卓图库中选择媒体,则返回“TYPE”。如果从文件管理器中选择,则返回 null。 - Rahul Rastogi
我可以肯定地确认Rahul所说的,我刚测试了一下,他是正确的。 - Chris
在某些设备上,如果我从图库中选择图像,它也会返回 null。 - GS Nayma
@KalpeshPatel 说得对,文档中也提到了。 - Sarthak Mittal

30

这是一个优化版的Jens'答案,具有空安全和回退类型。

@NonNull
static String getMimeType(@NonNull File file) {
    String type = null;
    final String url = file.toString();
    final String extension = MimeTypeMap.getFileExtensionFromUrl(url);
    if (extension != null) {
        type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
    }
    if (type == null) {
        type = "image/*"; // fallback type. You might set it to */*
    }
    return type;
}

重要提示:getFileExtensionFromUrl() 只适用于小写字母!

更新(2018年3月19日)

奖励:上述方法可作为更简洁的Kotlin扩展函数

fun File.getMimeType(fallback: String = "image/*"): String {
    return MimeTypeMap.getFileExtensionFromUrl(toString())
            ?.run { MimeTypeMap.getSingleton().getMimeTypeFromExtension(toLowerCase()) }
            ?: fallback // You might set it to */*
}

1
这个可以工作。但对我来说有点啰嗦了。 - filthy_wizard
当然,这可以用更短的方式编写。使用 Kotlin 可能只需要两三行代码,但重点在于空值安全和 toLoverCase 部分。 - Tobias
@filthy_wizard,我为你添加了一个优化版本;-) - Tobias
对我来说,使用getPath()方法而不是toString()更清晰,只需使用Kotlin属性访问语法即可获取路径。 - Leonid
我很欣赏你的细致,但是回退类型违背了初衷。如果在失败时设置任意值,为什么还要检查呢? - Abandoned Cart

16
File file = new File(path, name);

    MimeTypeMap mime = MimeTypeMap.getSingleton();
    int index = file.getName().lastIndexOf('.')+1;
    String ext = file.getName().substring(index).toLowerCase();
    String type = mime.getMimeTypeFromExtension(ext);

    intent.setDataAndType(Uri.fromFile(file), type);
    try
    {
      context.startActivity(intent);
    }
    catch(ActivityNotFoundException ex)
    {
        ex.printStackTrace();

    }

1
对我来说完美运行!但是,使用FilenameUtils.getExtension(path)获取文件扩展名怎么样? - peterb

9
在Kotlin中,这个问题更加简单。
解决方法1:
fun getMimeType(file: File): String? = 
    MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.extension)

解决方案二:(文件扩展名功能)
fun File.mimeType(): String? = 
    MimeTypeMap.getSingleton().getMimeTypeFromExtension(this.extension)

7

请特别注意umerk44上的solutiongetMimeTypeFromExtension 调用 guessMimeTypeTypeFromExtension 并且区分大小写。我花了一个下午才发现 - 如果你传递 "JPG",getMimeTypeFromExtension 将返回 NULL,而如果你传递 "jpg",它将返回 "image/jpeg"。


6

我尝试使用标准方法来确定MIME类型,但是使用MimeTypeMap.getFileExtensionFromUrl(uri.getPath())无法保留文件扩展名。该方法返回了一个空字符串。因此,我想出了一个不太简单的解决方案来保留文件扩展名。

这里是返回文件扩展名的方法:

private String getExtension(String fileName){
    char[] arrayOfFilename = fileName.toCharArray();
    for(int i = arrayOfFilename.length-1; i > 0; i--){
        if(arrayOfFilename[i] == '.'){
            return fileName.substring(i+1, fileName.length());
        }
    }
    return "";
}

保留文件扩展名,可以通过以下方式获取MIME类型:
public String getMimeType(File file) {
    String mimeType = "";
    String extension = getExtension(file.getName());
    if (MimeTypeMap.getSingleton().hasExtension(extension)) {
        mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
    }
    return mimeType;
}

这个人完美地回答了这个问题中所有的答案!我也遇到了所有旧版类的同样问题。问题与你所提到的一样,MimeTypeMap.getFileExtensionFromUrl对于文件名中包含内部实现PatternMatcher不支持的字符的情况返回空字符串。这个方法完全解决了我的问题!我很喜欢它,目前没有任何问题! - sud007
@I. Shvedenenko String extension = file.getName().split("\\.")[1] 也可以工作,从而消除了 getExtension() 方法。 - Shahood ul Hassan
但问题是,如果在创建文件时使用了错误的扩展名怎么办?扩展名应该是 MIME 类型的唯一判断标准吗? - Shahood ul Hassan
看起来如果文件名中有多个 .,那么你在这个解决方案上遇到了问题。在 Kotlin 中,val extension: String = file.name.split(".").last() 可以消除对 getExtension 和多个 . 的需要。 - lasec0203
如果您仍在使用Java:file.getName().substring(file.getName().lastIndexOf(".")) - Carlos Cabello Ruiz
"fileName.length()" 是多余的。 "substring" 定义:返回一个字符串,它是此字符串的子字符串。 子字符串从指定索引处的字符开始,并延伸到此字符串的末尾。 - Rajat Sharma

6

这是我在我的Android应用程序中使用的解决方案:

public static String getMimeType(String url)
    {
        String extension = url.substring(url.lastIndexOf("."));
        String mimeTypeMap = MimeTypeMap.getFileExtensionFromUrl(extension);
        String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(mimeTypeMap);
        return mimeType;
    }

2
url是什么?很多url没有扩展名。例如,这个页面没有扩展名。对于url,您应该使用new URL(url)。openConnection()。getRequestProperty(“Content-type”); - barwnikk

6

编辑

我为此创建了一个小型库。 但底层代码几乎相同。

它可以在GitHub上获得

MimeMagic-Android

2020年9月的解决方案

使用Kotlin语言

fun File.getMimeType(context: Context): String? {
    if (this.isDirectory) {
        return null
    }

    fun fallbackMimeType(uri: Uri): String? {
        return if (uri.scheme == ContentResolver.SCHEME_CONTENT) {
            context.contentResolver.getType(uri)
        } else {
            val extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString())
            MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase(Locale.getDefault()))
        }
    }

    fun catchUrlMimeType(): String? {
        val uri = Uri.fromFile(this)

        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val path = Paths.get(uri.toString())
            try {
                Files.probeContentType(path) ?: fallbackMimeType(uri)
            } catch (ignored: IOException) {
                fallbackMimeType(uri)
            }
        } else {
            fallbackMimeType(uri)
        }
    }

    val stream = this.inputStream()
    return try {
        URLConnection.guessContentTypeFromStream(stream) ?: catchUrlMimeType()
    } catch (ignored: IOException) {
        catchUrlMimeType()
    } finally {
        stream.close()
    }
}

这似乎是最好的选择,因为它结合了先前的答案。

首先,它尝试使用URLConnection.guessContentTypeFromStream获取类型,但如果失败或返回null,则在Android O及以上版本上尝试获取MIME类型。

java.nio.file.Files
java.nio.file.Paths

否则,如果Android版本低于O或方法失败,则使用ContentResolver和MimeTypeMap返回类型。

1
关闭 InputStream - Jemshit Iskenderov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接