Как определить, является ли файл PDF или TIFF? - PullRequest
18 голосов
/ 28 апреля 2010

Пожалуйста, потерпите меня, так как я был брошен в середину этого проекта, не зная всего фона. Если у вас есть вопросы WTF, поверьте мне, у меня они тоже есть.

Вот сценарий: у меня есть группа файлов, находящихся на сервере IIS. У них нет расширения файла на них. Просто голые файлы с такими именами как "asda-2342-sd3rs-asd24-ut57" и так далее. Ничего интуитивного.

Проблема в том, что мне нужно подавать файлы на странице ASP.NET (2.0) и отображать файлы в формате TIFF в формате TIFF, а файлы PDF в формате PDF. К сожалению, я не знаю, что есть что, и мне нужно иметь возможность отображать их соответствующим образом в соответствующих форматах.

Например, предположим, что мне нужно отобразить 2 файла, один из которых - tiff, а другой - PDF. Страница должна отображаться с изображением в виде галочки и, возможно, ссылкой, которая откроет PDF в новой вкладке / окне.

Проблема:

Поскольку все эти файлы не имеют расширения, мне пришлось заставить IIS просто обслуживать все как TIFF. Но если я сделаю это, файлы PDF не будут отображаться. Я мог бы изменить IIS, чтобы тип MIME был PDF для неизвестных расширений файлов, но у меня возникла бы обратная проблема.

http://support.microsoft.com/kb/326965

Эта проблема легче, чем я думаю, или она такая же неприятная, как я ожидал?

Ответы [ 8 ]

22 голосов
/ 28 апреля 2010

Хорошо, достаточно людей ошибаются, что я собираюсь опубликовать некоторый код, который я должен идентифицировать TIFF:

private const int kTiffTagLength = 12;
private const int kHeaderSize = 2;
private const int kMinimumTiffSize = 8;
private const byte kIntelMark = 0x49;
private const byte kMotorolaMark = 0x4d;
private const ushort kTiffMagicNumber = 42;


private bool IsTiff(Stream stm)
{
    stm.Seek(0);
    if (stm.Length < kMinimumTiffSize)
        return false;
    byte[] header = new byte[kHeaderSize];

    stm.Read(header, 0, header.Length);

    if (header[0] != header[1] || (header[0] != kIntelMark && header[0] != kMotorolaMark))
        return false;
    bool isIntel = header[0] == kIntelMark;

    ushort magicNumber = ReadShort(stm, isIntel);
    if (magicNumber != kTiffMagicNumber)
        return false;
    return true;
}

private ushort ReadShort(Stream stm, bool isIntel)
{
    byte[] b = new byte[2];
    _stm.Read(b, 0, b.Length);
    return ToShort(_isIntel, b[0], b[1]);
}

private static ushort ToShort(bool isIntel, byte b0, byte b1)
{
    if (isIntel)
    {
        return (ushort)(((int)b1 << 8) | (int)b0);
    }
    else
    {
        return (ushort)(((int)b0 << 8) | (int)b1);
    }
}

Я взломал немного более общий код, чтобы получить это.

Для PDF у меня есть код, который выглядит следующим образом:

public bool IsPdf(Stream stm)
{
    stm.Seek(0, SeekOrigin.Begin);
    PdfToken token;
    while ((token = GetToken(stm)) != null) 
    {
        if (token.TokenType == MLPdfTokenType.Comment) 
        {
            if (token.Text.StartsWith("%PDF-1.")) 
                return true;
        }
        if (stm.Position > 1024)
            break;
    }
    return false;
}

Теперь GetToken () - это вызов сканера, который токенизирует поток в токены PDF. Это нетривиально, поэтому я не собираюсь вставлять это здесь. Я использую токенизатор вместо того, чтобы смотреть на подстроку, чтобы избежать такой проблемы:

% the following is a PostScript file, NOT a PDF file
% you'll note that in our previous version, it started with %PDF-1.3,
% incorrectly marking it as a PDF
%
clippath stroke showpage

этот фрагмент кода помечен как НЕ PDF в приведенном выше фрагменте кода, тогда как более упрощенный фрагмент кода неверно пометит его как PDF.

Я должен также отметить, что текущая спецификация ISO не содержит замечаний по реализации, которые были в предыдущей спецификации, принадлежащей Adobe. Наиболее важно из PDF Reference, версия 1.6:

Acrobat viewers require only that the header appear somewhere within
the first 1024 bytes of the file.
8 голосов
/ 28 апреля 2010

TIFF можно обнаружить, посмотрев на первые байты http://local.wasp.uwa.edu.au/~pbourke/dataformats/tiff/

Первые 8 байтов образуют заголовок. Первые два байта которых либо «II» для байтового порядка или "ММ" для порядка байтов с прямым порядком байтов.

О PDF: http://www.adobe.com/devnet/livecycle/articles/lc_pdf_overview_format.pdf

Заголовок содержит только одну строку, которая определяет версию PDF. Пример:% PDF-1.6

4 голосов
/ 28 апреля 2010

Чтение спецификации для каждого формата файла расскажет вам, как идентифицировать файлы этого формата.

TIFF файлы - Проверьте байты 1 и 2 для 0x4D4D или 0x4949 и байты 2-3 для значения '42'.

Страница 13 спецификации гласит:

