Внешнее исключение в ошибке GDI+ при сохранении определенного изображения

Мне нужно было получить изображения из базы данных, поэтому я написал для этого код. Проблема заключается в получении этой ошибки на некоторых изображениях и ошибки Invalid Parameter на других.

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

Так почему эти ошибки вообще появляются? Я получаю первую ошибку на 18-м и 26-м изображениях в части сохранения, а другую - на 25-м при создании изображения с потоком памяти.


person Micael Florêncio    schedule 20.11.2013    source источник
comment
Можете ли вы опубликовать ссылку на файл .mdb с тремя неприятными изображениями в нем (как вы делали раньше)?   -  person Gord Thompson    schedule 20.11.2013
comment
@GordThompson Вот тот, что с идентификатором = 2 является одним из тех, которые не показывают никаких проблем.   -  person Micael Florêncio    schedule 20.11.2013
comment
Я могу загрузить файл, но когда я пытаюсь его открыть, я получаю ошибку «Нераспознанный формат базы данных». Файл всего 443 КБ, поэтому я думаю, что он как-то обрезался.   -  person Gord Thompson    schedule 20.11.2013
comment
@GordThompson Здесь На этот раз решил отправить первое 30, кроме 1-го.   -  person Micael Florêncio    schedule 20.11.2013
comment
@GordThompson Вот проект, если вы хотите попробовать точно такой же код, только изменить источник данных и сохранить пути.   -  person Micael Florêncio    schedule 20.11.2013


Ответы (1)


Ошибки были вызваны порядком, в котором GetImageBytesFromOLEField() искал сигнатуры изображений. Сначала он искал сигнатуру BMP, и, к сожалению, эта сигнатура очень короткая («BM»), поэтому в нескольких случаях она находила эту пару байтов внутри данных изображения и извлекала то, что думала. были данные 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 });

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

Как только я это сделал, я мог обработать весь файл:

Размер .mdb до преобразования образа: 31,8 МБ
Размер .mdb после преобразования, но до сжатия и восстановления: 37,1 МБ
Размер .mdb после сжатия и восстановления: 8,5 МБ

Редактировать

Это код, который я использовал для преобразования изображений и записи их обратно в базу данных:

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.

person Gord Thompson    schedule 20.11.2013
comment
Если это не требует слишком многого, как вы снова ввели все обратно в .mdb? Я думал о том, чтобы сделать больше кода для этого, так как я не планирую делать это вручную для более чем 700 строк/изображений. - person Micael Florêncio; 20.11.2013
comment
И я все еще получаю внешнее исключение в GDI+ Error на ID = 18 один - person Micael Florêncio; 20.11.2013
comment
@MicaelFlorêncio Я обновил свой ответ. В моем коде не было проблем с ID=18, он извлек JPEG размером 143 КБ и преобразовал его в PNG размером 56 КБ. - person Gord Thompson; 20.11.2013
comment
Если вы хотите попробовать использовать побочный проект, который я сделал для этого, вот. - person Micael Florêncio; 20.11.2013
comment
Я попробовал ваш код для повторной вставки изображений, и с .mdb, который я отправил вам, он тоже работал отлично, но когда я попробовал его на .mdb, который был заполнен, он выдал необработанную ошибку OleDbException. Если быть точным, его размер составляет 1,95 ГБ (2 103 562 240 байт) с 692 строками. - person Micael Florêncio; 20.11.2013
comment
@MicaelFlorêncio В этом файле нет места, и запись преобразованных изображений обратно в файл базы данных приводит к его увеличению (ссылка: мой первоначальный ответ). Вам придется переместить около половины записей в отдельный файл .mdb, выполнить сжатие и восстановление, чтобы освободить место, обработать два файла по отдельности, сжать и восстановить их, а затем снова объединить их вместе. - person Gord Thompson; 20.11.2013
comment
Большое спасибо, делаю это сейчас. Однако не могли бы вы выяснить, что не так с этим проектом, который по-прежнему выдает ошибку с идентификатором = 18? Было бы полезно иметь фрагмент кода, успешно извлекающий все изображения из .mdb за один раз или за несколько. - person Micael Florêncio; 20.11.2013
comment
И не знаю, пытались ли вы запустить код над .mdb во второй раз после успешного запуска в первый раз, но если вы запустите второй раз, он выдаст ошибку ArgumentException в строке Image img = Image.FromStream(msIn); на ID = 7. Мне пришлось, потому что на ID=62 в OLE не было изображения, и из-за этого выдавало ошибку, так что остальное не было проанализировано. - person Micael Florêncio; 20.11.2013
comment
@MicaelFlorêncio Странно, я только что запустил свой код во второй раз в преобразованной базе данных и не получил ошибки. Все, что он сделал, это раздул базу данных с 8,5 МБ до 14,3 МБ, предположительно потому, что он извлекал необработанные файлы PNG, а затем снова записывал их обратно в базу данных. (После сжатия и восстановления размер файла снова уменьшился до 8,5 МБ.) - person Gord Thompson; 20.11.2013
comment
Очень. Точнее он говорит, что это недопустимый параметр. Мое решение состояло в том, чтобы взять оригинал и снова разделить его. В любом случае, первая часть уже сделана, она увеличилась с 600 МБ до 132 МБ. - person Micael Florêncio; 20.11.2013
comment
@MicaelFlorêncio Остальные изображения были преобразованы в порядке? Кстати, я пытался открыть ваш проект, но не смог. (Этот проект несовместим с текущей версией Visual Studio. Я все еще использую 2010.) - person Gord Thompson; 21.11.2013
comment
Почти все переделано. Когда я схватил вторую маленькую базу данных, некоторые из них просто не будут конвертироваться с параметром ArgumentException - Invalid. Вот база данных и принтскрин ошибки, если хотите попробовать . - person Micael Florêncio; 21.11.2013
comment
@MicaelFlorêncio Для ID = 12 данные JPEG начинаются со смещения 0x10369E (1 062 558). Остальные три идентификатора (7, 10 и 11) просто странные. Вероятно, на данном этапе проще всего просто преобразовать их вручную: дважды щелкните их в Access, чтобы открыть в Word, затем щелкните правой кнопкой мыши изображение и выберите «Сохранить как изображение». используйте немного кода C#, чтобы вставить их в соответствующие записи базы данных как необработанные двоичные файлы. - person Gord Thompson; 21.11.2013