23

I have this file types Filters:

    public const string Png = "PNG Portable Network Graphics (*.png)|" + "*.png";
    public const string Jpg = "JPEG File Interchange Format (*.jpg *.jpeg *jfif)|" + "*.jpg;*.jpeg;*.jfif";
    public const string Bmp = "BMP Windows Bitmap (*.bmp)|" + "*.bmp";
    public const string Tif = "TIF Tagged Imaged File Format (*.tif *.tiff)|" + "*.tif;*.tiff";
    public const string Gif = "GIF Graphics Interchange Format (*.gif)|" + "*.gif";
    public const string AllImages = "Image file|" + "*.png; *.jpg; *.jpeg; *.jfif; *.bmp;*.tif; *.tiff; *.gif";
    public const string AllFiles = "All files (*.*)" + "|*.*";

    static FilesFilters()
    {
        imagesTypes = new List<string>();
        imagesTypes.Add(Png);
        imagesTypes.Add(Jpg);
        imagesTypes.Add(Bmp);
        imagesTypes.Add(Tif);
        imagesTypes.Add(Gif);
   }

OBS: Is there any default filters in .NET or a free library for that?

I need a static method that checks if a string is an image or not. How would you solve this?

    //ext == Path.GetExtension(yourpath)
    public static bool IsImageExtension(string ext)
    {
        return (ext == ".bmp" || .... etc etc...)
    }

Solution using Jeroen Vannevel EndsWith. I think it is ok.

    public static bool IsImageExtension(string ext)
    {
        return imagesTypes.Contains(ext);
    }
