Java:一种将 Mime(内容)类型与 CommonsMultipartFile 中的文件扩展名匹配的方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27402992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 11:45:49  来源:igfitidea点击:

Java: A way to match Mime (content) type to file extension from CommonsMultipartFile

javastringcomparison

提问by Ascalonian

At my company, I need to compare the mime type to the file extension for extra reason. I have a CommonsMultipartFile. I am trying to figure out the best way to do this comparison. I have seen a MimetypesFileTypeMap, but not sure if that will work here. I am trying to avoid (or limit) any hard coding.

在我的公司,出于额外的原因,我需要将 MIME 类型与文件扩展名进行比较。我有一个CommonsMultipartFile. 我试图找出进行这种比较的最佳方法。我见过一个MimetypesFileTypeMap,但不确定这是否可以在这里工作。我试图避免(或限制)任何硬编码。

I get the values as such:

我得到这样的值:

CommonsMultipartFile file = ...;
String fileName = file.getOriginalFilename();
String contentType = file.getContentType();

String extension = FilenameUtils.getExtension(fileName);

I appreciate any guidance and help on this. Thanks!

我感谢任何有关这方面的指导和帮助。谢谢!

回答by Lucas

Java library for that case is quite limited (number of types). This is how I do it:

这种情况下的 Java 库非常有限(类型数量)。这就是我的做法:

static String getMimeType(String fileName) {
    // 1. first use java's built-in utils
    FileNameMap mimeTypes = URLConnection.getFileNameMap();
    String contentType = mimeTypes.getContentTypeFor(fileName);

    // 2. nothing found -> lookup our in extension map to find types like ".doc" or ".docx"
    if (contentType == null) {
        String extension = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length());;
        contentType = fileExtensionMap.get(extension);
    }
    return contentType;
}

step 2 involves having a static map:

第 2 步涉及拥有静态地图:

private static final Map<String, String> fileExtensionMap;

static {
    fileExtensionMap = new HashMap<String, String>();
    // MS Office
    fileExtensionMap.put("doc", "application/msword");
    fileExtensionMap.put("dot", "application/msword");
    fileExtensionMap.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
    fileExtensionMap.put("dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template");
    fileExtensionMap.put("docm", "application/vnd.ms-word.document.macroEnabled.12");
    fileExtensionMap.put("dotm", "application/vnd.ms-word.template.macroEnabled.12");
    fileExtensionMap.put("xls", "application/vnd.ms-excel");
    fileExtensionMap.put("xlt", "application/vnd.ms-excel");
    fileExtensionMap.put("xla", "application/vnd.ms-excel");
    fileExtensionMap.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    fileExtensionMap.put("xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template");
    fileExtensionMap.put("xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12");
    fileExtensionMap.put("xltm", "application/vnd.ms-excel.template.macroEnabled.12");
    fileExtensionMap.put("xlam", "application/vnd.ms-excel.addin.macroEnabled.12");
    fileExtensionMap.put("xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12");
    fileExtensionMap.put("ppt", "application/vnd.ms-powerpoint");
    fileExtensionMap.put("pot", "application/vnd.ms-powerpoint");
    fileExtensionMap.put("pps", "application/vnd.ms-powerpoint");
    fileExtensionMap.put("ppa", "application/vnd.ms-powerpoint");
    fileExtensionMap.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
    fileExtensionMap.put("potx", "application/vnd.openxmlformats-officedocument.presentationml.template");
    fileExtensionMap.put("ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow");
    fileExtensionMap.put("ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12");
    fileExtensionMap.put("pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12");
    fileExtensionMap.put("potm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12");
    fileExtensionMap.put("ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12");
    // Open Office
    fileExtensionMap.put("odt", "application/vnd.oasis.opendocument.text");
    fileExtensionMap.put("ott", "application/vnd.oasis.opendocument.text-template");
    fileExtensionMap.put("oth", "application/vnd.oasis.opendocument.text-web");
    fileExtensionMap.put("odm", "application/vnd.oasis.opendocument.text-master");
    fileExtensionMap.put("odg", "application/vnd.oasis.opendocument.graphics");
    fileExtensionMap.put("otg", "application/vnd.oasis.opendocument.graphics-template");
    fileExtensionMap.put("odp", "application/vnd.oasis.opendocument.presentation");
    fileExtensionMap.put("otp", "application/vnd.oasis.opendocument.presentation-template");
    fileExtensionMap.put("ods", "application/vnd.oasis.opendocument.spreadsheet");
    fileExtensionMap.put("ots", "application/vnd.oasis.opendocument.spreadsheet-template");
    fileExtensionMap.put("odc", "application/vnd.oasis.opendocument.chart");
    fileExtensionMap.put("odf", "application/vnd.oasis.opendocument.formula");
    fileExtensionMap.put("odb", "application/vnd.oasis.opendocument.database");
    fileExtensionMap.put("odi", "application/vnd.oasis.opendocument.image");
    fileExtensionMap.put("oxt", "application/vnd.openofficeorg.extension");
    // Other
    fileExtensionMap.put("txt", "text/plain");
    fileExtensionMap.put("rtf", "application/rtf");
    fileExtensionMap.put("pdf", "application/pdf");
}

