2010-04-28 6 views
16

Bitte ertragen Sie mich, wie ich in die Mitte dieses Projekts geworfen wurde, ohne den Hintergrund zu kennen. Wenn du WTF-Fragen hast, vertrau mir, ich habe sie auch.Wie erkennt man, ob eine Datei PDF oder TIFF ist?

Hier ist das Szenario: Ich habe eine Reihe von Dateien auf einem IIS-Server. Sie haben keine Dateiendung auf ihnen. Nur nackte Dateien mit Namen wie "asda-2342-sd3rs-asd24-ut57" und so weiter. Nichts intuitives.

Das Problem ist, ich muss Dateien auf einer ASP.NET (2.0) -Seite liefern und die Tiff-Dateien als TIFF und die PDF-Dateien als PDF anzeigen. Leider weiß ich nicht welche welche ist und ich muss sie in ihren jeweiligen Formaten passend anzeigen können.

Zum Beispiel sagen wir, dass es zwei Dateien gibt, die ich anzeigen muss, eins ist tiff und eins ist PDF. Die Seite sollte mit einem TIFF-Bild und vielleicht einem Link angezeigt werden, der das PDF in einem neuen Tab/Fenster öffnet.

Das Problem:

Da diese Dateien sind alle Erweiterungs weniger hatte ich IIS zu zwingen, nur alles als TIFF dienen oben. Aber wenn ich das tue, werden die PDF-Dateien nicht angezeigt. Ich könnte IIS ändern, um zu erzwingen, dass der MIME-Typ für unbekannte Dateierweiterungen PDF ist, aber ich hätte das umgekehrte Problem.

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

Ist das Problem einfacher, als ich denke, oder ist es so böse, wie ich erwarte?

Antwort

19

OK, genug, um die Menschen immer dies falsch, dass ich einige Code schreiben werde ich habe TIFFs zu identifizieren:

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); 
    } 
} 

ich gehackt etwas viel allgemeinere Code auseinander, dieses zu erhalten.

Für PDF, ich habe Code, der wie folgt aussieht:

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; 
} 

Nun GetToken() ist ein Anruf in einen Scanner, der einen Strom in der PDF-Token tokenizes. Das ist nicht trivial, also werde ich es hier nicht einfügen. Ich verwende den tokenizer stattdessen bei String sucht ein Problem wie diese zu vermeiden:

% 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 

dieser Code wird als NICHT PDF durch das obige Codefragment markiert, während ein stark vereinfach Stück Code falsch es markieren wird als PDF.

Ich sollte auch darauf hinweisen, dass die aktuelle ISO-Spezifikation keine Implementierungshinweise enthält, die in der früheren von Adobe gehaltenen Spezifikation enthalten waren. Am wichtigsten ist aus der PDF Reference, Version 1.6:

Acrobat viewers require only that the header appear somewhere within 
the first 1024 bytes of the file. 
+0

danke! Ich überprüfe das heute Abend – eviljack

+0

genial, es funktioniert !! – eviljack

+0

stm.Seek (0); scheitert für mich, nicht kompiliert. Ich benutze vs 2008, .net 3.5. – Kiquenet

0

Wenn Sie gehen here, werden Sie sehen, dass die TIFF beginnt normalerweise mit "magische Zahlen" 0x49 0x49 0x2A 0x00 (einige andere Definitionen sind auch angegeben), die die ersten 4 Bytes der Datei ist.

Verwenden Sie also diese ersten 4 Bytes, um festzustellen, ob die Datei TIFF ist oder nicht.

BEARBEITEN, ist es wahrscheinlich besser, es anders zu machen, und PDF zuerst zu erkennen. Die magischen Zahlen für PDF sind standardisierter: Wie Plinth freundlicherweise darauf hingewiesen hat, beginnen sie mit "% PDF" irgendwo in den ersten 1024 Bytes (0x25 0x50 0x44 0x46). source

+0

Diese magischen Zahlen hängen von Little/Big Endian ab. – Andrey

+1

Das ist knapp, aber falsch. Ein TIFF beginnt mit einer von zwei Signaturen, 0x49 0x49 0x2a 0x00 ODER 0x4d 0x4d 0x00 0x2a. – plinth

+0

Ihre PDF-Prüfung ist ebenfalls falsch. Das% PDF muss nur in den ersten 1024 Bytes angezeigt werden. – plinth

8

TIFF kann durch Spähen auf den ersten Bytes http://local.wasp.uwa.edu.au/~pbourke/dataformats/tiff/

Die ersten 8 Bytes des Headers bildet, detektiert werden. Die ersten beiden Bytes davon sind entweder "II" für kleine Endian-Byte-Reihenfolge oder "MM" für Big-Endian-Byte-Reihenfolge.

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

Der Header enthält nur eine Zeile, die die Version von PDF identifiziert. Beispiel:% PDF-1.6

+0

Das Dokument von Adobe ist nicht genau die genaue Spezifikation. % PDF-1.x, wobei x für eine Zahl steht, die irgendwo innerhalb der ersten 1K der Datei erscheint. – plinth

+0

ok, hier ist die komplette Spezifikation http://www.adobe.com/devnet/acrobat/pdfs/pdf_reference_1-7.pdf ist es> 30 Mb – Andrey

2

Intern sollten die Dateikopfinformationen helfen. Wenn Sie eine Datei auf niedriger Ebene wie StreamReader() oder FOPEN() öffnen, sehen Sie sich die ersten beiden Zeichen in der Datei an ... Fast jeder Dateityp hat seine eigene Signatur.

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" 

