web-dev-qa-db-fra.com

déterminer si le fichier est une image

Je suis en train de parcourir un répertoire et de copier tous les fichiers. En ce moment, je vérifie string.EndsWith pour ".jpg" ou ".png", etc. .

Existe-t-il un moyen plus élégant de déterminer si un fichier est une image (n'importe quel type d'image) sans la vérification de hacky comme ci-dessus?

37
leora

Recherchez dans le fichier un en-tête connu . (Info du lien également mentionné dans cette réponse )

Les huit premiers octets d'un fichier PNG contiennent toujours les valeurs (décimales) suivantes: 137 80 78 71 13 10 26 10

30
Mitch Wheat

Départ System.IO.Path.GetExtension

Voici un échantillon rapide.

public static readonly List<string> ImageExtensions = new List<string> { ".JPG", ".JPE", ".BMP", ".GIF", ".PNG" };

private void button_Click(object sender, RoutedEventArgs e)
{
    var folder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
    var files = Directory.GetFiles(folder);
    foreach(var f in files)
    {
        if (ImageExtensions.Contains(Path.GetExtension(f).ToUpperInvariant()))
        {
            // process image
        }
    }
}
24
bendewey

Cela examinera les premiers octets d'un fichier et déterminera s'il s'agit d'une image.

using System.Collections.Generic;
using System.IO;
using System.Linq;

public static class Extension
{
    public static bool IsImage(this Stream stream)
    {
        stream.Seek(0, SeekOrigin.Begin);

        List<string> jpg = new List<string> { "FF", "D8" };
        List<string> bmp = new List<string> { "42", "4D" };
        List<string> gif = new List<string> { "47", "49", "46" };
        List<string> png = new List<string> { "89", "50", "4E", "47", "0D", "0A", "1A", "0A" };
        List<List<string>> imgTypes = new List<List<string>> { jpg, bmp, gif, png };

        List<string> bytesIterated = new List<string>();

        for (int i = 0; i < 8; i++)
        {
            string bit = stream.ReadByte().ToString("X2");
            bytesIterated.Add(bit);

            bool isImage = imgTypes.Any(img => !img.Except(bytesIterated).Any());
            if (isImage)
            {
                return true;
            }
        }

        return false;
    }
}

Modifier

J'ai apporté quelques modifications à ce qui précède pour vous permettre d'ajouter vos propres images si nécessaire, ainsi que des collections supprimées qui n'étaient pas nécessaires pour commencer. J'ai également ajouté une surcharge acceptant un paramètre out de type string, en définissant la valeur sur le type d'image composant le flux.

public static class Extension
{
    static Extension()
    {
        ImageTypes = new Dictionary<string, string>();
        ImageTypes.Add("FFD8","jpg");
        ImageTypes.Add("424D","bmp");
        ImageTypes.Add("474946","gif");
        ImageTypes.Add("89504E470D0A1A0A","png");
    }

    /// <summary>
    ///     <para> Registers a hexadecimal value used for a given image type </para>
    ///     <param name="imageType"> The type of image, example: "png" </param>
    ///     <param name="uniqueHeaderAsHex"> The type of image, example: "89504E470D0A1A0A" </param>
    /// </summary>
    public static void RegisterImageHeaderSignature(string imageType, string uniqueHeaderAsHex)
    {
        Regex validator = new Regex(@"^[A-F0-9]+$", RegexOptions.CultureInvariant);

        uniqueHeaderAsHex = uniqueHeaderAsHex.Replace(" ", "");

        if (string.IsNullOrWhiteSpace(imageType))         throw new ArgumentNullException("imageType");
        if (string.IsNullOrWhiteSpace(uniqueHeaderAsHex)) throw new ArgumentNullException("uniqueHeaderAsHex");
        if (uniqueHeaderAsHex.Length % 2 != 0)            throw new ArgumentException    ("Hexadecimal value is invalid");
        if (!validator.IsMatch(uniqueHeaderAsHex))        throw new ArgumentException    ("Hexadecimal value is invalid");

        ImageTypes.Add(uniqueHeaderAsHex, imageType);
    }

    private static Dictionary<string, string> ImageTypes;

    public static bool IsImage(this Stream stream)
    {
        string imageType;
        return stream.IsImage(out imageType);
    }

