2010-04-28 12 views
16

Si prega di sopportare me come sono stato gettato nel mezzo di questo progetto senza conoscere tutto il background. Se hai domande sul WTF, fidati di me, ce l'ho anche io.Come rilevare se un file è PDF o TIFF?

Ecco lo scenario: ho un sacco di file che risiedono su un server IIS. Non hanno estensione di file su di loro. Solo file nudi con nomi come "asda-2342-sd3rs-asd24-ut57" e così via. Niente di intuitivo.

Il problema è che ho bisogno di servire file su una pagina ASP.NET (2.0) e visualizzare i file tiff come tiff e i file PDF come PDF. Sfortunatamente non so quale sia quale e ho bisogno di essere in grado di visualizzarli in modo appropriato nei rispettivi formati.

Ad esempio, diciamo che ci sono 2 file che devo visualizzare, uno è tiff e uno è PDF. La pagina dovrebbe apparire con un'immagine tiff, e forse un collegamento che aprirebbe il PDF in una nuova scheda/finestra.

Il problema:

Poiché questi file sono tutti estensione-less ho dovuto forzare IIS per servire proprio tutto come TIFF. Ma se lo faccio, i file PDF non verranno visualizzati. Potrei cambiare IIS per forzare il tipo MIME ad essere PDF per estensioni di file sconosciute ma avrei il problema inverso.

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

È questo il problema più facile che penso o è come brutto come mi aspetto?

risposta

19

OK, numero sufficiente di persone stanno ottenendo questo torto che ho intenzione di inviare un po 'di codice che ho per identificare 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); 
    } 
} 

ho inciso a parte qualche codice molto più generale per ottenere questo.

Per PDF, Ho codice che assomiglia a questo:

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

Ora, GetToken() è una chiamata in uno scanner che tokenizza un flusso in token PDF. Questo non è banale, quindi non ho intenzione di incollarlo qui. Sto usando il tokenizzatore invece di guardare a stringa per evitare un problema come questo:

% 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 

questo codice è contrassegnato come NOT un PDF dal frammento di codice di cui sopra, mentre un pezzo più semplicistico di codice errato segnarlo come un PDF.

Devo anche sottolineare che l'attuale specifica ISO è priva delle note di implementazione che erano nella specifica precedente posseduta da Adobe. La cosa più importante dal PDF Reference, versione 1.6:

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

grazie! Verificherò stasera – eviljack

+0

fantastico, funziona !! – eviljack

+0

stm.Seek (0); fallisce per me, non compila. Sto usando vs 2008, .net 3.5. – Kiquenet

0

Se si passa a here, si vedrà che il TIFF di solito inizia con "numeri magici" 0x49 0x49 0x2A 0x00 (sono fornite anche alcune altre definizioni), ovvero i primi 4 byte del file.

Quindi utilizzare questi primi 4 byte per determinare se il file è TIFF o meno.

EDIT, probabilmente è meglio farlo in un altro modo e rilevare prima il PDF. I numeri magici per PDF sono più standardizzati: come ha sottolineato Plinth, iniziano con "% PDF" da qualche parte nei primi 1024 byte (0x25 0x50 0x44 0x46). source

+0

questi numeri magici dipendono dal piccolo/grande endian. – Andrey

+1

Questo è vicino, ma sbagliato. Un TIFF inizia con una delle due firme, 0x49 0x49 0x2a 0x00 O 0x4d 0x4d 0x00 0x2a. – plinth

+0

Anche il controllo PDF è sbagliato. Il% PDF deve apparire solo nei primi 1024 byte. – plinth

8

TIFF può essere rilevato da peeking dapprima byte http://local.wasp.uwa.edu.au/~pbourke/dataformats/tiff/

I primi 8 byte forma dell'intestazione. I primi due byte dei quali sono "II" per il byte little endian che ordina o "MM" per l'ordine di byte big endian.

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

L'intestazione contiene una sola riga che identifica la versione di PDF. Esempio:% PDF-1.6

+0

Il documento da Adobe non è esattamente la specifica esatta. % PDF-1.x, dove x è un numero può apparire ovunque all'interno del primo 1K del file. – plinth

+0

ok, ecco le specifiche più complete http://www.adobe.com/devnet/acrobat/pdfs/pdf_reference_1-7.pdf è> 30 Mb – Andrey

2