works fine for me, hope that helps!

对我来说很好用,希望有帮助!

回答by VGR

The most reliable method is to use Files.probeContentType, but that requires an actual file, partly because not all operating systems rely on the extension to determine the file type.

最可靠的方法是使用Files.probeContentType,但这需要实际文件,部分原因是并非所有操作系统都依赖扩展名来确定文件类型。

In Linux, the next best approach is new MimetypesFileTypeMap("/etc/mime.types"), which will be truly based on the system's mappings. I don't know if OS X has a mime.typesfile (though a search shows that CUPSfor OS X uses such a file, with an enhanced but mostly backward compatible syntax).

在 Linux 中,下一个最好的方法是new MimetypesFileTypeMap("/etc/mime.types"),这将真正基于系统的映射。我不知道 OS X 是否有一个mime.types文件(尽管搜索显示OS X 的CUPS使用这样一个文件,具有增强但主要向后兼容的语法)。

As far as I know, there is no built-in Java class for getting MIME-type?extension mappings in Windows.

据我所知,在 Windows 中没有用于获取 MIME 类型扩展映射的内置 Java 类。

URLConnection.getFileNameMap()returns a FileNameMap that has a decent number of entries, but still isn't based on the system's mappings. On my Linux system, for example, it returns null for .bz2 and .rpm files.

URLConnection.getFileNameMap()返回一个具有相当数量条目的 FileNameMap,但仍然不是基于系统的映射。例如,在我的 Linux 系统上,它为 .bz2 和 .rpm 文件返回 null。

FileTypeMap.getDefaultFileTypeMap()seems to have a few entries, but it too is clearly not based on the system's mappings. At least on my system, it returns "application/octet-stream" for .png files.

FileTypeMap.getDefaultFileTypeMap()似乎有一些条目,但它显然也不是基于系统的映射。至少在我的系统上,它为 .png 文件返回“application/octet-stream”。

I think the easiest thing to do is encapsulate all of them in a class:

我认为最简单的方法是将它们全部封装在一个类中:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;

public class MimeTypes {

    private static final Logger logger =
        Logger.getLogger(MimeTypes.class.getName());

    private static final String DEFAULT_TYPE = "application/octet-stream";

    private static final Path mimeTypesFile = Paths.get("/etc/mime.types");

    private final FileNameMap fileNameMap = URLConnection.getFileNameMap();

    private final FileTypeMap fileTypeMap = FileTypeMap.getDefaultFileTypeMap();

    private final FileTypeMap mimeTypesMap;

    public MimeTypes() {
        FileTypeMap map = null;
        if (Files.isRegularFile(mimeTypesFile)) {
            try {
                map = new MimetypesFileTypeMap(mimeTypesFile.toString());
            } catch (IOException e) {
                logger.log(Level.WARNING, "Couldn't read " + mimeTypesFile, e);
            }
        }
        mimeTypesMap = map;
    }

    public String getContentType(String filename) {
        String type = null;

        if (mimeTypesMap != null) {
            type = mimeTypesMap.getContentType(filename);
        }

        if (type == null || type.equals(DEFAULT_TYPE)) {
            type = fileNameMap.getContentTypeFor(filename);
        }

        if (type == null || type.equals(DEFAULT_TYPE)) {
            type = fileTypeMap.getContentType(filename);
        }

        return type;
    }

    public static void main(String[] args) {

        MimeTypes mimeTypes = new MimeTypes();

        for (String arg : args) {
            System.out.printf("Type of file \"%s\" is \"%s\"%n",
                arg, mimeTypes.getContentType(arg));
        }
    }
}

That will get you fairly good results. If you want to have true platform-based MIME type checking in Windows, the only way I know to do that is to read the registry:

这会给你带来相当好的结果。如果您想在 Windows 中进行真正的基于平台的 MIME 类型检查,我所知道的唯一方法就是读取注册表:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;

import java.io.File;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.Collections;
import java.util.Formatter;

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ExecutionException;

public class MimeTypes {

    private static final Logger logger =
        Logger.getLogger(MimeTypes.class.getName());

    private static final String DEFAULT_TYPE = "application/octet-stream";

    private static final Path mimeTypesFile = Paths.get("/etc/mime.types");

    private final FileNameMap fileNameMap = URLConnection.getFileNameMap();

    private final FileTypeMap fileTypeMap = FileTypeMap.getDefaultFileTypeMap();

    private final FileTypeMap mimeTypesMap;

    public MimeTypes() {
        FileTypeMap map = null;

        if (Files.isRegularFile(mimeTypesFile)) {
            try {
                map = new MimetypesFileTypeMap(mimeTypesFile.toString());
            } catch (IOException e) {
                logger.log(Level.WARNING, "Couldn't read " + mimeTypesFile, e);
            }
        }

        if (map == null && System.getProperty("os.name").contains("Windows")) {
            try {
                map = new WindowsFileTypeMap();
            } catch (IOException e) {
                logger.log(Level.WARNING, "Couldn't read registered types", e);
            } catch (InterruptedException e) {
                logger.log(Level.INFO, "Interrupted; read canceled", e);
            }
        }

        mimeTypesMap = map;
    }