    public static bool IsImage(this Stream stream, out string imageType)
    {
        stream.Seek(0, SeekOrigin.Begin);
        StringBuilder builder = new StringBuilder();
        int largestByteHeader = ImageTypes.Max(img => img.Value.Length);

        for (int i = 0; i < largestByteHeader; i++)
        {
            string bit = stream.ReadByte().ToString("X2");
            builder.Append(bit);

            string builtHex = builder.ToString();
            bool isImage = ImageTypes.Keys.Any(img => img == builtHex);
            if (isImage)
            {
                imageType = ImageTypes[builder.ToString()];
                return true;
            }
        }
        imageType = null;
        return false;
    }
}
14
Aydin

Nous pouvons utiliser les classes Image et Graphics de namespace System.Drawing; faire notre travail. Si le code fonctionne sans erreur, c'est une image, sinon ce n'est pas le cas. C’est-à-dire que le framework DotNet fait le travail pour nous. Le code - 

public string CheckFile(file)
{
    string result="";
    try
    {
        System.Drawing.Image imgInput = System.Drawing.Image.FromFile(file);
        System.Drawing.Graphics gInput = System.Drawing.Graphics.fromimage(imgInput);  
        Imaging.ImageFormat thisFormat = imgInput.RawFormat;   
        result="It is image";        
    }
    catch(Exception ex)
    {
        result="It is not image"; 
    }
    return result;
}
12
yogihosting
System.Web.MimeMapping.GetMimeMapping(filename).StartsWith("image/");

MimeMapping.GetMimeMapping produit ces résultats:

  • fichier.jpg: image/jpeg
  • fichier.gif: image/gif
  • fichier.jpeg: image/jpeg
  • fichier.png: image/png
  • fichier.bmp: image/bmp
  • fichier.tiff: image/tiff
  • fichier.svg: application/octet-stream

file.svg ne pas renvoyer d'image/type MIME fonctionne dans la plupart des cas, car vous n'allez probablement pas traiter une image vectorielle comme une image scalaire. Lors de la vérification du type MIME, sachez que SVG a le type MIME standard image/svg + xml, même si GetMimeMapping ne le renvoie pas.

12
Jeremy Cook

Voir si cela aide.

EDIT: En outre, Image.FromFile (....). RawFormat pourrait aider. Il pourrait jeter une exception si le fichier n'est pas une image.

5
shahkalpesh

J'utilise la méthode suivante. Il utilise le décodeur d'image intégré pour extraire une liste d'extensions reconnues par le système en tant que fichiers image, puis compare ces extensions à l'extension du nom de fichier que vous transmettez. Renvoie un simple TRUE/FALSE.

public static bool IsRecognisedImageFile(string fileName)
{
    string targetExtension = System.IO.Path.GetExtension(fileName);
    if (String.IsNullOrEmpty(targetExtension))
        return false;
    else
        targetExtension = "*" + targetExtension.ToLowerInvariant();

    List<string> recognisedImageExtensions = new List<string>();

    foreach (System.Drawing.Imaging.ImageCodecInfo imageCodec in System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders())
        recognisedImageExtensions.AddRange(imageCodec.FilenameExtension.ToLowerInvariant().Split(";".ToCharArray()));

    foreach (string extension in recognisedImageExtensions)
    {
        if (extension.Equals(targetExtension))
        {
            return true;
        }
    }
    return false;
}
4
dylmcc