Файл TIFF начинается с 8-байтового заголовок файла изображения, содержащий следующая информация: Байты 0-1: порядок байтов, используемый в файле. легальный значения: «II» (4949.H) «ММ» (4D4D.H) В формате «II», байт порядок всегда от малейшего значительный байт для большинства значащий байт, как для 16-битных, так и для 32-разрядные целые числа Это называется порядок байтов с прямым порядком байтов. В «ММ» формат, порядок байтов всегда от большинства от значимого к наименее значимому, для 16-битные и 32-битные целые числа. это называется байтовым порядком байтов. Б 2-3 Произвольный, но тщательно подобранный номер (42), который дополнительно идентифицирует файл в виде файла TIFF. Байт порядок зависит от значения байтов 0-1.

PDF файлы начинаются с версии PDF, за которой следуют несколько двоичных байтов. (Я думаю, что теперь вам нужно приобрести спецификацию ISO для текущей версии.)

Раздел 7.5.2

Первая строка файла PDF должна быть заголовок, состоящий из 5 символы% PDF– с последующей версией номер формы 1.N, где N представляет собой цифра от 0 до 7. Соответствующая Читатель должен принимать файлы с любым из следующие заголовки:% PDF – 1.0, % PDF – 1.1,% PDF – 1.2,% PDF – 1.3,% PDF – 1.4, % PDF – 1,5,% PDF – 1,6,% PDF – 1,7 Начало с PDF 1.4, запись версии в словарь каталога документов (находится через корневую запись в файле трейлер, как описано в 7.5.5, «Файл» Трейлер "), если имеется, должен использоваться вместо версии, указанной в Заголовок.

Если файл PDF содержит двоичные данные, как большинство делают (см. 7.2, «Лексический Условные обозначения "), заголовок строки должен немедленно сопровождаться комментарием строка, содержащая как минимум четыре двоичных файла символы, то есть символы, чьи коды 128 или больше. Это обеспечивает правильное поведение при передаче файлов приложения, которые проверяют данные рядом начало файла для определения обрабатывать ли содержимое файла как текст или как двоичный файл.

Конечно, вы можете выполнить «более глубокую» проверку каждого файла, проверив больше элементов, относящихся к файлу.

3 голосов
/ 28 апреля 2010

Доступен очень полезный список сигнатур файлов, называемый «магические числа» Гэри Кесслера http://www.garykessler.net/library/file_sigs.html

3 голосов
/ 28 апреля 2010

Внутренне информация заголовка файла должна помочь. если вы открываете файл низкого уровня, такой как StreamReader () или FOPEN (), посмотрите на первые два символа в файле ... Почти каждый тип файла имеет свою собственную подпись.

PDF always starts with "%P" (but more specifically would have like %PDF)
TIFF appears to start with "II"
Bitmap files with "BM"
Executable files with "MZ"

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

РЕДАКТИРОВАТЬ - Прикрепленный пример кода для чтения и проверки типов заголовков файлов

String fn = "Example.pdf";

StreamReader sr = new StreamReader( fn );
char[] buf = new char[5];
sr.Read( buf, 0, 4);
sr.Close();
String Hdr = buf[0].ToString()
    + buf[1].ToString()
    + buf[2].ToString()
    + buf[3].ToString()
    + buf[4].ToString();

String WhatType;
if (Hdr.StartsWith("%PDF"))
   WhatType = "PDF";
else if (Hdr.StartsWith("MZ"))
   WhatType = "EXE or DLL";
else if (Hdr.StartsWith("BM"))
   WhatType = "BMP";
else if (Hdr.StartsWith("?_"))
   WhatType = "HLP (help file)";
else if (Hdr.StartsWith("\0\0\1"))
   WhatType = "Icon (.ico)";
else if (Hdr.StartsWith("\0\0\2"))
   WhatType = "Cursor (.cur)";
else
   WhatType = "Unknown";
1 голос
/ 28 апреля 2010

Если вы введете здесь , вы увидите, что TIFF обычно начинается с «магических чисел» 0x49 0x49 0x2A 0x00 (также приведены некоторые другие определения), то есть первые 4 байта файла.

Так что просто используйте эти первые 4 байта, чтобы определить, является ли файл TIFF или нет.

РЕДАКТИРОВАТЬ, вероятно, лучше сделать это другим способом и сначала обнаружить PDF. Магические числа для PDF более стандартизированы: как любезно указал Плинт, они начинаются с "% PDF" где-то в первых 1024 байтах (0x25 0x50 0x44 0x46). источник

0 голосов
/ 14 февраля 2018

вы можете использовать Myrmec для определения типа файла, эта библиотека использует заголовок файла. эта библиотека доступна на nuget "Myrmec", и это репозиторий, myrmec также поддерживает тип MIME, вы можете попробовать это. код будет выглядеть так:

// create a sniffer instance.
Sniffer sniffer = new Sniffer();

// populate with mata data.
sniffer.Populate(FileTypes.CommonFileTypes);

// get file head byte, may be 20 bytes enough.
byte[] fileHead = ReadFileHead();

// start match.
List<string> results = sniffer.Match(fileHead);

и получите MIME-тип:

List<string> result = sniffer.Match(head);

string mimeType = MimeTypes.GetMimeType (result.First ());

, но которые поддерживают только две подписи "49 49 2A 00" и "4D 4D 00 2A". Если у вас есть больше, вы можете добавить себя, возможно, вы можете обратиться к файлу readme myrmec за помощью. myrmec github repo

0 голосов
/ 28 апреля 2010

Вам нужно написать ashx, чтобы получить запрошенный файл.

тогда ваш обработчик должен прочитать первые несколько байтов (или около того), чтобы определить, какой тип файла на самом деле - PDF и TIFF имеют «магические числа» в начале файла, которые вы можете использовать для определения этого, затем соответственно установите заголовки ответа.

...