Распакуйте из текстового столбца SQL Server в столбец изображения - PullRequest
1 голос
/ 15 апреля 2011

У меня есть изображения различных форматов (.png, .jpg, .bmp и т. Д.), Которые хранятся в виде сжатого текста в текстовом столбце таблицы SQL Server 2005. Мне нужно прочитать строку, разархивировать изображение и сохранить его в столбце изображения в другой таблице.

Я использую библиотеку SharpZip, и все примеры касаются источников и мест назначения файлов. Я не могу найти ничего, что охватывает разархивирование из переменной в другую переменную. Фрагмент кода, иллюстрирующий это, или ссылка на соответствующий ресурс, будет очень признателен.

РЕДАКТИРОВАТЬ: немного больше информации - данные хранятся в столбце TEXT. Это выглядит следующим образом (текстовый столбец сокращенно для отображения):

ImageID  ImageData
1       FORMAT-ZIPV3 UEsDBBQAAAAIAOV6wzxdTnDvshs...
2       FORMAT-ZIPV3 UEsDBBQAAAAIAAF2yjxGncjOLgA...
3       FORMAT-ZIPV3 UEsDBBQAAAAIAKd6yjyjnQNr6gg...
4       FORMAT-ZIPV3 UEsDBBQAAAAIALdNyzyrPC8EMJw...
5       FORMAT-ZIPV3 UEsDBBQAAAAIAA1rOD1nZY1t0f0...
6       FORMAT-ZIPV3 UEsDBBQAAAAIANZplj2seyJ+VmM...
7       FORMAT-ZIPV3 UEsDBBQAAAAIAC5vhD27LPbPcv8...
8       FORMAT-ZIPV3 UEsDBBQAAAAIAK1qKz5DJNH3xMg...
9       FORMAT-ZIPV3 UEsDBBQAAAAIAHVkEztC3th/9hs...
10     FORMAT-ZIPV3 UEsDBBQAAAAIAEtXKz7DXHUdvow...

Что я точно знаю, так это то, что изображения были сжаты в какой-то момент процесса с использованием SharpZip перед вставкой в ​​таблицу. Похоже, что информация о формате была добавлена ​​в начало данных перед вставкой.

Глядя на эти данные, сможет ли кто-нибудь понять, как эти данные изображения манипулировали? Опять же, мне нужно поместить несжатые данные изображения в столбец с типом данных, способствующим чтению, для отображения на веб-странице.

РЕДАКТИРОВАТЬ: Хорошо, я в тупике. Выполнение следующего кода приводит к ошибке «Не удалось преобразовать значение параметра из Int32 в байт []». Кажется, он помещает длину байтового массива в значение байтового массива ...

        commandUncompressed.Connection = connectionUncompressed;
        commandUncompressed.Parameters.Add("@Image_k", SqlDbType.VarChar, 10);
        commandUncompressed.Parameters.Add("@ImageContents", SqlDbType.Image);
        commandUncompressed.CommandText = sqlSaveImage;

        connectionUncompressed.Open();
        reader = command.ExecuteReader();

        if (reader.HasRows)
        {
            while (reader.Read())
            {
                Console.WriteLine(reader["Image_k"].ToString());  // Merely for testing
                String format = reader["ImageContents_Compressed"].ToString().Substring(0, 12);
                var offset = 13; //"FORMAT-ZIPV3 ".Length;
                var s = reader["ImageContents_Compressed"].ToString().Substring(offset);
                var bytes = Convert.FromBase64String(s);
                if (format == "FORMAT-ZIPV2 ")
                {
                    bytes = ConvertStringToBytes(s);    // Not a Base-64 encoded string? External conversion function utilized.
                }

                using (var zis = new ZipInputStream(new MemoryStream(bytes)))
                {
                    ZipEntry zipEntry = zis.GetNextEntry();   // Doesn't seem to work unless an entry has been referenced
                    byte[] buffer = new byte[zis.Length];
                    commandUncompressed.Parameters["@Image_k"].Value = reader["Image_k"].ToString();
                    commandUncompressed.Parameters["@ImageContents"].Value = zis.Read(buffer, 0, buffer.Length);
                    commandUncompressed.ExecuteNonQuery();

                }
            }
        }

