2012-02-20 28 views
15

No quiero depender de la extensión de archivo. No me importa saber qué tipo de imagen es (.jpg, .png, etc.), simplemente quiero saber si el archivo es una imagen o no. Preferiría no usar dlls que no sean .NET si es posible.¿Cómo puedo determinar si un archivo es un archivo de imagen en .NET?

La mejor manera que sé cómo hacer esto es el siguiente:

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

Como se ha señalado aquí: http://msdn.microsoft.com/en-us/library/stf701f5.aspx, Image.FromFile() lanza una OutOfMemoryException si el archivo no es un formato de imagen válida. Utilizando lo anterior me da exactamente el resultado que quiero, sin embargo yo prefiero no usarlo por las siguientes razones:

  • Es mi creencia de que el uso de Try-capturas para la ejecución normal del programa es una mala práctica por razones de rendimiento.
  • Image.FromFile() carga todo el archivo de imagen (si es un archivo de imagen) en la memoria. Esto es un desperdicio, supongo, porque solo necesito el tipo de archivo y no necesito hacer más manipulación de la imagen en este punto de mi código.
  • No me gusta atrapar OutOfMemoryException s porque, ¿qué ocurre si hay un problema REAL de memoria insuficiente y mi programa se lo traga y sigue funcionando?

¿Hay alguna forma mejor de hacerlo? O, ¿Alguna de mis inquietudes enumeradas anteriormente no está justificada?

Editar: Desde la recepción de las respuestas aquí, estos son los tres soluciones Ahora estoy consciente de:

  1. carga toda la imagen en la memoria a través de Image.FromFile() y un try-catch.
    • Pros: Hace una comprobación más profunda contra el contenido de los archivos de imagen; cubre muchos tipos de imágenes.
    • Contras: Más lento; sobrecarga desde try-catch y cargando el archivo de imagen completo en la memoria; peligro potencial de atrapar una 'OutOfMemoryException' real.
  2. Compruebe los bytes del encabezado del archivo de imagen.
    • Pros: Rápido, bajo uso de memoria.
    • Contras: potencialmente frágil; necesita programar para cada tipo de archivo.
  3. Compruebe la extensión del archivo.
    • Pros: Más rápido; más simple
    • Contras: No funciona en todas las situaciones; lo más fácil es equivocado

(no veo un "ganador" clara ya que puedo imaginar una situación en la que cada uno sería apropiado. Para los propósitos de mi solicitud, la comprobación de tipos de archivos sucede bastante infrecuente que los problemas de rendimiento de método 1 no eran un problema.)

+0

Ver también: http://stackoverflow.com/questions/670546/determine-if-file-is-an-image – Daryl

+0

Ver también # 2 : http://stackoverflow.com/questions/210650/validate-image-from-file-in-c-sharp – Daryl

+0

Vea también # 3: http://stackoverflow.com/questions/55869/determine-file-type- de-una-imagen – Daryl

Respuesta

7
  1. Sólo se dará cuenta de un impacto en el rendimiento de excepciones si usted está constantemente tirando de ellos. Entonces, a menos que su programa espere ver muchas imágenes inválidas (cientos por segundo), no debería notar la sobrecarga del manejo de excepciones.
  2. Esta es realmente la única manera de saber si la imagen es una imagen completa o está dañada. Puede verificar los encabezados como las otras personas lo recomiendan, pero eso solo verifica si los primeros bytes son correctos, cualquier otra cosa podría ser basura. Si esto es lo suficientemente bueno o no depende de los requisitos de su aplicación. Solo leer el encabezado podría ser lo suficientemente bueno para su caso de uso.
  3. Sí, este es un diseño bastante pobre por parte del equipo de BCL. Si está cargando muchas imágenes grandes, muy bien podría estar alcanzando una situación OOM real en el montón de objetos grandes. Hasta donde yo sé, no hay forma de diferenciar las dos excepciones.
+0

Gracias por la información. Decidí por mi situación que el golpe de rendimiento no fue significativo. – Daryl

10

Si sólo va a apoyar un puñado de los formatos de imagen más populares, entonces puede simplemente leer los primeros bytes del archivo para determinar el tipo basado en la Magic Number

Ejemplos del enlace proporcionado:

  • archivos de imágenes GIF tienen el código ASCII para "89a" (47 49 46 38 39 61) o "GIF87a" (47 49 46 38 37 61)
  • archivos de imagen JPEG comenzar con FF D8 y terminar con FF D9. Los archivos JPEG/JFIF contienen el código ASCII para "JFIF" (4A 46 49 46) como una cadena terminada nula.
  • Los archivos de imagen PNG comienzan con una firma de 8 bytes que identifica el archivo como un archivo PNG y permite la detección de problemas comunes de transferencia de archivos: \ 211 PNG \ r \ n \ 032 \ n (89 50 4E 47 0D 0A 1A 0A)
+1

Gracias por la información; No sabía que esto podría hacerse. Escribí una implementación y la publiqué aquí como una wiki. – Daryl

+0

Bonito post ......... – Ansari

1

Primero use el método System.IO.Path.GetExtension() para comprobar si la extensión es un tipo de imagen. Luego, si desea terminar, puede verificar los encabezados en el archivo.

+0

¿Qué pasa si no hay una extensión? – Coding4Fun

2

Puedo entender sus inquietudes, pero si mira el origen del método Image.FromFile, es solo envoltorio para llamadas GDI +, por lo que lamentablemente no puede hacer nada, ya que puedo ver esa rara elección de excepción (OutOfMemoryException) se realizó en GDI +

Parece que está atascado con el código actual o revisa los encabezados de los archivos, pero eso no garantiza que el archivo sea realmente una imagen válida.

Tal vez deberías considerar ¿realmente necesitas el método de ImageFile en primer lugar? Detecta archivos de imagen en la extensión, sería mucho más rápido y si la carga del archivo falla generará una excepción para que puedas manejarla cuando realmente necesites cargar la imagen.

3

Tomando la ruta de la comprobación de la cabecera del archivo, escribí esta implementación:

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

pongo esto en una clase de utilidad/ayudante junto con métodos: GetFileImageTypeFromFullLoad() y GetFileImageTypeFromExtension().El primero usa mi enfoque Image.FromFile mencionado anteriormente y este último simplemente comprueba la extensión del archivo. Planeo usar los tres dependiendo de los requisitos de la situación.

+0

Aquí hay otro que encontré. Básicamente es lo mismo, pero es un poco más pequeño: – Justin

+0

@Panuvin, ¿se olvidó de incluir un enlace? – Daryl

2

aquí está uno que utiliza las firmas en 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; 
} 
Cuestiones relacionadas