【问题标题】:Determine if uploaded file is image (any format) on MVC确定上传的文件是否是 MVC 上的图像(任何格式)
【发布时间】:2012-06-19 07:18:18
【问题描述】:

所以我使用这段代码来查看:

<form action="" method="post" enctype="multipart/form-data">

  <label for="file">Filename:</label>
  <input type="file" name="file" id="file" />

  <input type="submit" />
</form>

这适用于模型:

[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");
}

除非用户添加不是图像的文件,否则效果很好。我如何确保上传的文件是图像。谢谢

【问题讨论】:

  • 图片格式有很多种。
  • @DanielA.White 这正是我的问题的重点。确定它是任何图像格式,但毕竟是图像。
  • 您可以检查fileContentType 作为基本的验证形式。如果这还不够好,您可以尝试查看文件流的“标题”,看看它是否与您支持的任何图像类型匹配,ala stackoverflow.com/questions/210650/…
  • 正确执行此操作并use Binary Analysis for Mime Type detection

标签: c# .net asp.net-mvc razor file-upload


【解决方案1】:

对于IFormFile:基于这样的逻辑,如果.NET可以将文件视为有效图像并且可以进一步处理,那么它就是有效图像。

using System.Drawing;

    private bool IsValidImageFile(IFormFile file) {
    
      try {
        var isValidImage = Image.FromStream(file.OpenReadStream());
      } catch {
        return false;
      }
    
      return true;
    }

