Хранение двоичной информации в XML файле

К началу раздела

Раздел 10. Глава 3. Хранение двоичной информации в XML файле

Аннотация: В главе представлены собственные практические наработки, которые стали результатом попытки ответа на один из многочисленных вопросов, поступающих автору, и на который, "с налету", ответа не нашлось. И, как результат, более глубокого погружения в XML и материалов этой главы, стала следующая глава "Собственная база данных на основе XML файла ". Применение материалов данных глав позволяет создавать однопользовательские базы данных (или собственные базы данных для приложений) на основе XML файлов без использования СУБД.


В начало

Параграф 1. XML файл и возможность хранения двоичных данных
(рисунков, музыкальных файлов, документов)

Напомним определение: "Содержимым элемента (content) называется всё, что расположено между открывающим и закрывающим тегами. Это текст, вложенные элементы, комментарии и т.п. ". Все спецификации XML документов подчеркивают, что именно текст, то, что является содержимым документа. Все средства для работы с XML документами, в том числе и методы класса XmlDocument, также ориентированы на текст (CreateTextNode и т.п.).

Из сказанного можно сделать вывод - хранить двоичные данные можно только если они будут представлены как текстовые строки. Однако, прямое преобразование всего массива байт (двоичных данных) в текстовую строку не будет решением в силу того, что возможно присутствие в двоичном коде значений, совпадающих со служебными символами кодировки строк и значений равных завершению строки. Как результат - мы сможем поместить в контент тэга только часть строки (до первого совпадения двоичного символа со значением окончания строки) при возможном отображении помещенного кода на нескольких строках XML файла для одного содержимого контента. Следовательно, строка должна формироваться и присваиваться значению контента как единое целое. Способы выполнить это могут быть разные, от посимвольного преобразования байт в значение char и последовательного добавления результата в содержимое контента (медленный способ), до использования StringBuilder (быстрый способ). Ниже рассмотрен способ использования именно класса построителя строк.

И последнее замечание - наиболее часто хранение двоичных данных связано с задачей хранения рисунков (хотя приведенный ниже код будет равно хорошо работать и при использовании его для хранения в виде двоичных данных Web страниц, Word или Excel документов и т.д. и т.п..). Содержимое файла рисунка - массив шестнадцатеричных символов. Как следствие, и код представления рисунка в контенте опирается на шестнадцатеричное отображение строк.


В начало

Параграф 2. Сохраняем рисунок в XML файл