Кажется, что он читает данные из столбца исходного текста просто отлично. Я просто не могу понять, как получить это в параметре типа изображения. Значение для переменной буфера показывает длину байтового массива, а не фактические байты. Может быть, именно это свойство значения обычно показывает для байтовых массивов? Я так близко и все же так далеко. : /

РЕДАКТИРОВАТЬ: Хорошо, я тупица. Я сделал следующее исправление, и оно работает!

zis.Read(buffer, 0, buffer.Length)                            
commandUncompressed.Parameters["@ImageContents"].Value = buffer;

На данный момент я могу обрабатывать только данные FORMAT-ZIPV3, так как я еще не понял, как декодировать строки FORMAT-ZIP2. Ниже приводится выборка данных V2. Если кто-нибудь сможет определить кодировку, дайте мне знать. Будет ли по-другому, если архив с использованием BZIP вместо формата ZIP?

ImageID ImageData
1          FORMAT-ZIPV2 504B03041400020008005157422A2E25FDBAF26701008D6901000E...
2          FORMAT-ZIPV2 504B03041400020008009159422A7FC94BA2B2540500D35705000E...
3          FORMAT-ZIPV2 504B0304140002000800685A422A0CAA51F4473A0600B97206000E...
4          FORMAT-ZIPV2 504B03041400020008001D5D422A770BD3ED201902002C4A02000E...
5          FORMAT-ZIPV2 504B0304140002000800325E422A4B6C2FB4045001001C6E01000E...
6          FORMAT-ZIPV2 504B03041400020008006F72422A5F793AC1A1F00200ECF302000E...
7          FORMAT-ZIPV2 504B0304140002000800D572422A1B348A731DE5000085EB00000E...
8          FORMAT-ZIPV2 504B03041400020008003D73422A8AEBB7F855640300DD1B04000E...
9          FORMAT-ZIPV2 504B03041400020008006368D528C5D0A6BA794900004A2502000E...
10         FORMAT-ZIPV2 504B03041400020008008E5B6C2A2D9E9C33D7AF05005CEC05000E...

Ответы [ 3 ]

3 голосов
/ 16 апреля 2011

В ответ на аналогичный вопрос кто-то на sqlmonster.com предоставил классный класс VarBinaryStream . Работает с типом столбца varbinary(max).

Если ваши данные хранятся в varbinary (max) и в формате zip, вы можете использовать этот класс для создания экземпляра VarBinaryStream, а затем создать экземпляр ZipInputStream вокруг , который , и ba-da-boom , ты здесь. Просто прочитайте из ZipInputStream.

В C # это может выглядеть так

using (var imageSrc = new VarBinarySource(connection, 
                                          "Table.Name", 
                                          "Column",
                                          "KeyColName",
                                          1))
{
    using (var s = new VarBinaryStream(imageSrc))
    {
        using(var zis = new ZipInputStream(s))
        {
           ....
        }
    }
}

Если изображения маленькие, то вам, вероятно, не понадобятся все эти потоковые вещи. Если столбец имеет тип binary(n) или varbinary(n), где n меньше 8000, просто используйте тип SqlBinary и прочитайте все данные в память, а затем создайте экземпляр MemoryStream вокруг этого. Simpler. В VB.NET это выглядит примерно так:

Dim bytes as Bytes()
bytes = dr.GetSqlBinary(columnNumber)
Using ms As New MemoryStream(bytes)
    Using zis As New ZipInputStream(ms)
        ...
    End Using
End Using 

Наконец, я собираюсь поставить под сомнение целесообразность применения сжатия zip к изображениям .jpg и т. П. Формат jpg уже сжат; Сжатие еще раз перед помещением данных в SQL Server не приведет к значительному уменьшению данных. Это только увеличивает время обработки. Если возможно, я бы посоветовал вам пересмотреть свой дизайн для хранения сжатых изображений.

