在 C# 中验证来自文件的图像
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/210650/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Validate image from file in C#
提问by SemiColon
I'm loading an image from a file, and I want to know how to validate the image before it is fully read from the file.
我正在从文件加载图像,我想知道如何在从文件中完全读取图像之前对其进行验证。
string filePath = "image.jpg";
Image newImage = Image.FromFile(filePath);
The problem occurs when image.jpg isn't really a jpg. For example, if I create an empty text file and rename it to image.jpg, an OutOfMemory Exception will be thrown when image.jpg is loaded.
当 image.jpg 不是真正的 jpg 时会出现问题。例如,如果我创建一个空文本文件并将其重命名为 image.jpg,则在加载 image.jpg 时将抛出 OutOfMemory 异常。
I'm looking for a function that will validate an image given a stream or a file path of the image.
我正在寻找一个函数,可以在给定图像的流或文件路径的情况下验证图像。
Example function prototype
示例函数原型
bool IsValidImage(string fileName);
bool IsValidImage(Stream imageStream);
采纳答案by FlySwat
JPEG's don't have a formal header definition, but they do have a small amount of metadata you can use.
JPEG 没有正式的标头定义,但它们确实有少量可以使用的元数据。
- Offset 0 (Two Bytes): JPEG SOI marker (FFD8 hex)
- Offset 2 (Two Bytes): Image width in pixels
- Offset 4 (Two Bytes): Image height in pixels
- Offset 6 (Byte): Number of components (1 = grayscale, 3 = RGB)
- 偏移 0(两个字节):JPEG SOI 标记(FFD8 十六进制)
- 偏移量 2(两个字节):以像素为单位的图像宽度
- 偏移量 4(两个字节):图像高度(以像素为单位)
- 偏移量 6(字节):分量数(1 = 灰度,3 = RGB)
There are a couple other things after that, but those aren't important.
之后还有其他一些事情,但这些都不重要。
You can open the file using a binary stream, and read this initial data, and make sure that OffSet 0 is 0, and OffSet 6 is either 1,2 or 3.
您可以使用二进制流打开文件,并读取此初始数据,并确保 OffSet 0 为 0,OffSet 6 为 1,2 或 3。
That would at least give you slightly more precision.
这至少会让你更精确。
Or you can just trap the exception and move on, but I thought you wanted a challenge :)
或者你可以捕获异常并继续前进,但我认为你想要一个挑战:)
回答by Enrico Murru
I would create a method like:
我会创建一个方法,如:
Image openImage(string filename);
in which I handle the exception. If the returned value is Null, there is an invalid file name / type.
我在其中处理异常。如果返回值为 Null,则存在无效的文件名/类型。
回答by Quantenmechaniker
You could read the first few bytes of the Stream and compare them to the magic header bytes for JPEG.
您可以读取 Stream 的前几个字节,并将它们与 JPEG 的魔术头字节进行比较。
回答by MusiGenesis
Using Windows Forms:
使用 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;
}
Otherwise if you're using WPFyou can do the following:
否则,如果您使用 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;
}
You must release the image created. Otherwise when you call this function large number of times, this would throw OutOfMemoryExceptionbecause the system ran out of resources, and not because the image is corrupt yielding an incorrect result, and if you delete images after this step, you'd potentially be deleting good ones.
您必须释放创建的映像。否则,当您多次调用此函数时,这将抛出OutOfMemoryException,因为系统资源不足,而不是因为图像已损坏而产生不正确的结果,如果在此步骤之后删除图像,则可能会删除好的。
回答by Troy Howard
You can do a rough typing by sniffing the header.
您可以通过嗅探标题进行粗略的打字。
This means that each file format you implement will need to have a identifiable header...
这意味着您实现的每种文件格式都需要有一个可识别的标题...
JPEG: First 4 bytes are FF D8 FF E0 (actually just the first two bytes would do it for non jfif jpeg, more info here).
JPEG:前 4 个字节是 FF D8 FF E0(实际上只有前两个字节可以用于非 jfif jpeg,更多信息在这里)。
GIF: First 6 bytes are either "GIF87a" or "GIF89a" (more info here)
GIF:前 6 个字节是“GIF87a”或“GIF89a”(更多信息在这里)
PNG: First 8 bytes are: 89 50 4E 47 0D 0A 1A 0A (more info here)
PNG:前 8 个字节是:89 50 4E 47 0D 0A 1A 0A(更多信息在这里)
TIFF: First 4 bytes are: II42 or MM42 (more info here)
TIFF:前 4 个字节是:II42 或 MM42(更多信息在这里)
etc... you can find header/format information for just about any graphics format you care about and add to the things it handles as needed. What this won't do, is tell you if the file is a valid version of that type, but it will give you a hint about "image not image?". It could still be a corrupt or incomplete image, and thus crash when opening, so a try catch around the .FromFile call is still needed.
等等...您可以找到几乎任何您关心的图形格式的标题/格式信息,并根据需要添加到它处理的内容中。这不会做的是告诉您该文件是否是该类型的有效版本,但它会给您提示“图像不是图像?”。它可能仍然是损坏或不完整的图像,因此在打开时会崩溃,因此仍然需要尝试捕获 .FromFile 调用。
回答by SemiColon
Well, I went ahead and coded a set of functions to solve the problem. It checks the header first, then attempts to load the image in a try/catch block. It only checks for GIF, BMP, JPG, and PNG files. You can easily add more types by adding a header to imageHeaders.
好吧,我继续编写了一组函数来解决问题。它首先检查标题,然后尝试在 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;
}
回答by lorddarq
in case yo need that data read for other operations and/or for other filetypes (PSD for example), later on, then using the Image.FromStream
function is not necessarily a good ideea.
如果您稍后需要为其他操作和/或其他文件类型(例如 PSD)读取数据,那么使用该Image.FromStream
功能不一定是一个好主意。
回答by Paulo
A method that supports Tiff and Jpeg also
一种也支持 Tiff 和 Jpeg 的方法
private bool IsValidImage(string filename)
{
Stream imageStream = null;
try
{
imageStream = new FileStream(filename, FileMode.Open);
if (imageStream.Length > 0)
{
byte[] header = new byte[30]; // Change size if needed.
string[] imageHeaders = new[]
{
"BM", // BMP
"GIF", // GIF
Encoding.ASCII.GetString(new byte[]{137, 80, 78, 71}),// PNG
"MM\x00\x2a", // TIFF
"II\x2a\x00" // TIFF
};
imageStream.Read(header, 0, header.Length);
bool isImageHeader = imageHeaders.Count(str => Encoding.ASCII.GetString(header).StartsWith(str)) > 0;
if (imageStream != null)
{
imageStream.Close();
imageStream.Dispose();
imageStream = null;
}
if (isImageHeader == false)
{
//Verify if is jpeg
using (BinaryReader br = new BinaryReader(File.Open(filename, FileMode.Open)))
{
UInt16 soi = br.ReadUInt16(); // Start of Image (SOI) marker (FFD8)
UInt16 jfif = br.ReadUInt16(); // JFIF marker
return soi == 0xd8ff && (jfif == 0xe0ff || jfif == 57855);
}
}
return isImageHeader;
}
return false;
}
catch { return false; }
finally
{
if (imageStream != null)
{
imageStream.Close();
imageStream.Dispose();
}
}
}
回答by David Boike
This should do the trick - you don't have to read raw bytes out of the header:
这应该可以解决问题 - 您不必从标头中读取原始字节:
using(Image test = Image.FromFile(filePath))
{
bool isJpeg = (test.RawFormat.Equals(ImageFormat.Jpeg));
}
Of course, you should trap the OutOfMemoryException too, which will save you if the file isn't an image at all.
当然,您也应该捕获 OutOfMemoryException ,如果文件根本不是图像,它会拯救您。
And, ImageFormat has pre-set items for all the other major image types that GDI+ supports.
而且,ImageFormat 具有 GDI+ 支持的所有其他主要图像类型的预设项。
Note, you must use .Equals() and not == on ImageFormat objects (it is not an enumeration) because the operator == isn't overloaded to call the Equals method.
请注意,您必须在 ImageFormat 对象上使用 .Equals() 而不是 ==(它不是枚举),因为运算符 == 未重载以调用 Equals 方法。
回答by ray
I took Semicolon's answer and converted to VB:
我接受了分号的答案并转换为 VB:
Private Function IsValidImage(imageStream As System.IO.Stream) As Boolean
If (imageStream.Length = 0) Then
isvalidimage = False
Exit Function
End If
Dim pngByte() As Byte = New Byte() {137, 80, 78, 71}
Dim pngHeader As String = System.Text.Encoding.ASCII.GetString(pngByte)
Dim jpgByte() As Byte = New Byte() {255, 216}
Dim jpgHeader As String = System.Text.Encoding.ASCII.GetString(jpgByte)
Dim bmpHeader As String = "BM"
Dim gifHeader As String = "GIF"
Dim header(3) As Byte
Dim imageHeaders As String() = New String() {jpgHeader, bmpHeader, gifHeader, pngHeader}
imageStream.Read(header, 0, header.Length)
Dim isImageHeader As Boolean = imageHeaders.Count(Function(str) System.Text.Encoding.ASCII.GetString(header).StartsWith(str)) > 0
If (isImageHeader) Then
Try
System.Drawing.Image.FromStream(imageStream).Dispose()
imageStream.Close()
IsValidImage = True
Exit Function
Catch ex As Exception
System.Diagnostics.Debug.WriteLine("Not an image")
End Try
Else
System.Diagnostics.Debug.WriteLine("Not an image")
End If
imageStream.Close()
IsValidImage = False
End Function