Создадим простое решение приложения имеющее две кнопки (два контрола Button на форме, пока без обработчиков событий нажатия кнопок) выполним его в режиме Release. В директорию, где будет сформирован exe файл приложения (у меня C:\Samples_C#_2008\WindowsFormsApplication2\bin\Release\), поместим XML файл (хотя поместить файл можно и куда угодно). Пусть это будет файл с именем sales.xml, а его содержимое - данные о собачках, подготовленных к продаже неким обществом, этим занимающимся:

<?xml version="1.0" encoding="windows-1251"?>
<NewDataSet>
 <dog>
  <id></id>
  <name></name>
  <pic></pic>
  <price></price>
 </dog>
</NewDataSet>

Обратим внимание на тэг NewDataSet. Почему он введен будет ясно только из материала следующей главы, пока это примем как должное. Назначение других тэгов ясно из их названий.

Наша задача - поместить в тэг PIC - рисунок. Для этого создадим обработчик нажатия кнопки 1, в коде которого и выполним сохранение рисунка в XML файл:

public partial class Form1 : Form
{
 //Переменные класса
 private string sCurDir = string.Empty;
 private XmlDocument xmldoc = null;
 public Form1()
 {
  InitializeComponent();
 }
 private void Form1_Load(object sender, EventArgs e)
 {
  //Текущая директория exe файла
  sCurDir = Directory.GetCurrentDirectory();
 }
 private void button1_Click(object sender, EventArgs e)
 {
  StringBuilder MyStringBuilder = new StringBuilder();
  xmldoc = new XmlDocument();
  //Загружаем пустой XML файл из \bin\Release\
  xmldoc.Load(sCurDir + @"\sales.xml");
  //rootnode NewDataSet
  XmlNode rootnode = xmldoc.DocumentElement;
  //Узлы dog  их у нас пока один
  foreach (XmlNode childnode2 in rootnode.ChildNodes)
  {
   //Узлы ID, NAME, PRICE и PIC
   foreach (XmlNode childnode in childnode2)
   {
    if (childnode.LocalName.ToUpper().Contains("ID"))
    {
     childnode.InnerText = "1";
    }
    if (childnode.LocalName.ToUpper().Contains("NAME"))
    {
     childnode.InnerText = "Шарпей";
    }
    if (childnode.LocalName.ToUpper().Contains("PRICE"))
    {
     childnode.InnerText = "1000";
    }
    //Сюда помещаем рисунок
    if (childnode.LocalName.ToUpper().Contains("PIC"))
    {
     //Файл фото шарпея dog.gif предварительно помещен в ...\bin\Release\
     //там где и exe файл и файл sale.xml
     using (FileStream filestream = new FileStream(sCurDir + @"\dog.gif", FileMode.Open))
     {
      byte[] b = new byte[filestream.Length];
      //Байты фото переносим в  массив b
      filestream.Read(b, 0, (int)filestream.Length);
      foreach (Byte zb in b)
      {
       int a = (int)zb;
       //Шестнадцатеричное представление байт рисунка 
       MyStringBuilder.Append(a.ToString("X2"));
      }
      //Переносим шестнадцатеричное представление строки (представление массива байт) в тэг PIC
      childnode.InnerText = Convert.ToString(MyStringBuilder);
     }
    }
    //Сохраняем 
    xmldoc.Save(sCurDir + @"\sales1.xml");
   }
  }
 }
....

Файл XML будет выглядеть следующим образом:

<?xml version="1.0" encoding="windows-1251"?>
<NewDataSet>
  <dog>
    <id>1</id>
    <name>Шарпей</name>
    
    //Здесь двоичное представление рисунка - к сожалению несколько сот байт не поместить в одну 
    //строку, поэтому недостающие сотни байт показаны точками
    <pic>474946383961C5006500F70000000000800000008000808000000080800080008080C0C0C0C......</pic>
    
    <price>1000</price>
  </dog>
</NewDataSet>

Отмеченное цветом показывает и недостаток хранения данных в XML файлах - длина строки, хотя это не значит, что при хранении в таблицах баз данных мы выиграем в объеме или скорости обработки (скорее наоборот проиграем за счет сетевого трафика).

Обратим внимание, что, как отмечено выше, точно таким образом можно сохранить в файл любые данные (Web страницы, Word или Excel документы и т.д. и т.п...). Проверить это не сложно, достаточно загрузить вместо картинки требуемый документ.


В начало

Параграф 3. Извлекаем рисунок (двоичные данные) из XML файла

Создадим обработчик нажатия кнопки 2, в коде которого и выполним извлечение рисунка из XML файла:

private void button2_Click(object sender, EventArgs e)
{
 xmldoc = new XmlDocument();
 xmldoc.Load(sCurDir + @"\sales1.xml");
 XmlNode rootnode = xmldoc.DocumentElement;
 //Корневой узел
 foreach (XmlNode childnode2 in rootnode.ChildNodes)
 {
  foreach (XmlNode childnode in childnode2)
  {
   if (childnode.LocalName.ToUpper().Contains("PIC"))
   {
    //При выходе из  using (FileStream .... изображение будет сохранено в файле dog1.gif
    using (FileStream filestream = new FileStream(sCurDir + @"\dog1.gif", FileMode.OpenOrCreate))
    {
     //Переносим представление изображения в строку
     string s = childnode.InnerText;
     for (int i = 0; i < s.Length / 2; i++)
     {
      if (i * 2 +1 < s.Length)
      {
       string s2 = Convert.ToString(s[i * 2]);
       string s3 = Convert.ToString(s[i * 2 + 1]);                                    
       string s4 = s2 + s3;
       //Восстанавливаем в filestream изображение
       int a = int.Parse(s4, System.Globalization.NumberStyles.HexNumber);
       filestream.WriteByte(Convert.ToByte(a));
      }
     }
    } 
   }
  }
 }//foreach (XmlNode childnode2 in rootnode.ChildNodes)
}

Обратим внимание, что, как отмечено выше, точно таким образом можно сохранять и восстанавливать любые данные (Web страницы, Word или Excel документы и т.д. и т.п...). Проверить это не сложно, достаточно попытаться загрузить и восстановить вместо картинки требуемый документ.

Молчанов Владислав 14.12.2007г.

Еcли Вы пришли с поискового сервера - посетите мою главную страничку

На главной странице Вы найдете программы комплекса Veles - программы для автолюбителей, программу NumberPhoto, созданную для работы с фото, сделанными цифровым фотоаппаратом, программу Локальный Web сайт - предназначенную для просмотра и прослушивания файлов большинства графических и звуковых форматов в Web Browser, программу Bricks - игрушку для детей и взрослых, программу записную книжку, программу TellMe - говорящий Русско-Английский разговорник - программу для тех, кто собирается погостить за бугром или повысить свои знания в английском, теоретический материал по программированию в среде Borland C++ builder, C# (Windows приложения и ASP.Net Web сайты).

В начало страницы

К началу раздела

В начало книги

На главную страницу сайта


Сайт управляется системой uCoz