1 голос
/ 17 апреля 2011

хорошо, с предоставленным вами обновлением, содержащим формат данных, вы можете сделать некоторые выводы.

Данные являются фактической строкой.Подозревая, что это строка в кодировке Base64, я провел небольшой тест и использовал Convert.ToBase64String() в потоке байтов, содержащем zip-файл.Это выглядит так: UEsDBBQAAAAIAJJyYyk3M56F+QIAA...

Ага!у вас есть байт-кодированная (строковая) версия байтовых данных для истинного zip-файла.Чтобы декодировать его, удалите префикс и затем используйте FromBase64String () , чтобы получить массив байтов, вставить его в MemoryStream, затем прочитать его с помощью ZipInputStream.

примерно так:

var offset = "FORMAT-ZIPV3 ".Length(); 
var s = sqlReader["CompressedImage"].ToString().Substring(offset);
var bytes = Convert.FromBase64String(s);    
using (var zis = new ZipInputStream(new MemoryStream(bytes))) 
{
    ...
    zis.Read(...);
    ...
}

Если данные «очень длинные», вы захотите вывести их из этой таблицы, а не просто прочитать их в большую строку и преобразовать.Я не знаю, насколько большими могут быть текстовые столбцы, но предположим, что это может быть 500 МБ, вам не нужна строка 500 МБ, и вы не хотите выполнять преобразование строки 500 МБ с помощью Convert.FromBase64String ().В этом случае вам необходимо использовать Base64Stream или FromBase64Transform в пространстве имен System.Security.Cryptography.


Ред. Коммент.Это своего рода обратная сторона для сжатия данных изображения.Изображения, вероятно, уже сжаты.Но чтобы усугубить эту отсталость, сделав кодирование base64, расширив тем самым данные ... ???Это в три раза назад.Это вообще не имеет смысла.Я понимаю, что так поступил ваш поставщик.


Хорошо, с вашим дальнейшим обновлением, используя этот формат:

ImageID ImageData
1          FORMAT-ZIPV2 504B03041400020008005157422A2E25FDBAF26701008D6901000E...
2          FORMAT-ZIPV2 504B03041400020008009159422A7FC94BA2B2540500D35705000E...

Эти данные все еще являются данными zipfile, но этозакодированы как простые шестнадцатеричные цифры.Вам нужно преобразовать это в байтовый массив.Вот некоторый код, чтобы сделать это.

public static class ConvertEx
{
    static readonly String prefix= "FORMAT-ZIPV2 ";

    public static string ToHexString(byte[] b)
    {
        System.Text.StringBuilder sb1 = new System.Text.StringBuilder();
        int i = 0;
        for (i = 0; i < b.Length; i++)
        {
            sb1.Append(System.String.Format("{0:X2}", b[i]));
        }
        return sb1.ToString().ToLower();
    }

    public static byte[] ToByteArray(string s)
    {
        if (s.StartsWith(prefix))
        {
            System.Console.WriteLine("removing prefix");
            s = s.Substring(prefix.Length);
        }
        s= s.Trim(); // whitespace
        System.Console.WriteLine("length: {0}", s.Length);

        var r= new byte[s.Length/2];
        for (int i = 0; i < s.Length; i+=2)
        {
            r[i/2] = (byte) Convert.ToUInt32(s.Substring(i,2), 16);
        }
        return r;
    }
}

Вы можете использовать это следующим образом:

        string s = GetStringContentFromDatabase()
        var decoded = ConvertEx.ToByteArray(s);

        using (var ms = new MemoryStream(decoded))
        {
            // use DotNetZip to read the zip file
            // SharpZipLib is something similar...
            using (var zip = ZipFile.Read(ms))
            {
                // print out the list of entries in the zipfile
                foreach (var e in zip)
                {
                    System.Console.WriteLine("{0}", e.FileName);
                }
            }
        }
0 голосов
/ 15 апреля 2011

В примерах на SharpZip Wiki используются объекты Stream - в то время как в примере используется файл, вы можете легко использовать здесь объект MemoryStream , и образец будет работать так же.

...