2012-02-20 14 views
15

Non voglio fare affidamento sull'estensione del file. Non mi interessa sapere che tipo di immagine è (.jpg, .png, ecc.), Voglio semplicemente sapere se il file è un'immagine o meno. Preferirei non utilizzare eventuali dll non.NET, se possibile.Come posso determinare se un file è un file immagine in .NET?

Il modo migliore che conosco come fare questo è la seguente:

bool isImageFile; 
try 
{ 
    Image.FromFile(imageFile).Dispose(); 
    isImageFile = true; 
} 
catch (OutOfMemoryException) 
{ 
    isImageFile = false; 
} 

Come notato qui: http://msdn.microsoft.com/en-us/library/stf701f5.aspx, Image.FromFile() genera OutOfMemoryException se il file non è un formato immagine valido. Utilizzando quanto sopra mi dà esattamente il risultato che voglio, però io preferisco non usarlo per le seguenti ragioni:

  • E 'mia convinzione che l'utilizzo di try-catture per la normale esecuzione del programma è una cattiva pratica per motivi di prestazioni.
  • Image.FromFile() carica l'intero file di immagine (se è un file di immagine) nella memoria. Questo è uno spreco presumo perché ho solo bisogno del tipo di file e non ho bisogno di fare ulteriori manipolazioni di immagine a questo punto nel mio codice.
  • Non mi piace prendere lo OutOfMemoryException s perché se c'è un vero problema di memoria esaurita e il mio programma lo inghiotte e continua a funzionare?

Ci sono modi migliori per fare questo? Oppure, sono alcuni/tutti i miei dubbi sopra elencati ingiustificati?

Edit: Da quando abbiamo ricevuto le risposte qui, queste sono le soluzionitre Ora sono a conoscenza di:

  1. carico l'intera immagine in memoria tramite Image.FromFile() e un try-catch.
    • Pro: esegue un controllo più approfondito del contenuto dei file di immagine; copre molti tipi di immagine.
    • Contro: Più lento; sovraccarico di try-catch e caricamento di file immagine completi in memoria; potenziale pericolo derivante da una "reale" OutOfMemoryException.
  2. Controllare i byte di intestazione del file immagine.
    • Pro: utilizzo della memoria basso e veloce.
    • Contro: potenzialmente fragile; bisogno di programmare per ogni tipo di file.
  3. Controllare l'estensione del file.
    • Pro: Più rapido; più semplice.
    • Contro: non funziona in tutte le situazioni; più facilmente sbagliato.

(non vedo un chiaro "vincitore" dal momento che posso immaginare una situazione in cui ognuno sarebbe appropriato. Per gli scopi della mia applicazione, il controllo del tipo di file accade abbastanza di rado che i problemi di prestazioni di metodo 1 non erano un problema.)

+0

Vedi anche: http://stackoverflow.com/questions/670546/determine-if-file-is-an-image – Daryl

+0

Vedi anche 2 # : http://stackoverflow.com/questions/210650/validate-image-from-file-in-c-sharp – Daryl

+0

Vedere anche # 3: http://stackoverflow.com/questions/55869/determine-file-type- of-an-image – Daryl

risposta

7
  1. Si noterà solo un calo di prestazioni da eccezioni se si è costantemente gettarli. Quindi, a meno che il tuo programma non veda molte immagini non valide (centinaia al secondo) non dovresti notare il sovraccarico della gestione delle eccezioni.
  2. Questo è davvero l'unico modo per dire se l'immagine è un'immagine intera o è corrotta. Puoi controllare le intestazioni come suggeriscono le altre persone, ma questo controlla solo se l'inizio di pochi byte è corretto, qualsiasi altra cosa potrebbe essere spazzatura. Se questo è abbastanza buono o meno dipende dai requisiti della vostra applicazione. Basta leggere l'intestazione potrebbe essere abbastanza buono per il tuo caso d'uso.
  3. Sì, questo è un design piuttosto scarso da parte del team BCL. Se si caricano molte immagini di grandi dimensioni, è possibile che si verifichi una situazione OOM reale nell'heap di oggetti di grandi dimensioni. Per quanto ne so, non c'è modo di differenziare le due eccezioni.
+0

Grazie per le informazioni. Ho deciso per la mia situazione che il successo della performance non fosse significativo. – Daryl

10

Se sosterrai solo una manciata di formati di immagine più diffusi, allora si può semplicemente leggere i primi byte del file per determinare il tipo basato sulla Magic Number

Esempi dal link fornito:

  • file di immagine GIF hanno il codice ASCII per "GIF89a" (47 49 46 38 39 61) o "GIF87a" (47 49 46 38 37 61)
  • file di immagine JPEG cominciare FF D8 e terminare con FF D9. I file JPEG/JFIF contengono il codice ASCII per "JFIF" (4A 46 49 46) come stringa terminata da null.
  • I file di immagine PNG iniziano con una firma a 8 byte che identifica il file come file PNG e consente di rilevare i problemi di trasferimento file più comuni: \ 211 PNG \ r \ n \ 032 \ n (89 50 4E 47 0D 0A 1A 0A).
+1

Grazie per le informazioni; Non ero a conoscenza che ciò potesse essere fatto. Ho scritto un'implementazione e l'ho postata qui come wiki. – Daryl

+0

Nice post ......... – Ansari

1

Utilizzare innanzitutto il metodo System.IO.Path.GetExtension() per verificare se Extension è un tipo di immagine. Quindi se vuoi passare attraverso puoi controllare le intestazioni nel file.

+0

