如何使用 Java 从文件中获取媒体类型(MIME 类型)?到目前为止,我已经尝试过 JMimeMagic 和 Mime-Util。第一个给了我内存异常,第二个没有正确关闭它的流。
您将如何探测文件以确定其实际类型(不仅仅是基于扩展名)?
很遗憾,
mimeType = file.toURL().openConnection().getContentType();
不起作用,因为这种 URL 的使用会导致文件被锁定,例如,它是不可删除的。
但是,你有这个:
mimeType= URLConnection.guessContentTypeFromName(file.getName());
还有以下内容,它的优点是超越了仅仅使用文件扩展名,并查看了内容
InputStream is = new BufferedInputStream(new FileInputStream(file));
mimeType = URLConnection.guessContentTypeFromStream(is);
//...close stream
但是,正如上面的评论所建议的,内置的 mime 类型表非常有限,不包括例如 MSWord 和 PDF。所以,如果你想概括,你需要超越内置库,使用例如 Mime-Util(这是一个很棒的库,同时使用文件扩展名和内容)。
FileInputStream
包装到 BufferedInputStream
是关键部分 - 否则 guessContentTypeFromStream
返回 null
(传递的 InputStream
实例应该支持标记)
URLConnection
的内容类型非常有限,它确实可以识别。例如,它无法检测 application/pdf
。
guessContentTypeFromName()
使用默认的 $JAVA_HOME/lib/content-types.properties
文件。您可以通过更改系统属性 System.setProperty("content.types.user.table","/lib/path/to/your/property/file");
添加您自己的扩展文件
使用 Apache Tika,您只需要 三行代码:
File file = new File("/path/to/file");
Tika tika = new Tika();
System.out.println(tika.detect(file));
如果您有一个 groovy 控制台,只需粘贴并运行此代码即可使用它:
@Grab('org.apache.tika:tika-core:1.14')
import org.apache.tika.Tika;
def tika = new Tika()
def file = new File("/path/to/file")
println tika.detect(file)
请记住,它的 API 很丰富,它可以解析“任何东西”。从 tika-core 1.14 开始,您拥有:
String detect(byte[] prefix)
String detect(byte[] prefix, String name)
String detect(File file)
String detect(InputStream stream)
String detect(InputStream stream, Metadata metadata)
String detect(InputStream stream, String name)
String detect(Path path)
String detect(String name)
String detect(URL url)
有关详细信息,请参阅 the apidocs。
new Tika().detect(file.toPath())
用于基于文件扩展名的检测,而不是基于文件内容的检测
new Tika().detect(file.getPath())
,它只使用文件扩展名
JAF API 是 JDK 6 的一部分。查看 javax.activation
包。
最有趣的类是 javax.activation.MimeType
- 一个实际的 MIME 类型持有者 - 和 javax.activation.MimetypesFileTypeMap
- 其实例可以将 MIME 类型解析为文件的 String 的类:
String fileName = "/path/to/file";
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
// only by file name
String mimeType = mimeTypesMap.getContentType(fileName);
// or by actual File instance
File file = new File(fileName);
mimeType = mimeTypesMap.getContentType(file);
getContentType(File)
的 javadoc 所述:返回文件对象的 MIME 类型。此类中的实现调用 getContentType(f.getName())
。
MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file)
跳过创建新对象
application/octet-stream
)。
Apache Tika 在 tika-core 中提供基于流前缀中的魔术标记的 mime 类型检测。 tika-core
不获取其他依赖项,这使其与当前未维护的 Mime Type Detection Utility 一样轻量级。
使用变量 theInputStream
和 theFileName
的简单代码示例 (Java 7)
try (InputStream is = theInputStream;
BufferedInputStream bis = new BufferedInputStream(is);) {
AutoDetectParser parser = new AutoDetectParser();
Detector detector = parser.getDetector();
Metadata md = new Metadata();
md.add(Metadata.RESOURCE_NAME_KEY, theFileName);
MediaType mediaType = detector.detect(bis, md);
return mediaType.toString();
}
请注意,不能直接使用 MediaType.detect(...)
(TIKA-1120)。 https://tika.apache.org/1.24/detection.html 提供了更多提示。
Metadata.RESOURCE_NAME_KEY
也可以省略(如果您没有或不能依赖原始名称),但在这种情况下,在某些情况下您会得到错误的结果(例如办公文件)。
如果您是 Android 开发人员,则可以使用实用程序类 android.webkit.MimeTypeMap
,它将 MIME 类型映射到文件扩展名,反之亦然。
以下代码段可能会对您有所帮助。
private static String getMimeType(String fileUrl) {
String extension = MimeTypeMap.getFileExtensionFromUrl(fileUrl);
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
从 roseindia:
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String mimeType = fileNameMap.getContentTypeFor("alert.gif");
如果您被 java 5-6 所困,那么 servoy open source product 中的这个实用程序类。
你只需要这个功能
public static String getContentType(byte[] data, String name)
它探测内容的第一个字节并根据该内容而不是文件扩展名返回内容类型。
我只是想知道大多数人如何从 Java 文件中获取 mime 类型?
我已经发布了我的 SimpleMagic Java 包,它允许从文件和字节数组中确定内容类型(mime 类型)。它旨在读取和运行 Unix file(1) 命令魔法文件,这些文件是大多数 ~Unix OS 配置的一部分。
我尝试了 Apache Tika,但它巨大 有大量依赖项,URLConnection
不使用文件的字节,MimetypesFileTypeMap
也只查看文件名。
使用 SimpleMagic,您可以执行以下操作:
// create a magic utility using the internal magic file
ContentInfoUtil util = new ContentInfoUtil();
// if you want to use a different config file(s), you can load them by hand:
// ContentInfoUtil util = new ContentInfoUtil("/etc/magic");
...
ContentInfo info = util.findMatch("/tmp/upload.tmp");
// or
ContentInfo info = util.findMatch(inputStream);
// or
ContentInfo info = util.findMatch(contentByteArray);
// null if no match
if (info != null) {
String mimeType = info.getMimeType();
}
用我的 5 美分筹码:
总长,博士
我使用 MimetypesFileTypeMap 并将任何不存在且我特别需要的 mime 添加到 mime.types 文件中。
现在,长读:
首先,MIME 类型列表是巨大的,见这里:https://www.iana.org/assignments/media-types/media-types.xhtml
我喜欢先使用JDK提供的标准工具,如果不行,我会去找别的东西。
根据文件扩展名确定文件类型
从 1.6 开始,Java 具有 MimetypesFileTypeMap,正如上面的答案之一所指出的那样,它是确定 mime 类型的最简单方法:
new MimetypesFileTypeMap().getContentType( fileName );
在它的普通实现中,这并没有多大作用(即它适用于.html,但不适用于.png)。但是,添加您可能需要的任何内容类型都非常简单:
在项目的 META-INF 文件夹中创建名为“mime.types”的文件为您需要的每种 mime 类型添加一行,默认实现不提供(有数百种 mime 类型,随着时间的推移,列表会增加)。
png 和 js 文件的示例条目为:
image/png png PNG
application/javascript js
对于 mime.types 文件格式,请在此处查看更多详细信息:https://docs.oracle.com/javase/7/docs/api/javax/activation/MimetypesFileTypeMap.html
从文件内容确定文件类型
从 1.7 开始,Java 有 java.nio.file.spi.FileTypeDetector,它定义了一个标准 API,用于以实现特定的方式确定文件类型。
要获取文件的 mime 类型,您只需使用 Files 并在代码中执行此操作:
Files.probeContentType(Paths.get("either file name or full path goes here"));
API 定义提供了支持从文件名或文件内容(魔术字节)确定文件 mime 类型的工具。这就是 probeContentType() 方法抛出 IOException 的原因,以防此 API 的实现使用提供给它的 Path 来实际尝试打开与其关联的文件。
同样,这个(JDK 附带的)的普通实现还有很多不足之处。
在遥远的星系中的某个理想世界中,所有这些试图解决这个文件到mime-type问题的库都将简单地实现java.nio.file.spi.FileTypeDetector,您可以将首选实现库的jar文件放入您的类路径中,然后会的。
在现实世界中,您需要 TL、DR 部分,您应该找到名称旁边有最多星号的库并使用它。对于这种特殊情况,我不需要一个(还;))。
简单和最佳选项从文件位置检索内容 mime 类型。
使用这个进口
import java.nio.file.Files;
import java.nio.file.Path;
代码
String type = Files.probeContentType(Path.of(imagePath));
我尝试了几种方法来做到这一点,包括@Joshua Fox 所说的第一种方法。但是有些人无法识别常见的模仿类型,例如 PDF 文件,而其他人则无法信任假文件(我尝试使用扩展名更改为 TIF 的 RAR 文件)。正如@Joshua Fox 以肤浅的方式所说,我找到的解决方案是使用MimeUtil2,如下所示:
MimeUtil2 mimeUtil = new MimeUtil2();
mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
String mimeType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file)).toString();
这是我发现的最简单的方法:
byte[] byteArray = ...
InputStream is = new BufferedInputStream(new ByteArrayInputStream(byteArray));
String mimeType = URLConnection.guessContentTypeFromStream(is);
阿帕奇蒂卡。
<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-parsers -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers</artifactId>
<version>1.24</version>
</dependency>
和两行代码。
Tika tika=new Tika();
tika.detect(inputStream);
下面的截图
https://i.stack.imgur.com/zGTrB.png
文件上传最好使用两层验证。
首先,您可以检查 mimeType 并对其进行验证。
其次,您应该将文件的前 4 个字节转换为十六进制,然后将其与幻数进行比较。那么这将是检查文件验证的一种非常安全的方法。
您只需一行即可完成:MimetypesFileTypeMap().getContentType(new File("filename.ext"))。查看完整的测试代码(Java 7):
import java.io.File;
import javax.activation.MimetypesFileTypeMap;
public class MimeTest {
public static void main(String a[]){
System.out.println(new MimetypesFileTypeMap().getContentType(
new File("/path/filename.txt")));
}
}
此代码产生以下输出: text/plain
如果您正在使用 Servlet,并且您可以使用 Servlet 上下文,则可以使用:
getServletContext().getMimeType( fileName );
getServletContext
是什么?
我找不到任何可以检查 video/mp4
MIME 类型的内容,因此我制定了自己的解决方案。我碰巧观察到 Wikipedia 是错误的,并且 00 00 00 18 66 74 79 70 69 73 6F 6D
文件签名不正确。第四个字节 (18
) 和所有 70
(排除) 在其他有效的 mp4
文件中发生了很大变化。
此代码本质上是 URLConnection.guessContentTypeFromStream
代码的复制/粘贴,但针对 video/mp4
量身定制。
BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(content));
String mimeType = URLConnection.guessContentTypeFromStream(bis);
// Goes full barbaric and processes the bytes manually
if (mimeType == null){
// These ints converted in hex ar:
// 00 00 00 18 66 74 79 70 69 73 6F 6D
// which are the file signature (magic bytes) for .mp4 files
// from https://www.wikiwand.com/en/List_of_file_signatures
// just ctrl+f "mp4"
int[] mp4_sig = {0, 0, 0, 24, 102, 116, 121, 112};
bis.reset();
bis.mark(16);
int[] firstBytes = new int[8];
for (int i = 0; i < 8; i++) {
firstBytes[i] = bis.read();
}
// This byte doesn't matter for the file signature and changes
mp4_sig[3] = content[3];
bis.reset();
if (Arrays.equals(firstBytes, mp4_sig)){
mimeType = "video/mp4";
}
}
针对 10 个不同的 .mp4
文件成功测试。
编辑:Here is a useful link (if it is still online) where you can find samples of many types。我不拥有这些视频,也不知道谁拥有,但它们对于测试上述代码很有用。
实际上,Apache Tika detector Tika.detect(File)
是最好的选择,并且比 Files.probeContentType(path)
更准确。
检查此 great quick reference,其中包含示例和代码示例。
在 Spring MultipartFile 文件中;
org.springframework.web.multipart.MultipartFile
file.getContentType();
检测文件的 Media Type1 的解决方案包含以下部分:
文件签名列表(参见 Kessler 的列表、维基百科的列表和 Space Maker 的列表)
媒体类型列表
媒体类型到文件扩展名的映射
将文件签名与 File、Path 或 InputStream 数据源进行比较
如果您复制代码,请记住给予信用。
StreamMediaType.java
在下面的代码中,-1
表示跳过比较该索引处的字节; -2
表示文件类型签名的结尾。这会检测二进制格式,主要是图像和一些纯文本格式变体(HTML、SVG、XML)。该代码最多使用数据源标头中的前 11 个“魔术”字节。欢迎进行缩短逻辑的优化和改进。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.keenwrite.io.MediaType.*;
import static java.lang.System.arraycopy;
public class StreamMediaType {
private static final int FORMAT_LENGTH = 11;
private static final int END_OF_DATA = -2;
private static final Map<int[], MediaType> FORMAT = new LinkedHashMap<>();
static {
//@formatter:off
FORMAT.put( ints( 0x3C, 0x73, 0x76, 0x67, 0x20 ), IMAGE_SVG_XML );
FORMAT.put( ints( 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A ), IMAGE_PNG );
FORMAT.put( ints( 0xFF, 0xD8, 0xFF, 0xE0 ), IMAGE_JPEG );
FORMAT.put( ints( 0xFF, 0xD8, 0xFF, 0xEE ), IMAGE_JPEG );
FORMAT.put( ints( 0xFF, 0xD8, 0xFF, 0xE1, -1, -1, 0x45, 0x78, 0x69, 0x66, 0x00 ), IMAGE_JPEG );
FORMAT.put( ints( 0x49, 0x49, 0x2A, 0x00 ), IMAGE_TIFF );
FORMAT.put( ints( 0x4D, 0x4D, 0x00, 0x2A ), IMAGE_TIFF );
FORMAT.put( ints( 0x47, 0x49, 0x46, 0x38 ), IMAGE_GIF );
FORMAT.put( ints( 0x8A, 0x4D, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A ), VIDEO_MNG );
FORMAT.put( ints( 0x25, 0x50, 0x44, 0x46, 0x2D, 0x31, 0x2E ), APP_PDF );
FORMAT.put( ints( 0x38, 0x42, 0x50, 0x53, 0x00, 0x01 ), IMAGE_PHOTOSHOP );
FORMAT.put( ints( 0x25, 0x21, 0x50, 0x53, 0x2D, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x2D ), APP_EPS );
FORMAT.put( ints( 0x25, 0x21, 0x50, 0x53 ), APP_PS );
FORMAT.put( ints( 0xFF, 0xFB, 0x30 ), AUDIO_MP3 );
FORMAT.put( ints( 0x49, 0x44, 0x33 ), AUDIO_MP3 );
FORMAT.put( ints( 0x3C, 0x21 ), TEXT_HTML );
FORMAT.put( ints( 0x3C, 0x68, 0x74, 0x6D, 0x6C ), TEXT_HTML );
FORMAT.put( ints( 0x3C, 0x68, 0x65, 0x61, 0x64 ), TEXT_HTML );
FORMAT.put( ints( 0x3C, 0x62, 0x6F, 0x64, 0x79 ), TEXT_HTML );
FORMAT.put( ints( 0x3C, 0x48, 0x54, 0x4D, 0x4C ), TEXT_HTML );
FORMAT.put( ints( 0x3C, 0x48, 0x45, 0x41, 0x44 ), TEXT_HTML );
FORMAT.put( ints( 0x3C, 0x42, 0x4F, 0x44, 0x59 ), TEXT_HTML );
FORMAT.put( ints( 0x3C, 0x3F, 0x78, 0x6D, 0x6C, 0x20 ), TEXT_XML );
FORMAT.put( ints( 0xFE, 0xFF, 0x00, 0x3C, 0x00, 0x3f, 0x00, 0x78 ), TEXT_XML );
FORMAT.put( ints( 0xFF, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00 ), TEXT_XML );
FORMAT.put( ints( 0x42, 0x4D ), IMAGE_BMP );
FORMAT.put( ints( 0x23, 0x64, 0x65, 0x66 ), IMAGE_X_BITMAP );
FORMAT.put( ints( 0x21, 0x20, 0x58, 0x50, 0x4D, 0x32 ), IMAGE_X_PIXMAP );
FORMAT.put( ints( 0x2E, 0x73, 0x6E, 0x64 ), AUDIO_BASIC );
FORMAT.put( ints( 0x64, 0x6E, 0x73, 0x2E ), AUDIO_BASIC );
FORMAT.put( ints( 0x52, 0x49, 0x46, 0x46 ), AUDIO_WAV );
FORMAT.put( ints( 0x50, 0x4B ), APP_ZIP );
FORMAT.put( ints( 0x41, 0x43, -1, -1, -1, -1, 0x00, 0x00, 0x00, 0x00, 0x00 ), APP_ACAD );
FORMAT.put( ints( 0xCA, 0xFE, 0xBA, 0xBE ), APP_JAVA );
FORMAT.put( ints( 0xAC, 0xED ), APP_JAVA_OBJECT );
//@formatter:on
}
private StreamMediaType() {
}
public static MediaType getMediaType( final Path path ) throws IOException {
return getMediaType( path.toFile() );
}
public static MediaType getMediaType( final java.io.File file )
throws IOException {
try( final var fis = new FileInputStream( file ) ) {
return getMediaType( fis );
}
}
public static MediaType getMediaType( final InputStream is )
throws IOException {
final var input = new byte[ FORMAT_LENGTH ];
final var count = is.read( input, 0, FORMAT_LENGTH );
if( count > 1 ) {
final var available = new byte[ count ];
arraycopy( input, 0, available, 0, count );
return getMediaType( available );
}
return UNDEFINED;
}
public static MediaType getMediaType( final byte[] data ) {
assert data != null;
final var source = new int[]{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
for( int i = 0; i < data.length; i++ ) {
source[ i ] = data[ i ] & 0xFF;
}
for( final var key : FORMAT.keySet() ) {
int i = -1;
boolean matches = true;
while( ++i < FORMAT_LENGTH && key[ i ] != END_OF_DATA && matches ) {
matches = key[ i ] == source[ i ] || key[ i ] == -1;
}
if( matches ) {
return FORMAT.get( key );
}
}
return UNDEFINED;
}
private static int[] ints( final int... data ) {
final var magic = new int[ FORMAT_LENGTH ];
int i = -1;
while( ++i < data.length ) {
magic[ i ] = data[ i ];
}
while( i < FORMAT_LENGTH ) {
magic[ i++ ] = END_OF_DATA;
}
return magic;
}
}
媒体类型.java
根据 IANA Media Type 列表定义文件格式。请注意,文件扩展名映射在 MediaTypeExtension
中。 getExtension
函数依赖于 Apache 的 FilenameUtils 类。
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import static MediaType.TypeName.*;
import static MediaTypeExtension.getMediaType;
import static org.apache.commons.io.FilenameUtils.getExtension;
public enum MediaType {
APP_ACAD( APPLICATION, "acad" ),
APP_JAVA_OBJECT( APPLICATION, "x-java-serialized-object" ),
APP_JAVA( APPLICATION, "java" ),
APP_PS( APPLICATION, "postscript" ),
APP_EPS( APPLICATION, "eps" ),
APP_PDF( APPLICATION, "pdf" ),
APP_ZIP( APPLICATION, "zip" ),
FONT_OTF( "otf" ),
FONT_TTF( "ttf" ),
IMAGE_APNG( "apng" ),
IMAGE_ACES( "aces" ),
IMAGE_AVCI( "avci" ),
IMAGE_AVCS( "avcs" ),
IMAGE_BMP( "bmp" ),
IMAGE_CGM( "cgm" ),
IMAGE_DICOM_RLE( "dicom_rle" ),
IMAGE_EMF( "emf" ),
IMAGE_EXAMPLE( "example" ),
IMAGE_FITS( "fits" ),
IMAGE_G3FAX( "g3fax" ),
IMAGE_GIF( "gif" ),
IMAGE_HEIC( "heic" ),
IMAGE_HEIF( "heif" ),
IMAGE_HEJ2K( "hej2k" ),
IMAGE_HSJ2( "hsj2" ),
IMAGE_X_ICON( "x-icon" ),
IMAGE_JLS( "jls" ),
IMAGE_JP2( "jp2" ),
IMAGE_JPEG( "jpeg" ),
IMAGE_JPH( "jph" ),
IMAGE_JPHC( "jphc" ),
IMAGE_JPM( "jpm" ),
IMAGE_JPX( "jpx" ),
IMAGE_JXR( "jxr" ),
IMAGE_JXRA( "jxrA" ),
IMAGE_JXRS( "jxrS" ),
IMAGE_JXS( "jxs" ),
IMAGE_JXSC( "jxsc" ),
IMAGE_JXSI( "jxsi" ),
IMAGE_JXSS( "jxss" ),
IMAGE_KTX( "ktx" ),
IMAGE_KTX2( "ktx2" ),
IMAGE_NAPLPS( "naplps" ),
IMAGE_PNG( "png" ),
IMAGE_PHOTOSHOP( "photoshop" ),
IMAGE_SVG_XML( "svg+xml" ),
IMAGE_T38( "t38" ),
IMAGE_TIFF( "tiff" ),
IMAGE_WEBP( "webp" ),
IMAGE_WMF( "wmf" ),
IMAGE_X_BITMAP( "x-xbitmap" ),
IMAGE_X_PIXMAP( "x-xpixmap" ),
AUDIO_BASIC( AUDIO, "basic" ),
AUDIO_MP3( AUDIO, "mp3" ),
AUDIO_WAV( AUDIO, "x-wav" ),
VIDEO_MNG( VIDEO, "x-mng" ),
TEXT_HTML( TEXT, "html" ),
TEXT_MARKDOWN( TEXT, "markdown" ),
TEXT_PLAIN( TEXT, "plain" ),
TEXT_XHTML( TEXT, "xhtml+xml" ),
TEXT_XML( TEXT, "xml" ),
TEXT_YAML( TEXT, "yaml" ),
/*
* When all other lights go out.
*/
UNDEFINED( TypeName.UNDEFINED, "undefined" );
public enum TypeName {
APPLICATION,
AUDIO,
IMAGE,
TEXT,
UNDEFINED,
VIDEO
}
private final String mMediaType;
private final TypeName mTypeName;
private final String mSubtype;
MediaType( final String subtype ) {
this( IMAGE, subtype );
}
MediaType( final TypeName typeName, final String subtype ) {
mTypeName = typeName;
mSubtype = subtype;
mMediaType = typeName.toString().toLowerCase() + '/' + subtype;
}
public static MediaType valueFrom( final File file ) {
assert file != null;
return fromFilename( file.getName() );
}
public static MediaType fromFilename( final String filename ) {
assert filename != null;
return getMediaType( getExtension( filename ) );
}
public static MediaType valueFrom( final Path path ) {
assert path != null;
return valueFrom( path.toFile() );
}
public static MediaType valueFrom( String contentType ) {
if( contentType == null || contentType.isBlank() ) {
return UNDEFINED;
}
var i = contentType.indexOf( ';' );
contentType = contentType.substring(
0, i == -1 ? contentType.length() : i );
i = contentType.indexOf( '/' );
i = i == -1 ? contentType.length() : i;
final var type = contentType.substring( 0, i );
final var subtype = contentType.substring( i + 1 );
return valueFrom( type, subtype );
}
public static MediaType valueFrom(
final String type, final String subtype ) {
assert type != null;
assert subtype != null;
for( final var mediaType : values() ) {
if( mediaType.equals( type, subtype ) ) {
return mediaType;
}
}
return UNDEFINED;
}
public boolean equals( final String type, final String subtype ) {
assert type != null;
assert subtype != null;
return mTypeName.name().equalsIgnoreCase( type ) &&
mSubtype.equalsIgnoreCase( subtype );
}
public boolean isType( final TypeName typeName ) {
return mTypeName == typeName;
}
public String getSubtype() {
return mSubtype;
}
@Override
public String toString() {
return mMediaType;
}
}
MediaTypeExtension.java
最后一块拼图是 MediaType
到其已知和常见/流行的文件扩展名的映射。这允许基于文件扩展名的双向查找。
import static MediaType.*;
import static java.util.List.of;
public enum MediaTypeExtension {
MEDIA_APP_ACAD( APP_ACAD, of( "dwg" ) ),
MEDIA_APP_PDF( APP_PDF ),
MEDIA_APP_PS( APP_PS, of( "ps" ) ),
MEDIA_APP_EPS( APP_EPS ),
MEDIA_APP_ZIP( APP_ZIP ),
MEDIA_AUDIO_MP3( AUDIO_MP3 ),
MEDIA_AUDIO_BASIC( AUDIO_BASIC, of( "au" ) ),
MEDIA_AUDIO_WAV( AUDIO_WAV, of( "wav" ) ),
MEDIA_FONT_OTF( FONT_OTF ),
MEDIA_FONT_TTF( FONT_TTF ),
MEDIA_IMAGE_APNG( IMAGE_APNG ),
MEDIA_IMAGE_BMP( IMAGE_BMP ),
MEDIA_IMAGE_GIF( IMAGE_GIF ),
MEDIA_IMAGE_JPEG( IMAGE_JPEG,
of( "jpg", "jpe", "jpeg", "jfif", "pjpeg", "pjp" ) ),
MEDIA_IMAGE_PNG( IMAGE_PNG ),
MEDIA_IMAGE_PSD( IMAGE_PHOTOSHOP, of( "psd" ) ),
MEDIA_IMAGE_SVG( IMAGE_SVG_XML, of( "svg" ) ),
MEDIA_IMAGE_TIFF( IMAGE_TIFF, of( "tiff", "tif" ) ),
MEDIA_IMAGE_WEBP( IMAGE_WEBP ),
MEDIA_IMAGE_X_BITMAP( IMAGE_X_BITMAP, of( "xbm" ) ),
MEDIA_IMAGE_X_PIXMAP( IMAGE_X_PIXMAP, of( "xpm" ) ),
MEDIA_VIDEO_MNG( VIDEO_MNG, of( "mng" ) ),
MEDIA_TEXT_MARKDOWN( TEXT_MARKDOWN, of(
"md", "markdown", "mdown", "mdtxt", "mdtext", "mdwn", "mkd", "mkdown",
"mkdn" ) ),
MEDIA_TEXT_PLAIN( TEXT_PLAIN, of( "txt", "asc", "ascii", "text", "utxt" ) ),
MEDIA_TEXT_R_MARKDOWN( TEXT_R_MARKDOWN, of( "Rmd" ) ),
MEDIA_TEXT_R_XML( TEXT_R_XML, of( "Rxml" ) ),
MEDIA_TEXT_XHTML( TEXT_XHTML, of( "xhtml" ) ),
MEDIA_TEXT_XML( TEXT_XML ),
MEDIA_TEXT_YAML( TEXT_YAML, of( "yaml", "yml" ) ),
MEDIA_UNDEFINED( UNDEFINED, of( "undefined" ) );
private final MediaType mMediaType;
private final List<String> mExtensions;
MediaTypeExtension( final MediaType mediaType ) {
this( mediaType, of( mediaType.getSubtype() ) );
}
MediaTypeExtension(
final MediaType mediaType, final List<String> extensions ) {
assert mediaType != null;
assert extensions != null;
assert !extensions.isEmpty();
mMediaType = mediaType;
mExtensions = extensions;
}
public String getExtension() {
return mExtensions.get( 0 );
}
public static MediaTypeExtension valueFrom( final MediaType mediaType ) {
for( final var type : values() ) {
if( type.isMediaType( mediaType ) ) {
return type;
}
}
return MEDIA_UNDEFINED;
}
boolean isMediaType( final MediaType mediaType ) {
return mMediaType == mediaType;
}
static MediaType getMediaType( final String extension ) {
final var sanitized = sanitize( extension );
for( final var mediaType : MediaTypeExtension.values() ) {
if( mediaType.isType( sanitized ) ) {
return mediaType.getMediaType();
}
}
return UNDEFINED;
}
private boolean isType( final String sanitized ) {
for( final var extension : mExtensions ) {
if( extension.equalsIgnoreCase( sanitized ) ) {
return true;
}
}
return false;
}
private static String sanitize( final String extension ) {
return extension == null ? "" : extension.toLowerCase();
}
private MediaType getMediaType() {
return mMediaType;
}
}
用途:
// EXAMPLE -- Detect media type
//
final File image = new File( "filename.jpg" );
final MediaType mt = StreamMediaType.getMediaType( image );
// Tricky! The JPG could be a PNG in disguise.
if( mt.isType( MediaType.TypeName.IMAGE ) ) {
if( mt == MediaType.IMAGE_PNG ) {
// Nice try! Sneaky sneak.
}
}
// EXAMPLE -- Get typical media type file name extension
//
final String ext = MediaTypeExtension.valueFrom( MediaType.IMAGE_SVG_XML ).getExtension();
// EXAMPLE -- Get media type from HTTP request
//
final var url = new URL( "https://localhost/path/file.ext" );
final var conn = (HttpURLConnection) url.openConnection();
final var contentType = conn.getContentType();
MediaType mediaType = valueFrom( contentType );
// Fall back to stream detection probe
if( mediaType == UNDEFINED ) {
mediaType = StreamMediaType.getMediaType( conn.getInputStream() );
}
conn.disconnect();
你明白了。
简短的图书馆评论:
Apache Tika -- 600kb 膨胀,需要多行配置和多个 JAR 文件。
jMimeMagic -- 未完成,需要多行配置。
MimeUtil2——相当大,不能开箱即用。
FileTypeDetector -- 与 JDK 捆绑在一起,比山松甲虫出没的森林还要臭。
Files.probeContentType - 检测是特定于平台的,被认为是不可靠的(来源)。
MimetypesFileTypeMap -- 与activation.jar 捆绑在一起,使用文件扩展名。
用于测试的示例音频、视频和图像文件:
http://mirrors.standaloneinstaller.com/video-sample/
https://www.w3.org/People/mimasa/test/imgformat/
1 “MIME 类型”是一个已弃用的术语。
如果您在 linux 操作系统上工作,则有一个命令行 file --mimetype
:
String mimetype(file){
//1. run cmd
Object cmd=Runtime.getRuntime().exec("file --mime-type "+file);
//2 get output of cmd , then
//3. parse mimetype
if(output){return output.split(":")[1].trim(); }
return "";
}
然后
mimetype("/home/nyapp.war") // 'application/zip'
mimetype("/var/www/ggg/au.mp3") // 'audio/mp3'
在尝试了各种其他库后,我选择了 mime-util。
<groupId>eu.medsea.mimeutil</groupId>
<artifactId>mime-util</artifactId>
<version>2.1.3</version>
</dependency>
File file = new File("D:/test.tif");
MimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");
Collection<?> mimeTypes = MimeUtil.getMimeTypes(file);
System.out.println(mimeTypes);
public String getFileContentType(String fileName) {
String fileType = "Undetermined";
final File file = new File(fileName);
try
{
fileType = Files.probeContentType(file.toPath());
}
catch (IOException ioException)
{
System.out.println(
"ERROR: Unable to determine file type for " + fileName
+ " due to exception " + ioException);
}
return fileType;
}
File file = new File(PropertiesReader.FILE_PATH);
MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
String mimeType = fileTypeMap.getContentType(file);
URLConnection uconnection = file.toURL().openConnection();
mimeType = uconnection.getContentType();
如果您想要一种将文件扩展名映射到 mime 类型的可靠(即一致)方式,我使用的是:
它包括一个捆绑的 mime 类型数据库,并且通过使用数据库初始化“程序化”条目,基本上颠倒了 javax.activation 的 MimetypesFileTypeMap 类的逻辑。这样,库定义的类型总是优先于非捆绑资源中可能定义的类型。
我用下面的代码做到了。
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class MimeFileType {
public static void main(String args[]){
try{
URL url = new URL ("https://www.url.com.pdf");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
InputStream content = (InputStream)connection.getInputStream();
connection.getHeaderField("Content-Type");
System.out.println("Content-Type "+ connection.getHeaderField("Content-Type"));
BufferedReader in = new BufferedReader (new InputStreamReader(content));
}catch (Exception e){
}
}
}
.xml
、.png
和.xhtml
文件获取null
。我不知道我是否只是在做一些可怕的错误,但这似乎相当可怕。