Узнайте MIME-тип сжатых файлов, загруженных с S3 для Java

Клиент должен загрузить сжатый файл в папку S3. Затем сжатый файл загружается и распаковывается для выполнения различных операций с содержащимися в нем файлами. Первоначально мы сказали нашему клиенту сжать его файлы в ZIP- файл, но это оказалось слишком сложным для нашего клиента. Вместо этого он представил файл RAR с расширением ZIP... как умный. По понятным причинам невозможно распаковать RAR- файл, используя алгоритм распаковки ZIP.

Итак, я ищу способ узнать тип файлов загруженных файлов S3, учитывая, что я работаю над проектом Java с SDK Amazon в ОС Linux. Я позабочусь о том, как распаковать файл в зависимости от полученного типа файла.

Я рассмотрел много вопросов о переполнении стека, например, этот, но ни один из них не кажется эффективным на 100%, если просто посмотреть на них (и их комментарии).

Как лучше всего определить тип сжатого файла?

1 ответ

TL;DR;

Когда кто-то загружает файл в Amazon S3 программно, можно указать объект Content-Type, Если один из них не указан, как поясняет @Michael-bot, значение, назначенное по умолчанию, будет binary/octet-stream, Или, если вы решите загрузить файл через графический интерфейс Amazon S3, файл получит Content-Type из его расширения файла (к сожалению, не его содержимое). Если вы можете доверять тому, кто загрузил файл для установки Content-Type правильно, идти вперед и посмотреть на ObjectMetadata, но если вы не (как я), вам нужно другое решение.

Таким образом, если вы ищете решение, которое работает с наиболее распространенными типами сжатия файлов, файлы Files.probeContentType, Apache Tika и SimpleMagic кажутся приемлемыми решениями.

В итоге я выбрал Files.probeContentType поскольку он не требует дополнительных библиотек и прекрасно работает на Linux-машине (если у файла нет неправильного расширения, для которого есть обходной путь).


Сначала можно подумать, что объект ответа при загрузке файла из Amazon S3 включает в себя тип файла. И он содержит эту информацию, но проблема возникает, когда расширение файла не соответствует его содержимому.

import com.amazonaws.services.s3.model.S3Object;

final S3Object s3Object = ...;
final String contentType = s3Object.getObjectMetadata().getContentType();

Этот код вернется application/zip даже если содержимое файла Rar-файла. Так что это решение не работает для меня.

По этой причине я потратил время на создание примера проекта, в котором были протестированы различные сценарии с различными подходами и доступными библиотеками. Я использую Java 8, кстати.

Типы протестированных файлов:

  • Zip-файл с расширением Zip и без расширения
  • Файл Rar с расширением Rar, расширением Zip и без расширения
  • Файл 7z с расширением 7z, расширением Zip и без расширения
  • Tar.xz с расширением Tar.xz, расширением Zip и без расширения
  • Tar.gz с расширением Tar.gz, расширением Zip и без расширения

Осторожно, представленные здесь реализации предназначены только для тестирования. Они никоим образом не одобряются для использования в производственном коде, поскольку они не рассматривают проблемы с блокировкой файлов среди прочих, которые мое воображение не могло бы рассмотреть. знак равно


MimetypesFileTypeMap

Реализация

import java.io.File;
import javax.activation.MimetypesFileTypeMap;

final File file = new File(basePath + "/" + fileName);
try {
    return MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file);
} catch (final Exception exception) {
    return "<EXCEPTION: " + exception.getMessage() + ">";
}

Результаты

Rar with Rar extension is: application/octet-stream
Rar with Zip extension is: application/octet-stream
Zip with Zip extension is: application/octet-stream
7z with 7z extension is: application/octet-stream
7z with Zip extension is: application/octet-stream
Tar.xz with Tar.xz extension is: application/octet-stream
Tar.xz with Zip extension is: application/octet-stream
Tar.gz with Tar.gz extension is: application/octet-stream
Tar.gz with Zip extension is: application/octet-stream
Rar without extension is: application/octet-stream
Zip without extension is: application/octet-stream
7z without extension is: application/octet-stream
Tar.xz without extension is: application/octet-stream
Tar.gz without extension is: application/octet-stream

Заключение

Значение, возвращаемое этим подходом, когда тип файла не был распознан, application/octet-stream, Кажется, что все сценарии провалились, поэтому мы должны отказаться от этого подхода.


URLConnection.guessContentTypeFromStream

Реализация

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.net.URLConnection;