    public String getContentType(String filename) {
        String type = null;

        if (mimeTypesMap != null) {
            type = mimeTypesMap.getContentType(filename);
        }

        if (type == null || type.equals(DEFAULT_TYPE)) {
            type = fileNameMap.getContentTypeFor(filename);
        }

        if (type == null || type.equals(DEFAULT_TYPE)) {
            type = fileTypeMap.getContentType(filename);
        }

        return type;
    }

    private static class WindowsFileTypeMap
    extends FileTypeMap {

        private final Map<String, String> extensionToMimeType;

        public WindowsFileTypeMap()
        throws IOException,
               InterruptedException {

            ProcessBuilder builder =
                new ProcessBuilder("PowerShell.exe", "-Command", "-");

            Process process = builder.start();
            List<String> regEntryNames = getOutput(process, String.format(
                "Get-ChildItem \"Registry::HKEY_CLASSES_ROOT\\" |"
                    + " Select-String -InputObject { $_.Name }"
                    + " -SimpleMatch -Pattern \"HKEY_CLASSES_ROOT\.\"%n"));

            Formatter input = new Formatter();
            for (String name : regEntryNames) {
                input.format(
                    "$values = Get-ItemProperty \"Registry::%s\"%n" +
                    "$values.\"Content Type\" + \" \"%n", name);
            }

            process = builder.start();
            List<String> mimeTypes = getOutput(process, input.toString());

            int len = regEntryNames.size();
            extensionToMimeType = new HashMap<>(len);
            for (int i = 0; i < len; i++) {
                String mimeType = mimeTypes.get(i).trim();
                if (!mimeType.isEmpty()) {
                    String extension =
                        regEntryNames.get(i).replace("HKEY_CLASSES_ROOT\", "");
                    extensionToMimeType.put(extension, mimeType);
                }
            }
        }

        @Override
        public String getContentType(File file) {
            return getContentType(file.toString());
        }

        @Override
        public String getContentType(String filename) {
            int period = filename.lastIndexOf('.');
            if (period < 0) {
                return DEFAULT_TYPE;
            }

            String ext = filename.substring(period);
            String type = extensionToMimeType.get(ext);
            return (type != null ? type : DEFAULT_TYPE);
        }

        private static List<String> getOutput(final Process process,
                                              final String input)
        throws IOException,
               InterruptedException {

            Callable<Void> inputSender = new Callable<Void>() {
                @Override
                public Void call()
                throws IOException {
                    try (BufferedWriter writer =
                        new BufferedWriter(
                            new OutputStreamWriter(
                                process.getOutputStream()))) {
                        writer.write(input);
                    }
                    return null;
                }
            };

            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<?> inputSenderStatus = executor.submit(inputSender);

            List<String> lines;
            try (BufferedReader reader =
                new BufferedReader(
                    new InputStreamReader(
                        process.getInputStream()))) {
                lines = new ArrayList<>();
                String line;
                while ((line = reader.readLine()) != null) {
                    lines.add(line);
                }
            }

            try {
                inputSenderStatus.get();
            } catch (ExecutionException e) {
                throw new IOException(e);
            }

            executor.shutdown();
            executor.awaitTermination(30, TimeUnit.SECONDS);

            process.waitFor();

            return lines;
        }
    }

    public static void main(String[] args) {

        MimeTypes mimeTypes = new MimeTypes();

        for (String arg : args) {
            System.out.printf("Type of file \"%s\" is \"%s\"%n",
                arg, mimeTypes.getContentType(arg));
        }
    }
}

回答by nablex

I have a class https://github.com/nablex/utils-io/blob/master/src/main/java/be/nabu/utils/io/ContentTypeMap.javathat does this.

我有一个类https://github.com/nablex/utils-io/blob/master/src/main/java/be/nabu/utils/io/ContentTypeMap.java这样做。

It adds a few things that are missing in the URLConnection stuff:

它添加了 URLConnection 内容中缺少的一些内容:

  • it can handle 1-* relationships in both directions (one mime type, multiple extensions and the other way around)
  • you can go both directions (mimetype > extension and extension > mimetype)
  • 它可以在两个方向上处理 1-* 关系(一种 mime 类型,多个扩展名,反之亦然)
  • 你可以去两个方向(mimetype > 扩展和扩展 > mimetype)

Additionally it can be plugged into the URLConnection using:

此外,它可以插入到 URLConnection 使用:

ContentTypeMap.register()

By default it uses a file to load the mappings: https://github.com/nablex/utils-io/blob/master/src/main/resources/mime.properties

默认情况下,它使用一个文件来加载映射:https: //github.com/nablex/utils-io/blob/master/src/main/resources/mime.properties