Sem*_*lon 51 .net c# file-io image
我正在从文件中加载图像,我想知道在从文件中完全读取图像之前如何验证图像.
string filePath = "image.jpg";
Image newImage = Image.FromFile(filePath);
Run Code Online (Sandbox Code Playgroud)
当image.jpg不是真正的jpg时会出现问题.例如,如果我创建一个空文本文件并将其重命名为image.jpg,则在加载image.jpg时将抛出OutOfMemory Exception.
我正在寻找一个功能,它将在给定图像的流或文件路径的情况下验证图像.
示例函数原型
bool IsValidImage(string fileName);
bool IsValidImage(Stream imageStream);
Run Code Online (Sandbox Code Playgroud)
Ale*_*lex 68
这是我的图像检查.我不能依赖文件扩展名,必须自己检查格式.我正在从字节数组加载WPF中的BitmapImages,并且不知道预先格式化.WPF检测格式正常,但没有告诉你BitmapImage对象的图像格式(至少我不知道这个属性).我不想再使用System.Drawing加载图像来检测格式.这个解决方案很快,对我来说很好.
public enum ImageFormat
{
bmp,
jpeg,
gif,
tiff,
png,
unknown
}
public static ImageFormat GetImageFormat(byte[] bytes)
{
// see http://www.mikekunz.com/image_file_header.html
var bmp = Encoding.ASCII.GetBytes("BM"); // BMP
var gif = Encoding.ASCII.GetBytes("GIF"); // GIF
var png = new byte[] { 137, 80, 78, 71 }; // PNG
var tiff = new byte[] { 73, 73, 42 }; // TIFF
var tiff2 = new byte[] { 77, 77, 42 }; // TIFF
var jpeg = new byte[] { 255, 216, 255, 224 }; // jpeg
var jpeg2 = new byte[] { 255, 216, 255, 225 }; // jpeg canon
if (bmp.SequenceEqual(bytes.Take(bmp.Length)))
return ImageFormat.bmp;
if (gif.SequenceEqual(bytes.Take(gif.Length)))
return ImageFormat.gif;
if (png.SequenceEqual(bytes.Take(png.Length)))
return ImageFormat.png;
if (tiff.SequenceEqual(bytes.Take(tiff.Length)))
return ImageFormat.tiff;
if (tiff2.SequenceEqual(bytes.Take(tiff2.Length)))
return ImageFormat.tiff;
if (jpeg.SequenceEqual(bytes.Take(jpeg.Length)))
return ImageFormat.jpeg;
if (jpeg2.SequenceEqual(bytes.Take(jpeg2.Length)))
return ImageFormat.jpeg;
return ImageFormat.unknown;
}
Run Code Online (Sandbox Code Playgroud)
Mus*_*sis 33
使用Windows窗体:
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;
}
Run Code Online (Sandbox Code Playgroud)
否则,如果您使用的是WPF,则可以执行以下操作:
bool IsValidImage(string filename)
{
try
{
using(BitmapImage newImage = new BitmapImage(filename))
{}
}
catch(NotSupportedException)
{
// System.NotSupportedException:
// No imaging component suitable to complete this operation was found.
return false;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
您必须释放创建的图像.否则,当您多次调用此函数时,这会抛出OutOfMemoryException,因为系统资源不足,而不是因为图像损坏导致结果不正确,并且如果在此步骤之后删除图像,则可能会删除好的.
Fly*_*wat 22
JPEG没有正式的标头定义,但它们确实有少量可用的元数据.
之后还有其他一些事情,但那些并不重要.
您可以使用二进制流打开文件,并读取此初始数据,并确保OffSet 0为0,OffSet 6为1,2或3.
这至少可以让你更精确.
或者你可以捕获异常并继续前进,但我认为你想挑战:)
Sem*_*lon 19
好吧,我继续编写了一组函数来解决问题.它首先检查标头,然后尝试在try/catch块中加载图像.它仅检查GIF,BMP,JPG和PNG文件.您可以通过向imageHeaders添加标题来轻松添加更多类型.
static bool IsValidImage(string filePath)
{
return File.Exists(filePath) && IsValidImage(new FileStream(filePath, FileMode.Open, FileAccess.Read));
}
static bool IsValidImage(Stream imageStream)
{
if(imageStream.Length > 0)
{
byte[] header = new byte[4]; // Change size if needed.
string[] imageHeaders = new[]{
"\xFF\xD8", // JPEG
"BM", // BMP
"GIF", // GIF
Encoding.ASCII.GetString(new byte[]{137, 80, 78, 71})}; // PNG
imageStream.Read(header, 0, header.Length);
bool isImageHeader = imageHeaders.Count(str => Encoding.ASCII.GetString(header).StartsWith(str)) > 0;
if (isImageHeader == true)
{
try
{
Image.FromStream(imageStream).Dispose();
imageStream.Close();
return true;
}
catch
{
}
}
}
imageStream.Close();
return false;
}
Run Code Online (Sandbox Code Playgroud)
Tro*_*ard 13
你可以通过嗅探标题进行粗略的输入.
这意味着您实现的每种文件格式都需要具有可识别的标头...
JPEG:前4个字节是FF D8 FF E0(实际上只有前两个字节可以用于非jfif jpeg,这里有更多信息).
GIF:前6个字节是"GIF87a"或"GIF89a"(此处有更多信息)
PNG:前8个字节是:89 50 4E 47 0D 0A 1A 0A(更多信息在这里)
TIFF:前4个字节是:II42或MM42(此处有更多信息)
等等...您可以找到关于您关心的任何图形格式的标题/格式信息,并根据需要添加到它处理的内容中.这不会做的,是告诉你文件是否是该类型的有效版本,但它会给你一个关于"图像不是图像?"的提示.它仍然可能是一个损坏或不完整的图像,因此在打开时会崩溃,所以仍然需要尝试捕捉.FromFile调用.
这应该可以解决问题 - 您不必从标头中读取原始字节:
using(Image test = Image.FromFile(filePath))
{
bool isJpeg = (test.RawFormat.Equals(ImageFormat.Jpeg));
}
Run Code Online (Sandbox Code Playgroud)
当然,您也应该捕获OutOfMemoryException,如果文件根本不是图像,这将节省您的时间.
并且,ImageFormat为GDI +支持的所有其他主要图像类型预先设置了项目.
注意,您必须在ImageFormat对象上使用.Equals()而不是==(它不是枚举),因为operator ==没有重载以调用Equals方法.
2019 年,dotnet 核心 3.1。我接受亚历克斯的回答并稍微实现它
public static bool IsImage(this byte[] fileBytes)
{
var headers = new List<byte[]>
{
Encoding.ASCII.GetBytes("BM"), // BMP
Encoding.ASCII.GetBytes("GIF"), // GIF
new byte[] { 137, 80, 78, 71 }, // PNG
new byte[] { 73, 73, 42 }, // TIFF
new byte[] { 77, 77, 42 }, // TIFF
new byte[] { 255, 216, 255, 224 }, // JPEG
new byte[] { 255, 216, 255, 225 } // JPEG CANON
};
return headers.Any(x => x.SequenceEqual(fileBytes.Take(x.Length)));
}
Run Code Online (Sandbox Code Playgroud)
用法 :
public async Task UploadImage(Stream file)
{
using (MemoryStream ms = new MemoryStream())
{
await file.CopyToAsync(ms);
byte[] bytes = ms.ToArray();
if (!bytes.IsImage())
throw new ArgumentException("Not an image", nameof(file));
// Upload your file
}
}
Run Code Online (Sandbox Code Playgroud)