final File file = new File(basePath + "/" + fileName);
try {
    final FileInputStream fileInputStream = new FileInputStream(file);
    final InputStream inputStream = new BufferedInputStream(fileInputStream);

    return URLConnection.guessContentTypeFromStream(inputStream);
} catch (final Exception exception) {
    return "<EXCEPTION: " + exception.getMessage() + ">";
}

Результаты

Rar with Rar extension is: null
Rar with Zip extension is: null
Zip with Zip extension is: null
7z with 7z extension is: null
7z with Zip extension is: null
Tar.xz with Tar.xz extension is: null
Tar.xz with Zip extension is: null
Tar.gz with Tar.gz extension is: null
Tar.gz with Zip extension is: null
Rar without extension is: null
Zip without extension is: null
7z without extension is: null
Tar.xz without extension is: null
Tar.gz without extension is: null

Заключение

Опять же, этот метод не подходит для всех сценариев. Кажется, его поддержка очень ограничена.


Files.probeContentType

Реализация

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

try {
    final Path path = Paths.get(basePath + "/" + fileName);
    return Files.probeContentType(path);
} catch (final Exception exception) {
    return "<EXCEPTION: " + exception.getMessage() + ">";
}

Результаты

Rar with Rar extension is: application/vnd.rar
Rar with Zip extension is: application/zip
Zip with Zip extension is: application/zip
7z with 7z extension is: application/x-7z-compressed
7z with Zip extension is: application/zip
Tar.xz with Tar.xz extension is: application/x-xz-compressed-tar
Tar.xz with Zip extension is: application/zip
Tar.gz with Tar.gz extension is: application/x-compressed-tar
Tar.gz with Zip extension is: application/zip
Rar without extension is: application/vnd.rar
Zip without extension is: application/zip
7z without extension is: application/x-7z-compressed
Tar.xz without extension is: application/x-xz
Tar.gz without extension is: application/gzip

Заключение

Этот метод работал на удивление хорошо, но не обманывайте себя, есть сценарий, когда он постоянно терпит неудачу. Если файл имеет неправильное расширение (содержимое, которое не соответствует содержимому), он сообщит, что тип файла является расширением. Это не должно случаться очень часто, но если кто-то очень привередлив, этот метод не должен использоваться.

Также некоторые предупреждают, что его подход не работает в Windows.

Обходной путь: Если удастся удалить расширение из имени файла, это вернет правильное значение для всех заданных сценариев.


Apache Tika (тика-эвал 1.18)

Кажется, существует много разновидностей этой библиотеки (приложение, сервер, eval и т. Д.), Но многие в Интернете жалуются на то, что она несколько "сильно зависит от зависимостей".

Реализация

import org.apache.tika.Tika;

try {
    return new Tika().detect(new File(basePath + "/" + fileName));
} catch (final Exception exception) {
    return "<EXCEPTION: " + exception.getMessage() + ">";
}

Результаты

Rar with Rar extension is: application/x-rar-compressed
Rar with Zip extension is: application/x-rar-compressed
Zip with Zip extension is: application/zip
7z with 7z extension is: application/x-7z-compressed
7z with Zip extension is: application/x-7z-compressed
Tar.xz with Tar.xz extension is: application/x-xz
Tar.xz with Zip extension is: application/x-xz
Tar.gz with Tar.gz extension is: application/gzip
Tar.gz with Zip extension is: application/gzip
Rar without extension is: application/x-rar-compressed
Zip without extension is: application/zip
7z without extension is: application/x-7z-compressed
Tar.xz without extension is: application/x-xz
Tar.gz without extension is: application/gzip

Заключение

Все файлы были правильно идентифицированы, но, поскольку они имеют свои преимущества, они также имеют свои недостатки.

Плюсы:

  • Поддерживается Apache.
  • Не обманывается расширениями.

Минусы:

  • Действительно тяжелый, особенно если нужно проверить только тип файла. Вес баночки Тика-Эвал +40 МБ.

URLConnection

Реализация

import java.net.URL;
import java.net.URLConnection;

try {
    final URL url = new URL("file://" + basePath + "/" + fileName);
    final URLConnection urlConnection = url.openConnection();
    return urlConnection.getContentType();
} catch (final Exception exception) {
    return "<EXCEPTION: " + exception.getMessage() + ">";
}

Результаты