All'interno, le informazioni dell'intestazione del file devono essere d'aiuto. se si fa aprire un file di basso livello, come StreamReader() o FOPEN(), guarda i primi due caratteri nel file ... Quasi ogni tipo di file ha la propria firma.

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" 

ho avuto a che fare con questo in passato troppo ... anche per aiutare a prevenire i file indesiderati dal caricato ad un determinato sito e subito abortire una volta selezionata.

EDIT - Pubblicato il codice di esempio per leggere e file di prova tipi di intestazione

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, qualsiasi codice di esempio ?? – Kiquenet

+0

@alhambraeidos - Ho inserito il codice aggiornato tramite l'esempio C# – DRapp

+0

Non dovrebbe scrivere "sembra iniziare con" nella parte critica della risposta! Per specifica, i file TIFF iniziano con 2 byte di ASCII "II" o "MM", seguiti da 2 byte in (II) Intel little-endian, o (MM) Motorola big-endian byte order, formando l'intero 42. – Spike0xff

0

Si stanno per avere di scrivere un ASHX per ottenere il file richiesto.

quindi, il gestore deve leggere i primi pochi byte (o così) per determinare qual è il tipo di file in realtà: PDF e TIFF hanno "numeri magici" all'inizio del file che è possibile utilizzare per determinarlo, quindi imposta le tue intestazioni di risposta di conseguenza.

4

Leggendo le specifiche per ogni formato di file vi dirà come per identificare i file di questo formato.

TIFF file - Controllare i byte 1 e 2 per 0x4D4D o 0x4949 e byte 2-3 per il valore '42 '.

Page 13 della specifica legge:

file di

Un TIFF inizia con un'intestazione file immagine 8 byte, che contiene il seguenti informazioni: Byte 0-1: L'ordine byte utilizzato all'interno del file . I valori legali sono: "II" (4949.H) "MM" (4D4D.H) Nel formato "II", l'ordine byte è sempre dal minimo byte significativo al byte significativo più , per entrambi i 16 -bit e Numeri interi a 32 bit Si chiama ordine byte little-endian. Nel formato "MM" , l'ordine dei byte è sempre dalla maggior parte di significativo o meno significativo, per entrambi gli interi a 16 bit e 32 bit. Questo è chiamato ordine byte big-endian. Byte 2-3 Un numero arbitrario ma accuratamente scelto di numero (42) che identifica ulteriormente il file come file TIFF . L'ordine del byte dipende dal valore di Byte 0-1.

PDF i file iniziano con la versione PDF seguita da diversi byte binari. (Penso che ora necessario acquistare le specifiche ISO per la versione corrente.)

sezione 7.5.2

La prima riga di un file PDF sarà un colpo di testa costituito dai 5 caratteri% PDF- seguito da una versione numero del modulo 1.N, dove N è una cifra compresa tra 0 e 7. Un lettore conforme accetta i file con qualsiasi le seguenti intestazioni:% PDF-1.0, % PDF- 1.1,% PDF-1.2,% PDF-1.3,% PDF-1.4, % PDF-1.5,% PDF-1.6,% PDF-1.7 A partire da con PDF 1.4, la voce Versione nel dizionario del catalogo del documento tramite il comando Root nel file del file , come descritto in 7.5.5, "File Trailer", se presente, deve essere utilizzata anziché la versione specificata in l'intestazione.

Se un file PDF contiene dati binari, come la maggior parte non (si veda 7.2, "lessicali convenzioni"), la riga di intestazione deve essere immediatamente seguita da una riga di commento contenente almeno quattro binari caratteri, cioè , caratteri i cui codici sono 128 o superiori. Ciò garantisce il corretto comportamento dello di trasferimento file l'inizio di un file per determinare l'inizio di un file per determinare se trattare il contenuto del file come testo o come binario.

Ovviamente è possibile eseguire un controllo "approfondito" su ciascun file controllando più elementi specifici del file.

+0

qualsiasi codice di esempio, roygbiv? – Kiquenet

0

è possibile utilizzare Myrmec per identificare il tipo di file, questa biblioteca usa la testa di file di byte. questa libreria disponibile su nuget "Myrmec", e questo è il repository, myrmec supporta anche il tipo mime, puoi provarlo. il codice come questo:

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

e ricevere tipo MIME:

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

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

ma che supportano solo "49 49 2A 00" e "4D 4D 00 2A" due firme, se ne hai di più puoi aggiungere te stesso, potrebbe essere che tu possa vedere il file readme di myrmec per aiuto. myrmec github repo

Problemi correlati