【讨论】:

    【解决方案2】:

    如果它可以帮助任何人,这是HttpPostedFileBase 的静态方法,用于检查给定的上传文件是否为图像:

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

    编辑 2/10/2017:根据建议的编辑,添加了 finally 语句来重置流,以便我们以后可以使用它。

    【讨论】:

    • 您对这些文件类型有何想法:".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pps", ".ppsx",请。
    • 除了验证文件扩展名之外,您还可以验证第一个字节(例如参见serverfault.com/questions/338087/…)。在这方面,请注意它们是压缩的 ZIP(所以请记住 mime 类型)文件,它为您提供 XML,您可以尝试验证这种 XML 格式,尽管我认为这与上面的图像验证不同.一个可行的想法是使用 Office COM 并尝试加载文档以查看是否有任何错误。但是,这种方法并不适用于所有环境。
    • 由于没有绘图或位图类,您可以修改它以允许 ASP.NET Core 使用吗?非常感谢
    • if (postedFile.ContentLength &gt; ImageMinimumBytes) 这是错字吧?它应该是if (ContentLength &lt; ImageMinimumBytes)
    • 我已使用 .NET CORE 2.1(见下文)更正了 TomSelleck 在我对 2018 年此答案的改编中提到的错误/错字。
    【解决方案3】:

    它没有回答如何检查上传的文件是否是服务器上的图像的问题。

    然而,最初的问题陈述似乎更多的是用户不小心上传了错误的文件。

    在这种情况下,一个非常简单的解决方案是设置accept attribute on the input element

    <input type="file" id="file" accept="image/*">
    

    关于信任用户输入的常见警告适用。

    【讨论】:

      【解决方案4】:
      public static ImageFormat GetRawImageFormat(byte[] fileBytes)
      {
          using (var ms = new MemoryStream(fileBytes))
          {
              var fileImage = Image.FromStream(ms);
              return fileImage.RawFormat;
          }
      }
      

      用法:

      if (GetRawImageFormat(fileBytes).IsIn(ImageFormat.Jpeg, ImageFormat.Png, ImageFormat.Gif))
          {
              //do somthing
          }
      

      【讨论】:

        【解决方案5】:

        现在是 2018 年,接受的答案不适用于 .NET CORE 2.1,因为我们现在有 IFormFile 而不是 HttpPostedFileBase

        这是对 .NET CORE 2.1 的接受答案的改编(我还修复了 TomSelleck 在对接受的答案的评论中提到的错误/错字):

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

        【讨论】:

        • 我使用的是 .net core 3.1,我必须添加对 System.Drawing.Common 库的引用才能使其工作
        • 这项工作在 ubuntu 的 docker 上的 .net 核心上工作,我遇到了一些问题,调查它也可能是其背后的反向代理,
        • @MaxCarroll 我没有在 ubuntu 的 docker 上尝试过。我想如果您“遇到一些问题”,最好打开一个新问题并描述这些问题。
        • 我建议如果它对每个条件都返回 true 会好很多 - 除非你想检查所有内容?
        • 与@MaxCarroll 相同,这在 Fargate 上会中断。问题出在 System.Drawing:github.com/dotnet/runtime/issues/21980
        【解决方案6】:

        以更清洁的方式实现,

        public static class FileExtensions
        {
            private static readonly IDictionary<string, string> ImageMimeDictionary = new Dictionary<string, string>
            {
                { ".bmp", "image/bmp" },
                { ".dib", "image/bmp" },
                { ".gif", "image/gif" },
                { ".svg", "image/svg+xml" },
                { ".jpe", "image/jpeg" },
                { ".jpeg", "image/jpeg" },
                { ".jpg", "image/jpeg" },
                { ".png", "image/png" },
                { ".pnz", "image/png" }
            };
        
            public static bool IsImage(this string file)
            {
                if (string.IsNullOrEmpty(file))
                {
                    throw new ArgumentNullException(nameof(file));
                }
        
                var extension = Path.GetExtension(file);
                return ImageMimeDictionary.ContainsKey(extension.ToLower());
            }
        }
        

        【讨论】:

        • 此代码仅验证文件名。它不验证文件是图像。
        【解决方案7】:

        对于遇到这种情况的任何人。

        您也可以使用file.ContentType.Contains("image") 来检查内容类型是否为图像/*。

        if(file.ContentLength > 0 && file.ContentType.Contains("image"))
        {
            //valid image
        }
        else
        {
            //not a valid image
        }
        

        不确定这是否是最佳做法,但它对我有用。

        【讨论】:

        • 这在我的情况下也完全有效(好吧,IFormFile 不包含 ContentLength 但 Length 属性)
        • 这应该是公认的答案
        • 如果上传文件,检查MultipartFileData的headers if (file.Headers.ContentLength &gt; 0 &amp;&amp; file.Headers.Contains("image"))
        • 此代码仅验证文件名。它不验证文件是图像。
        • @EdwardOlamisan,您是在谈论答案还是评论上方的评论?我的代码审查了不是文件名的 contentType
        【解决方案8】:

        在服务器端与内容类型进行比较,如果它与您所需的上传格式匹配,则继续,否则返回错误消息

        【讨论】:

        • 这不会限制用户选择其他 MIME 类型。
        【解决方案9】:

        在静态助手类中使用:

        public static bool IsImage(HttpPostedFileBase postedFile)
            {
                try  {
                      using (var bitmap = new System.Drawing.Bitmap(postedFile.InputStream))
                            {                        
                                    return !bitmap.Size.IsEmpty;
                            }
                        }
                        catch (Exception)
                        {
                            return false;
                        }
                    }
            }
        

        在 ASP.NET MVC 视图模型中使用:

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

        本例检查图片是否为真实图片,可以修改转换。

        以六升V8为例,它吃内存,所以当你真的想知道这个图像是什么时,应该使用它。

        【讨论】:

        • 也请提供一点解释。 :D
        • 我认为这是唯一可以信任的解决方案
        【解决方案10】:

        作为第一步,您应该针对ContentType 属性围绕可接受的MIME 类型形成一个白名单。

        【讨论】:

        • 这不适用于 mime 类型与流内容不对应的精心制作的请求。
        【解决方案11】:

        手头没有编译器,但应该这样做:

        try
        {
           var bitmap = Bitmap.FromStream( file.InputStream );
           // valid image stream
        }
        catch 
        {
            // not an image
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-11
          • 2016-12-11
          • 1970-01-01
          • 2010-12-13
          • 2011-10-03
          • 1970-01-01
          相关资源
          最近更新 更多