Ich habe auch mit diesem in der Vergangenheit zu tun habe ... auch unerwünschte Dateien von auf eine bestimmte Website hochgeladen werden und sofort abgebrochen wird es einmal geprüft, um zu verhindern.

EDIT - Vor Beispielcode zu lesen und zu Testdatei Headertypen

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"; 
+0

mr.DRapp, irgendein Beispielcode ?? – Kiquenet

+0

@alhambraeidos - Ich postete aktualisierten Code über C# Beispiel – DRapp

+0

Sollte nicht geschrieben werden "scheint mit" im kritischen Teil der Antwort zu beginnen! Pro Spezifikation beginnen TIFF-Dateien mit 2 Bytes ASCII "II" oder "MM", gefolgt von 2 Bytes in (II) Intel-Little-Endian oder (MM) Motorola-Big-Endian-Byte-Reihenfolge und bilden die ganze Zahl 42. – Spike0xff

0

Sie gehen zu müssen, eine Ashx schreiben, um die Datei angefordert zu bekommen.

dann sollte Ihr Handler die ersten paar Bytes (oder so) lesen, um festzustellen, was der Dateityp wirklich ist-- PDF und TIFF haben "magic numers" am Anfang der Datei, die Sie verwenden können, um dies zu bestimmen, Setzen Sie dann Ihre Antwortheader entsprechend.

4

die Spezifikation für jedes Format Datei Lesen werden Ihnen sagen, wie Sie Dateien dieses Formats zu identifizieren.

TIFF Dateien - Überprüfen Sie die Bytes 1 und 2 für 0x4D4D oder 0x4949 und Bytes 2-3 für den Wert '42'.

Seite 13 der Spezifikation lautet:

Eine TIFF-Datei beginnt mit einem 8-Byte Datei-Header-Bild, das folgende Informationen enthalten: Bytes 0-1: innerhalb der Datei verwendet, die Byte-Reihenfolge . Legale Werte sind: "II" (4949.H) "MM" (4D4D.H) Im "II" -Format, Byte Reihenfolge ist immer von der Signifikanz-Byte zu den meisten signifikanten Byte, für beide 16 -Bit und 32-Bit-Ganzzahlen Dies wird Little-Endian Byte-Reihenfolge genannt. Im Format "MM" ist die Byte-Reihenfolge immer von signifikant bis niedrigstwertig, für sowohl 16-Bit- als auch 32-Bit-Ganzzahlen. Diese wird Big-Endian-Byte-Reihenfolge genannt. Bytes 2-3 Eine beliebige, aber sorgfältig ausgewählte Nummer (42), die die Datei als TIFF-Datei identifiziert. Das Byte Reihenfolge ist abhängig vom Wert von Bytes 0-1.

PDF Dateien beginnen mit der PDF-Version von mehreren Binärbytes gefolgt. (Ich glaube, Sie müssen jetzt die ISO-Spezifikation für die aktuelle Version erwerben.)

Abschnitt 7.5.2

Die erste Zeile einer PDF-Datei sein wird ein Kopf, der aus den 5 Zeichen% PDF- gefolgt von einer Version Nummer des Formulars 1.N, wobei N eine Ziffer zwischen 0 und 7 ist. Ein konformes Lesegerät akzeptiert Dateien mit einem der folgenden Header: % PDF-1.0, % PDF- 1.1,% PDF-1.2,% PDF-1.3,% PDF-1.4, % PDF-1.5,% PDF-1.6,% PDF-1.7 Anfang mit PDF 1.4, die Version Eintrag in Dokument Katalog Wörterbuch (befindet sich über den Root-Eintrag in der Datei Anhänger, wie in 7.5.5, "Datei Trailer" beschrieben), falls vorhanden, statt der angegebenen Version verwendet werden in der Header.

Wenn eine PDF-Datei binäre Daten enthält, als die meisten tun, die Kopfzeile (7.2, „Lexical Konventionen“ sehen) wird werden sofort durch einen Kommentar Linie gefolgt mindestens vier binäre die Zeichen enthalten, dh Zeichen, deren Codes 128 oder größer sind. Dies gewährleistet ordnungsgemäße Verhalten der Dateiübertragung Anwendungen, die Daten in der Nähe von den Anfang einer Datei zu prüfen, um zu bestimmen, ob der Inhalt der Datei als Text oder als Binärdatei behandelt werden soll.

Natürlich könnten Sie eine "tiefere" Prüfung für jede Datei durchführen, indem Sie weitere dateispezifische Elemente überprüfen.

+0

beliebiger Beispielcode, roygbiv? – Kiquenet

0

Sie können Myrmec verwenden Sie den Dateityp zu identifizieren, verwenden Sie diese Bibliothek die Datei Byte Kopf. Diese Bibliothek verfügbar auf nugget "Myrmec", und das ist der Repo, Myrmec auch Mime-Typ unterstützen, können Sie es versuchen. der Code wie folgt aus:

// 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); 

und Mime-Typ erhalten:

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

String mime = MimeTypes.GetMimeType (result.First());

aber die Unterstützung TIFF nur "49 49 2A 00" und "4D 4D 00 2A" zwei Signatur, wenn Sie mehr haben, können Sie sich selbst hinzufügen, können Sie die Readme-Datei von Myrmec für Hilfe sehen. myrmec github repo