Si vous voulez un moyen rapide de valider un fichier image avant qu'il ne soit entièrement lu, vous pouvez simplement vérifier son en-tête à la recherche d'une signature de fichier (le code suivant IsValidImageFile() recherche BMP, GIF87a, GIF89a , PNG, TIFF, JPEG )

    /// <summary>
    /// Reads the header of different image formats
    /// </summary>
    /// <param name="file">Image file</param>
    /// <returns>true if valid file signature (magic number/header marker) is found</returns>
    private bool IsValidImageFile(string file)
    {
        byte[] buffer = new byte[8];
        byte[] bufferEnd = new byte[2];

        var bmp = new byte[] { 0x42, 0x4D };               // BMP "BM"
        var gif87a = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 };     // "GIF87a"
        var gif89a = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };     // "GIF89a"
        var png = new byte[] { 0x89, 0x50, 0x4e, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };   // PNG "\x89PNG\x0D\0xA\0x1A\0x0A"
        var tiffI = new byte[] { 0x49, 0x49, 0x2A, 0x00 }; // TIFF II "II\x2A\x00"
        var tiffM = new byte[] { 0x4D, 0x4D, 0x00, 0x2A }; // TIFF MM "MM\x00\x2A"
        var jpeg = new byte[] { 0xFF, 0xD8, 0xFF };        // JPEG JFIF (SOI "\xFF\xD8" and half next marker xFF)
        var jpegEnd = new byte[] { 0xFF, 0xD9 };           // JPEG EOI "\xFF\xD9"

        try
        {
            using (System.IO.FileStream fs = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read))
            {
                if (fs.Length > buffer.Length)
                {
                    fs.Read(buffer, 0, buffer.Length);
                    fs.Position = (int)fs.Length - bufferEnd.Length;
                    fs.Read(bufferEnd, 0, bufferEnd.Length);
                }

                fs.Close();
            }

            if (this.ByteArrayStartsWith(buffer, bmp) ||
                this.ByteArrayStartsWith(buffer, gif87a) ||
                this.ByteArrayStartsWith(buffer, gif89a) ||
                this.ByteArrayStartsWith(buffer, png) ||
                this.ByteArrayStartsWith(buffer, tiffI) ||
                this.ByteArrayStartsWith(buffer, tiffM))
            {
                return true;
            }

            if (this.ByteArrayStartsWith(buffer, jpeg))
            {
                // Offset 0 (Two Bytes): JPEG SOI marker (FFD8 hex)
                // Offest 1 (Two Bytes): Application segment (FF?? normally ??=E0)
                // Trailer (Last Two Bytes): EOI marker FFD9 hex
                if (this.ByteArrayStartsWith(bufferEnd, jpegEnd))
                {
                    return true;
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, Lang.Lang.ErrorTitle + " IsValidImageFile()", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        return false;
    }

    /// <summary>
    /// Returns a value indicating whether a specified subarray occurs within array
    /// </summary>
    /// <param name="a">Main array</param>
    /// <param name="b">Subarray to seek within main array</param>
    /// <returns>true if a array starts with b subarray or if b is empty; otherwise false</returns>
    private bool ByteArrayStartsWith(byte[] a, byte[] b)
    {
        if (a.Length < b.Length)
        {
            return false;
        }

        for (int i = 0; i < b.Length; i++)
        {
            if (a[i] != b[i])
            {
                return false;
            }
        }

        return true;
    }

La vérification de la signature d'en-tête peut être rapide, car elle ne charge pas le fichier entier ni ne crée de gros objets, en particulier lors du traitement de plusieurs fichiers. Mais il ne vérifie pas si le reste des données est bien formé. Pour ce faire, vous pouvez effectuer une deuxième étape pour essayer de charger le fichier dans un objet Image (pour vous assurer que le fichier peut être affiché et géré par votre programme).

bool IsValidImage(string filename)
{
    try
    {
        using(Image newImage = Image.FromFile(filename))
        {}
    }
    catch (OutOfMemoryException ex)
    {
        //The file does not have a valid image format.
        //-or- GDI+ does not support the pixel format of the file

        return false;
    }
    return true;
}
3
Dr Yunke

Je ne suis pas sûr de l’inconvénient en termes de performances de cette solution, mais ne pouvez-vous pas exécuter une fonction d’image sur le fichier dans un bloc try qui échouerait et tomberait à un bloc catch s’il n’était pas une image? 

Cette stratégie n’est peut-être pas la meilleure solution dans toutes les situations, mais dans le cas où je travaille actuellement, elle présente un avantage majeur: Vous pouvez utiliser la fonction que vous souhaitez utiliser pour traiter l’image (s’il s’agit d’une image ) pour la fonction de test. De cette manière, vous pouvez tester tous les types d’image actuels, mais cela s’appliquerait également aux futurs types d’image sans ajouter cette nouvelle extension à la liste des types d’image pris en charge.

Quelqu'un voit-il des inconvénients à cette stratégie?

2
James Sumner

Pas exactement la réponse dont vous avez besoin. Mais si c’est Internet, alors MIME type. 

2
Cherian

Mon code simple

public static List<string> GetAllPhotosExtensions()
    {
        var list = new List<string>();
        list.Add(".jpg");
        list.Add(".png");
        list.Add(".bmp");
        list.Add(".gif");
        list.Add(".jpeg");
        list.Add(".tiff");
        return list;
    }

Vérifier si le fichier image

public static bool IsPhoto(string fileName)
    {
        var list = FileListExtensions.GetAllPhotosExtensions();
        var filename= fileName.ToLower();
        bool isThere = false;
        foreach(var item in list)
        {
            if (filename.EndsWith(item))
            {
                isThere = true;
                break;
            }
        }
        return isThere;     
    }
0
Minute V