Pedro77
  • 5,176
  • 7
  • 61
  • 91
  • 3
    You might also want to consider doing it by [detecting the MIME Type](http://stackoverflow.com/questions/15300567/alternative-to-findmimefromdata-method-in-urlmon-dll-one-which-has-more-mime-typ), its more "trust worthy" that file extension, – Jeremy Thompson Jul 19 '13 at 00:49

5 Answers5

17

You could use .endsWith(ext). It's not a very secure method though: I could rename 'bla.jpg' to 'bla.png' and it would still be a jpg file.

public static bool HasImageExtension(this string source){
 return (source.EndsWith(".png") || source.EndsWith(".jpg"));
}

This provides a more secure solution:

string InputSource = "mypic.png";
System.Drawing.Image imgInput = System.Drawing.Image.FromFile(InputSource);
Graphics gInput = Graphics.fromimage(imgInput);
Imaging.ImageFormat thisFormat = imgInput.rawformat;
Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
  • "I could rename 'bla.jpg' to 'bla.png' and it would still be a jpg file" Yeah I know that, but this leads to another problem... I think I will leave with this because all images will be loaded as a Bitmap object. endsWith is nice. – Pedro77 Jul 19 '13 at 00:29
  • If you have a list of allowed imagetypes just use `return imageTypes.Contains(ext);` in accordance to @Alejandro's example. – Jeroen Vannevel Jul 19 '13 at 00:35
  • Opsss! Thank you!! By the way: is there any default filters in .NET or a free library for that? – Pedro77 Jul 19 '13 at 00:36
  • Not that I know off, but I haven't done any search for it either. – Jeroen Vannevel Jul 19 '13 at 01:19
  • does this secure solution 100% safe? can we use it to save images as we want? I mean we can save gInput as png for example right? and it would be safe? – Furkan Gözükara Jun 29 '16 at 11:56
  • 2
    @DaveLucre If you desperately want to change the code to account for lowercase then you should use the `.EndsWith` overload with a StringComparison argument. `ToLower()` for string comparison is not done. – Jeroen Vannevel Nov 24 '17 at 10:51
  • @JeroenVannevel since you so desperately want to suppress any solution suggested by others, perhaps you can follow your own advice and implement the suggested solution yourself. – Dave Lucre Nov 27 '17 at 02:00
17
private static readonly string[] _validExtensions = {"jpg","bmp","gif","png"}; //  etc

public static bool IsImageExtension(string ext)
{
    return _validExtensions.Contains(ext.ToLower());
}

If you want to be able to make the list configurable at runtime without recompiling, add something like:

private static string[] _validExtensions;

private static string[] ValidExtensions()
{
    if(_validExtensions==null)
    { 
        // load from app.config, text file, DB, wherever
    }
    return _validExtensions
}

public static bool IsImageExtension(string ext)
{
    return ValidExtensions().Contains(ext.ToLower());
}
nathanchere
  • 8,008
  • 15
  • 65
  • 86
  • 2
    Make sure you include `using System.Linq;` to get access to the `Contains` extension method. – SavoryBytes Sep 27 '16 at 22:39
  • 1
    Note that this will only work with all lowercase extensions -- remember that Windows's file system is case insensitive, but C#'s string comparison is not. It's not uncommon for extensions to be all caps, e.g., somefile.PNG. [This overload of Contains can fix this limitation.](https://msdn.microsoft.com/en-us/library/bb339118(v=vs.110).aspx) For example, use `ValidExtensions().Contains(ext, StringComparer.OrdinalIgnoreCase);`, note that you need `using System.Linq;` to take advantage of that `Contains` overload. – jrh Jan 19 '18 at 14:52
  • 1
    @daniellmb nitpicking: Technically you don't need LINQ for [List.Contains(T)](https://msdn.microsoft.com/en-us/library/bhkz42b3(v=vs.110).aspx), but you do need LINQ for [Enumerable.Contains(this IEnumerable,T, Comparer)](https://msdn.microsoft.com/en-us/library/bb339118(v=vs.110).aspx), the latter extension method overload would help resolve some issues in the code in this post. – jrh Jan 19 '18 at 15:07
5

An option would be to have a list of all possible valid image extensions, then that method would only check if the supplied extension is within that collection:

private static readonly HashSet<string> validExtensions = new HashSet<string>()
{
    "png",
    "jpg",
    "bmp"
    // Other possible extensions
};

Then in the validation you just check against that:

public static bool IsImageExtension(string ext)
{
    return validExtensions.Contains(ext);
}
Alejandro
  • 7,290
  • 4
  • 34
  • 59
  • Good extra info is here http://stackoverflow.com/questions/4558754/define-what-is-a-hashset – NoWar Feb 17 '14 at 17:20
  • 1
    Note that this will only work with all lowercase extensions -- remember that Windows's file system is case insensitive, but C#'s string comparison (by default) is not. It's not uncommon for extensions to be all caps, e.g., somefile.PNG. Consider adding `StringComparer.OrdinalIgnoreCase` to your `HashSet` as Oliver did in [his answer](https://stackoverflow.com/a/21835675/4975230). – jrh Jan 19 '18 at 13:47
5

This method automatically creates a filter for the OpenFileDialog. It uses the informations of the image decoders supported by Windows. It also adds information of "unknown" image formats (see default case of the switch statement).

private static string SupportedImageDecodersFilter()
{
    ImageCodecInfo[] encoders = ImageCodecInfo.GetImageDecoders();

    string allExtensions = encoders
        .Select(enc => enc.FilenameExtension)
        .Aggregate((current, next) => current + ";" + next)
        .ToLowerInvariant();
    var sb = new StringBuilder(500)
        .AppendFormat("Image files  ({0})|{1}", allExtensions.Replace(";", ", "),
                      allExtensions);
    foreach (ImageCodecInfo encoder in encoders) {
        string ext = encoder.FilenameExtension.ToLowerInvariant();
        // ext = "*.bmp;*.dib;*.rle"           descr = BMP
        // ext = "*.jpg;*.jpeg;*.jpe;*.jfif"   descr = JPEG
        // ext = "*.gif"                       descr = GIF
        // ext = "*.tif;*.tiff"                descr = TIFF
        // ext = "*.png"                       descr = PNG

        string caption;
        switch (encoder.FormatDescription) {
            case "BMP":
                caption = "Windows Bitmap";
                break;
            case "JPEG":
                caption = "JPEG file";
                break;
            case "GIF":
                caption = "Graphics Interchange Format";
                break;
            case "TIFF":
                caption = "Tagged Image File Format";
                break;
            case "PNG":
                caption = "Portable Network Graphics";
                break;
            default:
                caption = encoder.FormatDescription;
                break;
        }
        sb.AppendFormat("|{0}  ({1})|{2}", caption, ext.Replace(";", ", "), ext);
    }
    return sb.ToString();
}

Use it like this:

var dlg = new OpenFileDialog {
    Filter = SupportedImageDecodersFilter(),
    Multiselect = false,
    Title = "Choose Image"
};

The code above (slightly modified) can be used to find available image file extensions. In order to test if a given file extension denotes an image, I would put the valid extension in a HashSet. HashSets have an
O(1) access time! Make sure to choose a case insensitive string comparer. Since the file extensions do not contain accented or non Latin letters, the culture can safely be ignored. Therefore I use an ordinal string comparison.

var imageExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
imageExtensions.Add(".png");
imageExtensions.Add(".bmp");
...

And test if a filename is an image:

string extension = Path.GetExtension(filename);
bool isImage = imageExtensions.Contains(extension);
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
4

I just had to do something similiar. Here is my solution:

    private bool IsImage(string fileExtension)
    {
        return GetImageFileExtensions().Contains(fileExtension.ToLower()));
    }

    private static List<string> GetImageFileExtensions()
    {
        return ImageCodecInfo.GetImageEncoders()
                             .Select(c => c.FilenameExtension)
                             .SelectMany(e => e.Split(';'))
                             .Select(e => e.Replace("*", "").ToLower())
                             .ToList();            
    }
Welcor
  • 2,431
  • 21
  • 32
  • This adds a dependency to System.Drawing.Imaging which is not cross platform. Doesn't work in .Net Standard / Core. – DhyMik Dec 02 '20 at 16:57