GDI+ 中的外部异常错误保存特定图像
Posted
技术标签:
【中文标题】GDI+ 中的外部异常错误保存特定图像【英文标题】:External Exception in GDI+ error saving specific image 【发布时间】:2013-11-20 12:08:30 【问题描述】:我需要从数据库中获取图像,所以我为它编写了一些代码。问题是在某些图像中出现该错误,而在其他图像中出现无效参数错误。
OleDbConnection l = new OleDbConnection(builder.ConnectionString);
List<Image> listaImagens = new List<Image>();
List<String> listaNomes = new List<string>();
string nome = "";
try
OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM [Fotografias e Manuais de Equipamentos] WHERE ID >26 AND ID < 30", l);
DataSet ds = new DataSet();
adapter.Fill(ds, "Fotografias e Manuais de Equipamentos");
//string s = ds.Tables["APP_Equip_Ult_Prox_Calibracao"].Columns[16].ColumnName;
foreach (DataRow row in ds.Tables["Fotografias e Manuais de Equipamentos"].Rows)
if (row["Designação Equipamento"].ToString().Equals(""))
nome = "semNome";
else
nome = row["Designação Equipamento"].ToString();
listaNomes.Add(row["ID"].ToString()+"_"+row["MARCA"].ToString() + "_" + row["MODELO"].ToString() + "_" + nome);
try
byte[] b = (byte[])row["FOTO"];
byte[] imagebyte = OleImageUnwrap.GetImageBytesFromOLEField(b, 30000);
MemoryStream ms = new MemoryStream();
ms.Write(imagebyte, 0, imagebyte.Length);
listaImagens.Add(Image.FromStream(ms));
catch (Exception)
try
byte[] b = (byte[])row["FOTO"];
byte[] imagebyte = OleImageUnwrap.GetImageBytesFromOLEField(b, 100000);
MemoryStream ms = new MemoryStream();
ms.Write(imagebyte, 0, imagebyte.Length);
listaImagens.Add(Image.FromStream(ms));
catch (Exception)
byte[] b = (byte[])row["FOTO"];
byte[] imagebyte = OleImageUnwrap.GetImageBytesFromOLEField(b, 600000);
MemoryStream ms = new MemoryStream();
ms.Write(imagebyte, 0, imagebyte.Length);
Image img = Image.FromStream(ms); // INVALID PARAMETER ERROR CAUGHT HERE IN DEBBUG
listaImagens.Add(img);
for (int i = 0; i < listaImagens.Count; i++)
listaImagens[i].Save("C:\\Users\\sies4578\\Desktop\\Testes\\Fotos\\" + listaNomes[i] +".png", System.Drawing.Imaging.ImageFormat.Png); //EXTERNAL EXCEPTON IN GDI+ ERROR CAUGHT HERE IN DEBBUG
catch (Exception ex)
MessageBox.Show("Deu o berro: "+ex.Message);
OleImageUnwrap.GetImageBytesFromOLEField 用于从作为数据库中的图像的 OLE 对象中删除标头,并仅获取图像本身的字节:
public static byte[] GetImageBytesFromOLEField(byte[] oleFieldBytes, int NumMaximoBytesSearch)
//ref http://***.com/questions/19688641/convert-ole-object-in-datarow-into-byte-c-sharp
// adapted from http://blogs.msdn.com/b/pranab/archive/2008/07/15/removing-ole-header-from-images-stored-in-ms-access-db-as-ole-object.aspx
int MaxNumberOfBytesToSearch = NumMaximoBytesSearch;
byte[] imageBytes; // return value
var ImageSignatures = new List<byte[]>();
// BITMAP_ID_BLOCK = "BM"
ImageSignatures.Add(new byte[] 0x42, 0x4D );
// JPG_ID_BLOCK = "\u00FF\u00D8\u00FF"
ImageSignatures.Add(new byte[] 0xFF, 0xD8, 0xFF );
// PNG_ID_BLOCK = "\u0089PNG\r\n\u001a\n"
ImageSignatures.Add(new byte[] 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A );
// GIF_ID_BLOCK = "GIF8"
ImageSignatures.Add(new byte[] 0x47, 0x49, 0x46, 0x38 );
// TIFF_ID_BLOCK = "II*\u0000"
ImageSignatures.Add(new byte[] 0x49, 0x49, 0x2A, 0x00 );
int NumberOfBytesToSearch = (oleFieldBytes.Count() < MaxNumberOfBytesToSearch ? oleFieldBytes.Count() : MaxNumberOfBytesToSearch);
var startingBytes = new byte[NumberOfBytesToSearch];
Array.Copy(oleFieldBytes, startingBytes, NumberOfBytesToSearch);
var positions = new List<int>();
foreach (byte[] BlockSignature in ImageSignatures)
positions = IndexOfSequence(startingBytes, BlockSignature, 0);
if (positions.Count > 0)
break;
int iPos = -1;
if (positions.Count > 0)
iPos = positions[0];
if (iPos == -1)
throw new Exception("Unable to determine header size for the OLE Object");
imageBytes = new byte[oleFieldBytes.LongLength - iPos];
System.IO.MemoryStream ms = new System.IO.MemoryStream();
ms.Write(oleFieldBytes, iPos, oleFieldBytes.Length - iPos);
imageBytes = ms.ToArray();
ms.Close();
ms.Dispose();
return imageBytes;
private static List<int> IndexOfSequence(this byte[] buffer, byte[] pattern, int startIndex)
// ref: http://***.com/a/332667/2144390
List<int> positions = new List<int>();
int i = Array.IndexOf<byte>(buffer, pattern[0], startIndex);
while (i >= 0 && i <= buffer.Length - pattern.Length)
byte[] segment = new byte[pattern.Length];
Buffer.BlockCopy(buffer, i, segment, 0, pattern.Length);
if (segment.SequenceEqual<byte>(pattern))
positions.Add(i);
i = Array.IndexOf<byte>(buffer, pattern[0], i + 1);
return positions;
那么为什么会出现这些错误呢?使用内存流生成图像时,我在保存部分的第 18 和第 26 图像处得到第一个错误,在第 25 个图像处得到另一个错误。
【问题讨论】:
您能否发布一个指向 .mdb 文件的链接,其中包含三个麻烦的图像(就像您之前所做的那样)? @GordThompson Here 是,ID = 2 的那个是没有问题的那个。 我可以下载该文件,但是当我尝试打开它时,我收到“无法识别的数据库格式”错误。该文件只有 443 KB,所以我认为它被截断了。 @GordThompson Here 这次我想我会发送除第一个之外的前 30 个。 @GordThompson Here's 该项目如果您想尝试使用完全相同的代码,只需更改数据源并保存路径。 【参考方案1】:错误是由GetImageBytesFromOLEField()
搜索图像签名的顺序引起的。它首先搜索 BMP 签名,不幸的是,该签名非常短(“BM”),因此在某些情况下,它发现图像数据中的一对字节inside 并提取了它的想法是 BMP 数据。
解决方法是更改顺序
var ImageSignatures = new List<byte[]>();
// BITMAP_ID_BLOCK = "BM"
ImageSignatures.Add(new byte[] 0x42, 0x4D );
// JPG_ID_BLOCK = "\u00FF\u00D8\u00FF"
ImageSignatures.Add(new byte[] 0xFF, 0xD8, 0xFF );
// PNG_ID_BLOCK = "\u0089PNG\r\n\u001a\n"
ImageSignatures.Add(new byte[] 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A );
// GIF_ID_BLOCK = "GIF8"
ImageSignatures.Add(new byte[] 0x47, 0x49, 0x46, 0x38 );
// TIFF_ID_BLOCK = "II*\u0000"
ImageSignatures.Add(new byte[] 0x49, 0x49, 0x2A, 0x00 );
到
var ImageSignatures = new List<byte[]>();
// JPG_ID_BLOCK = "\u00FF\u00D8\u00FF"
ImageSignatures.Add(new byte[] 0xFF, 0xD8, 0xFF );
// PNG_ID_BLOCK = "\u0089PNG\r\n\u001a\n"
ImageSignatures.Add(new byte[] 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A );
// GIF_ID_BLOCK = "GIF8"
ImageSignatures.Add(new byte[] 0x47, 0x49, 0x46, 0x38 );
// TIFF_ID_BLOCK = "II*\u0000"
ImageSignatures.Add(new byte[] 0x49, 0x49, 0x2A, 0x00 );
// BITMAP_ID_BLOCK = "BM"
ImageSignatures.Add(new byte[] 0x42, 0x4D );
一旦我这样做了,我就可以处理整个文件:
图像转换前 .mdb 的大小:31.8 MB 转换后但压缩和修复之前的 .mdb 大小:37.1 MB 压缩和修复后的 .mdb 大小:8.5 MB
编辑
这是我用来转换图像并将它们写回数据库的代码:
private void btnStart_Click(object sender, EventArgs e)
using (var con = new OleDbConnection())
con.ConnectionString =
@"Provider=Microsoft.ACE.OLEDB.12.0;" +
@"Data Source=C:\__tmp\test\Bd Fotos Equipamentos 2.mdb;";
con.Open();
using (OleDbCommand cmdIn = new OleDbCommand(), cmdOut = new OleDbCommand())
cmdOut.Connection = con;
cmdOut.CommandText = "UPDATE [Fotografias e Manuais de Equipamentos] SET [FOTO]=? WHERE [ID]=?";
cmdOut.Parameters.Add("?", OleDbType.VarBinary);
cmdOut.Parameters.Add("?", OleDbType.Integer);
cmdIn.Connection = con;
cmdIn.CommandText = "SELECT [ID], [FOTO] FROM [Fotografias e Manuais de Equipamentos]";
OleDbDataReader rdr = cmdIn.ExecuteReader();
while (rdr.Read())
int i = Convert.ToInt32(rdr["ID"]);
lblStatus.Text = string.Format("Processing ID 0...", i);
lblStatus.Refresh();
byte[] b = (byte[])rdr["FOTO"];
byte[] imageBytes = OleImageUnwrap.GetImageBytesFromOLEField(b);
byte[] pngBytes;
using (MemoryStream msIn = new MemoryStream(imageBytes), msOut = new MemoryStream())
Image img = Image.FromStream(msIn);
img.Save(msOut, System.Drawing.Imaging.ImageFormat.Png);
img.Dispose();
pngBytes = msOut.ToArray();
cmdOut.Parameters[0].Value = pngBytes;
cmdOut.Parameters[1].Value = rdr["ID"];
cmdOut.ExecuteNonQuery();
con.Close();
this.Close();
GetImageBytesFromOLEField()
代码与我之前使用的代码相同,为 MaxNumberOfBytesToSearch = 1000000
。
【讨论】:
如果不是要求太多,您是如何将所有内容重新输入到 .mdb 中的?我也在考虑为此编写更多代码,因为我不打算为超过 700 行/图像手动编写代码。 而且我仍然在 ID = 18 one 上的 GDI+ 错误中遇到外部异常 @MicaelFlorêncio 我已经更新了我的答案。我的代码 ID=18 没有任何问题,它提取了 143 KB JPEG 并将其转换为 56 KB PNG。 如果你想尝试使用我为此制作的副项目,here 是。 @MicaelFlorêncio 该文件没有空间,将转换后的图像写回数据库文件会导致它增长(参考:我的初始答案)。您必须将大约一半的记录移动到单独的 .mdb 文件中,执行压缩和修复以释放空间,分别处理这两个文件,压缩并修复它们,然后将它们合并在一起。跨度>以上是关于GDI+ 中的外部异常错误保存特定图像的主要内容,如果未能解决你的问题,请参考以下文章
c#中利用system.timers多线程做图像处理,图像保存时提示“GDI+ 中发生一般性错误”,如何解决?
GDI+ 中发生一般性错误。 究竟是啥问题不是一般性的图片上传出错。