Rar with Rar extension is: content/unknown
Rar with Zip extension is: application/zip
Zip with Zip extension is: application/zip
7z with 7z extension is: content/unknown
7z with Zip extension is: application/zip
Tar.xz with Tar.xz extension is: content/unknown
Tar.xz with Zip extension is: application/zip
Tar.gz with Tar.gz extension is: application/octet-stream
Tar.gz with Zip extension is: application/zip
Rar without extension is: content/unknown
Zip without extension is: content/unknown
7z without extension is: content/unknown
Tar.xz without extension is: content/unknown
Tar.gz without extension is: content/unknown

Заключение

Он едва определяет какой-либо формат сжатия файла и руководствуется расширением, а не его содержимым.


SimpleMagic 1.14

Этот проект обновляется не реже одного раза в год.

Реализация

import com.j256.simplemagic.ContentInfo;
import com.j256.simplemagic.ContentInfoUtil;

try {
    final ContentInfoUtil util = new ContentInfoUtil();
    final ContentInfo info = util.findMatch(basePath + "/" + fileName);

    return info.getMimeType();
} catch (final Exception exception) {
    return "<EXCEPTION: " + exception.getMessage() + ">";
}

Результаты

Rar with Rar extension is: application/x-rar
Rar with Zip extension is: application/x-rar
Zip with Zip extension is: application/zip
7z with 7z extension is: application/x-7z-compressed
7z with Zip extension is: application/x-7z-compressed
Tar.xz with Tar.xz extension is: <EXCEPTION: null>
Tar.xz with Zip extension is: <EXCEPTION: null>
Tar.gz with Tar.gz extension is: application/x-gzip
Tar.gz with Zip extension is: application/x-gzip
Rar without extension is: application/x-rar
Zip without extension is: application/zip
7z without extension is: application/x-7z-compressed
Tar.xz without extension is: <EXCEPTION: null>
Tar.gz without extension is: application/x-gzip

Заключение

Это работало почти во всех наших сценариях, но, похоже, что для большинства "неясных" форматов сжатия, таких как Tar.xz, он не смог их обнаружить (и в процессе возникло исключение).


MimeUtil 2.1.3

Этот проект не был изменен с 2010 года, поэтому не ожидайте поддержки или обновлений. Это только перечислено здесь ради завершения.

Реализация

import eu.medsea.mimeutil.MimeUtil2;

try {
    final MimeUtil2 mimeUtil = new MimeUtil2();
        mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector");

    return MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(basePath + "/" + fileName)).toString();
} catch (final Exception exception) {
    return "<EXCEPTION: " + exception.getMessage() + ">";
}

Результаты

Rar with Rar extension is: application/x-rar
Rar with Zip extension is: application/x-rar
Zip with Zip extension is: application/zip
7z with 7z extension is: application/octet-stream
7z with Zip extension is: application/octet-stream
Tar.xz with Tar.xz extension is: application/octet-stream
Tar.xz with Zip extension is: application/octet-stream
Tar.gz with Tar.gz extension is: application/x-gzip
Tar.gz with Zip extension is: application/x-gzip
Rar without extension is: application/x-rar
Zip without extension is: application/zip
7z without extension is: application/octet-stream
Tar.xz without extension is: application/octet-stream
Tar.gz without extension is: application/x-gzip

Заключение

Он определяет некоторые из наиболее популярных типов файлов, но не работает с Tar.xz и 7z.


файл - командная строка

Не самое симпатичное решение, но его нужно было попробовать: команда Ubuntu file.

Реализация

import java.io.BufferedReader;
import java.io.InputStreamReader;

try {
    final Process process = Runtime.getRuntime().exec("file --mime-type " + basePath + "/" + fileName);

    final BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));

    String text = "";

    String s;
    while ((s = stdInput.readLine()) != null) {
        text += s;
    }

    return text.split(": ")[1];
} catch (final Exception exception) {
    return "<EXCEPTION: " + exception.getMessage() + ">";
}

Результаты

Rar with Rar extension is: application/x-rar
Rar with Zip extension is: application/x-rar
Zip with Zip extension is: application/zip
7z with 7z extension is: application/x-7z-compressed
7z with Zip extension is: application/x-7z-compressed
Tar.xz with Tar.xz extension is: application/x-xz
Tar.xz with Zip extension is: application/x-xz
Tar.gz with Tar.gz extension is: application/gzip
Tar.gz with Zip extension is: application/gzip
Rar without extension is: application/x-rar
Zip without extension is: application/zip
7z without extension is: application/x-7z-compressed
Tar.xz without extension is: application/x-xz
Tar.gz without extension is: application/gzip

Заключение

Это работает для всех наших сценариев, но опять же, это зависит от команды File присутствовать в системе, где выполняется код.

Другие вопросы по тегам