Determine if uploaded file is image (any format) on MVC
Solution 1
In case it can helps anyone, Here is a static method for HttpPostedFileBase
that checks if a given uploaded file is an image:
public static class HttpPostedFileBaseExtensions
{
public const int ImageMinimumBytes = 512;
public static bool IsImage(this HttpPostedFileBase postedFile)
{
//-------------------------------------------
// Check the image mime types
//-------------------------------------------
if (!string.Equals(postedFile.ContentType, "image/jpg", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/jpeg", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/pjpeg", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/gif", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/x-png", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(postedFile.ContentType, "image/png", StringComparison.OrdinalIgnoreCase))
{
return false;
}
//-------------------------------------------
// Check the image extension
//-------------------------------------------
var postedFileExtension = Path.GetExtension(postedFile.FileName);
if (!string.Equals(postedFileExtension , ".jpg", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(postedFileExtension , ".png", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(postedFileExtension , ".gif", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(postedFileExtension , ".jpeg", StringComparison.OrdinalIgnoreCase))
{
return false;
}
//-------------------------------------------
// Attempt to read the file and check the first bytes
//-------------------------------------------
try
{
if (!postedFile.InputStream.CanRead)
{
return false;
}
//------------------------------------------
// Check whether the image size exceeding the limit or not
//------------------------------------------
if (postedFile.ContentLength < ImageMinimumBytes)
{
return false;
}
byte[] buffer = new byte[ImageMinimumBytes];
postedFile.InputStream.Read(buffer, 0, ImageMinimumBytes);
string content = System.Text.Encoding.UTF8.GetString(buffer);
if (Regex.IsMatch(content, @"<script|<html|<head|<title|<body|<pre|<table|<a\s+href|<img|<plaintext|<cross\-domain\-policy",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline))
{
return false;
}
}
catch (Exception)
{
return false;
}
//-------------------------------------------
// Try to instantiate new Bitmap, if .NET will throw exception
// we can assume that it's not a valid image
//-------------------------------------------
try
{
using (var bitmap = new System.Drawing.Bitmap(postedFile.InputStream))
{
}
}
catch (Exception)
{
return false;
}
finally
{
postedFile.InputStream.Position = 0;
}
return true;
}
}
Edit 2/10/2017: According to a suggested edit, added a finally statement to reset the stream, so we can use it later.
Solution 2
It's 2018 and the accepted answer does not work with .NET CORE 2.1 because we now have IFormFile
instead of HttpPostedFileBase
.
Here comes the adaption of the accepted answer to .NET CORE 2.1 (I also fixed the bug/typo mentioned by TomSelleck in his comment to the accepted answer):
public static class FormFileExtensions
{
public const int ImageMinimumBytes = 512;
public static bool IsImage(this IFormFile postedFile)
{
//-------------------------------------------
// Check the image mime types
//-------------------------------------------
if (postedFile.ContentType.ToLower() != "image/jpg" &&
postedFile.ContentType.ToLower() != "image/jpeg" &&
postedFile.ContentType.ToLower() != "image/pjpeg" &&
postedFile.ContentType.ToLower() != "image/gif" &&
postedFile.ContentType.ToLower() != "image/x-png" &&
postedFile.ContentType.ToLower() != "image/png")
{
return false;
}
//-------------------------------------------
// Check the image extension
//-------------------------------------------
if (Path.GetExtension(postedFile.FileName).ToLower() != ".jpg"
&& Path.GetExtension(postedFile.FileName).ToLower() != ".png"
&& Path.GetExtension(postedFile.FileName).ToLower() != ".gif"
&& Path.GetExtension(postedFile.FileName).ToLower() != ".jpeg")
{
return false;
}
//-------------------------------------------
// Attempt to read the file and check the first bytes
//-------------------------------------------
try
{
if (!postedFile.OpenReadStream().CanRead)
{
return false;
}
//------------------------------------------
//check whether the image size exceeding the limit or not
//------------------------------------------
if (postedFile.Length < ImageMinimumBytes)
{
return false;
}
byte[] buffer = new byte[ImageMinimumBytes];
postedFile.OpenReadStream().Read(buffer, 0, ImageMinimumBytes);
string content = System.Text.Encoding.UTF8.GetString(buffer);
if (Regex.IsMatch(content, @"<script|<html|<head|<title|<body|<pre|<table|<a\s+href|<img|<plaintext|<cross\-domain\-policy",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline))
{
return false;
}
}
catch (Exception)
{
return false;
}
//-------------------------------------------
// Try to instantiate new Bitmap, if .NET will throw exception
// we can assume that it's not a valid image
//-------------------------------------------
try
{
using (var bitmap = new System.Drawing.Bitmap(postedFile.OpenReadStream()))
{
}
}
catch (Exception)
{
return false;
}
finally
{
postedFile.OpenReadStream().Position = 0;
}
return true;
}
}
Solution 3
For anyone that runs into this.
You could also use a file.ContentType.Contains("image")
to check if the content type is of image/*.
if(file.ContentLength > 0 && file.ContentType.Contains("image"))
{
//valid image
}
else
{
//not a valid image
}
Not sure if this is best practice, but it works for me.
Solution 4
Don't have the compiler at hand but something like this should do:
try
{
var bitmap = Bitmap.FromStream( file.InputStream );
// valid image stream
}
catch
{
// not an image
}
Solution 5
Use in static helper class:
public static bool IsImage(HttpPostedFileBase postedFile)
{
try {
using (var bitmap = new System.Drawing.Bitmap(postedFile.InputStream))
{
return !bitmap.Size.IsEmpty;
}
}
catch (Exception)
{
return false;
}
}
}
Use in ASP.NET MVC viewmodel:
public class UploadFileViewModel
{
public HttpPostedFileBase postedFile { get; set; }
public bool IsImage()
{
try {
using (var bitmap = new System.Drawing.Bitmap(this.postedFile.InputStream))
{
return !bitmap.Size.IsEmpty;
}
}
catch (Exception)
{
return false;
}
}
}
}
This example checks to see whether the image is a real image, and you can modify and convert it.
It eats memory as an example of six-liter V8, so it should be used when you really want to know what this image.
Erre Efe
"When you're doing something you're passionate about, stress becomes a feature not a bug."
Updated on July 19, 2021Comments
-
Erre Efe almost 3 years
So I'm using this code for view:
<form action="" method="post" enctype="multipart/form-data"> <label for="file">Filename:</label> <input type="file" name="file" id="file" /> <input type="submit" /> </form>
This for model:
[HttpPost] public ActionResult Index(HttpPostedFileBase file) { if (file.ContentLength > 0) { var fileName = Path.GetFileName(file.FileName); var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName); file.SaveAs(path); } return RedirectToAction("Index"); }
Works great unless the user add a file which isn't an image. How can I assure the file uploaded is an image. Thanks
-
Wiktor Zychla almost 12 yearsThat won't work for crafted requests where mime type does not correspond to the stream content.
-
Rasool Ghafari over 10 yearsWhat's your idea for these file types:
".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pps", ".ppsx"
, please. -
OzB over 10 yearsBesides from validating the file extension, you can validate the first bytes as well (see for example serverfault.com/questions/338087/…). In this regard, please note that they're compressed ZIP (so remember the mime-type) files, which gives you an XML, you can try to validate this XML format, though I think that it'll hard unlike the image validation above. An idea that might work is to use the Office COM and try to load the documents to see if you got any errors. This approach, though, won't work in all environments.
-
Rohan Kandwal over 10 yearsplease provide a little bit of explanation too. :D
-
refactor over 8 yearsThis will not restrict users from selecting other MIME types.
-
MonsterMMORPG almost 8 yearsi think this is the only solution that can be trusted
-
Techy about 7 yearsCan you modify this to allow for ASP.NET Core as there is no drawing or bitmap class? Thanks a lot
-
TomSelleck almost 6 years
if (postedFile.ContentLength > ImageMinimumBytes)
This is a typo no? It should readif (ContentLength < ImageMinimumBytes)
-
René over 5 yearsI have corrected the bug/typo mentioned by TomSelleck in my adaption of this answer for 2018 using .NET CORE 2.1 (see below).
-
OzB over 5 yearsYou're absolutely right, @TomSelleck . Fixed here as well.
-
TomSelleck over 5 years@OzB Thanks for posting, proved very useful when I needed it!
-
Juan over 4 yearsThis also perfectly valid in my case (well, IFormFile does not contain ContentLength but Length property)
-
Max Carroll about 4 yearsI'm using .net core 3.1 and I had to add reference the System.Drawing.Common library for this to work
-
Max Carroll about 4 yearsdoes this work on .net core on docker in ubuntu, Im getting some issues, looking into it it might also be reverse proxy its behind,
-
René about 4 years@MaxCarroll I have not tried it on docker in ubuntu. I guess if you are "getting some issues" it's best to open a new question and give a description of those issues.
-
Boban Stojanovski about 4 yearsThis should be the accpeted answer
-
Boban Stojanovski about 4 yearsIf uploading a file, check the headers of the MultipartFileData
if (file.Headers.ContentLength > 0 && file.Headers.Contains("image"))
-
Edward Olamisan about 4 yearsthis code only validates file name. it does NOT validate the file is an image.
-
Edward Olamisan about 4 yearsthis code only validates file name. it does NOT validate the file is an image.
-
Shawn Gavett about 4 years@EdwardOlamisan, are you talking about the answer or the comment above your comment? My code reviews the contentType which is not the filename
-
Edward Olamisan about 4 years@ShawnGavett sorry, my mistake, I copy-pasted my comment to another answer because they are so similar: stackoverflow.com/a/41395863/556649. Bottom line, this answer has the same problem: file name and headers, including ContentType, are provided by the user and can be crafted by a malicious user.
-
OwenP over 3 years@OzB: Compare what "exceeds the size limit" means to what your code does now.
-
RemarkLima about 3 yearsI'd suggest that this would be a lot better if it returned true for each condition - unless you want to check everything?
-
GAltelino about 3 yearssame as @MaxCarroll, this breaks on Fargate. The issue is in System.Drawing: github.com/dotnet/runtime/issues/21980