Cosa succede se non ci sono estensioni? – Coding4Fun

2

Riesco a capire le tue preoccupazioni, ma se guardi l'origine del metodo Image.FromFile, è solo un wrapper per le chiamate GDI +, quindi purtroppo non puoi fare nulla, dal momento che posso vedere che bizzare la scelta dell'eccezione (OutOfMemoryException) è stato eseguito in GDI +

Quindi sembra che tu sia bloccato con il codice corrente o controlli le intestazioni dei file, ma ciò non garantisce che il file sia realmente un'immagine valida.

Forse dovresti considerare che hai davvero bisogno del metodo isImageFile? Rileva i file di immagine sull'estensione, sarebbe molto più veloce e se il caricamento da file fallisce, solleverà un'eccezione in modo che tu possa occupartene quando hai davvero bisogno di caricare l'immagine.

3

Prendendo il percorso di controllare l'intestazione del file, ho scritto questa implementazione:

public static ImageType GetFileImageTypeFromHeader(string file) 
    { 
     byte[] headerBytes; 
     using (FileStream fileStream = new FileStream(file, FileMode.Open)) 
     { 
      const int mostBytesNeeded = 11;//For JPEG 

      if (fileStream.Length < mostBytesNeeded) 
       return ImageType.Unknown; 

      headerBytes = new byte[mostBytesNeeded]; 
      fileStream.Read(headerBytes, 0, mostBytesNeeded); 
     } 

     //Sources: 
     //http://stackoverflow.com/questions/9354747 
     //http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Magic_numbers_in_files 
     //http://www.mikekunz.com/image_file_header.html 

     //JPEG: 
     if (headerBytes[0] == 0xFF &&//FF D8 
      headerBytes[1] == 0xD8 && 
      (
      (headerBytes[6] == 0x4A &&//'JFIF' 
       headerBytes[7] == 0x46 && 
       headerBytes[8] == 0x49 && 
       headerBytes[9] == 0x46) 
       || 
      (headerBytes[6] == 0x45 &&//'EXIF' 
       headerBytes[7] == 0x78 && 
       headerBytes[8] == 0x69 && 
       headerBytes[9] == 0x66) 
      ) && 
      headerBytes[10] == 00) 
     { 
      return ImageType.JPEG; 
     } 
     //PNG 
     if (headerBytes[0] == 0x89 && //89 50 4E 47 0D 0A 1A 0A 
      headerBytes[1] == 0x50 && 
      headerBytes[2] == 0x4E && 
      headerBytes[3] == 0x47 && 
      headerBytes[4] == 0x0D && 
      headerBytes[5] == 0x0A && 
      headerBytes[6] == 0x1A && 
      headerBytes[7] == 0x0A) 
     { 
      return ImageType.PNG; 
     } 
     //GIF 
     if (headerBytes[0] == 0x47 &&//'GIF' 
      headerBytes[1] == 0x49 && 
      headerBytes[2] == 0x46) 
     { 
      return ImageType.GIF; 
     } 
     //BMP 
     if (headerBytes[0] == 0x42 &&//42 4D 
      headerBytes[1] == 0x4D) 
     { 
      return ImageType.BMP; 
     } 
     //TIFF 
     if ((headerBytes[0] == 0x49 &&//49 49 2A 00 
      headerBytes[1] == 0x49 && 
      headerBytes[2] == 0x2A && 
      headerBytes[3] == 0x00) 
      || 
      (headerBytes[0] == 0x4D &&//4D 4D 00 2A 
      headerBytes[1] == 0x4D && 
      headerBytes[2] == 0x00 && 
      headerBytes[3] == 0x2A)) 
     { 
      return ImageType.TIFF; 
     } 

     return ImageType.Unknown; 
    } 
    public enum ImageType 
    { 
     Unknown, 
     JPEG, 
     PNG, 
     GIF, 
     BMP, 
     TIFF, 
    } 

ho messo questo in una classe di utilità/helper insieme con i metodi: GetFileImageTypeFromFullLoad() e GetFileImageTypeFromExtension().Il primo utilizza il mio approccio sopra menzionato Image.FromFile e quest'ultimo semplicemente controlla l'estensione del file. Ho intenzione di utilizzare tutti e tre a seconda dei requisiti della situazione.

+0

Ecco un altro che ho trovato. Fa praticamente la stessa cosa, ma è un po 'più piccolo: – Justin

+0

@Panuvin, hai dimenticato di includere un link? – Daryl

2

ecco uno che utilizza le firme in GDI +:

public static ImageCodecInfo DetectCodec(Stream stream) 
{ 
    var ib = 0; 

    var rgCodecs = ImageCodecInfo.GetImageDecoders(); 
    for (var cCodecs = rgCodecs.Length; cCodecs > 0;) 
    { 
     var b = stream.ReadByte(); 
     if (b == -1) 
      return null; // EOF 

     for (int iCodec = cCodecs - 1; iCodec >= 0; iCodec--) 
     { 
      var codec = rgCodecs[iCodec]; 
      for (int iSig = 0; iSig < codec.SignaturePatterns.Length; iSig++) 
      { 
       var mask = codec.SignatureMasks[iSig]; 
       var patt = codec.SignaturePatterns[iSig]; 

       if (ib >= patt.Length) 
        return codec; 

       if ((b & mask[ib]) != patt[ib]) 
       { 
        rgCodecs[iCodec] = rgCodecs[--cCodecs]; 
        break; 
       } 
      } 
     } 

     ib++; 
    } 

    return null; 
} 
Problemi correlati