I needed to grab the images from a database, so I made some code for it. Issue is getting that error in some images and an Invalid Parameter error in others.
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);
}
}
The OleImageUnwrap.GetImageBytesFromOLEField is used to remove the header from the OLE object that is the image in the Database and get only the bytes of the image itself :
public static byte[] GetImageBytesFromOLEField(byte[] oleFieldBytes, int NumMaximoBytesSearch)
{
//ref http://stackoverflow.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://stackoverflow.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;
}
}
So why are these erros even appearing? I get the first error at the 18th and 26th image at the save part and the other at the 25th when generating the image with the memory stream.
The errors were caused by the order in which GetImageBytesFromOLEField()
was searching for image signatures. It was searching for the BMP signature first, and unfortunately that signature is very short ('BM') so in a few cases it found that pair of bytes inside the data of the image and extracted what it thought was BMP data.
The fix was to change the order from
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 });
to
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 });
Once I did that I could process the entire file:
Size of .mdb before image conversion: 31.8 MB
Size of .mdb after conversion but before Compact and Repair: 37.1 MB
Size of .mdb after Compact and Repair: 8.5 MB
Edit
This is the code that I used to convert the images and write them back to the database:
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();
}
The GetImageBytesFromOLEField()
code is the same as I used before, with MaxNumberOfBytesToSearch = 1000000
.
来源:https://stackoverflow.com/questions/20095591/external-exception-in-gdi-error-saving-specific-image