"Любая задача требует вдвое больше времени и втрое больше усилий, чем кажется на первый взгляд",- сказал в прошлом веке кто-то из великих. В этом убеждаешься каждый раз, когда берешься решать какую-нибудь простую, на первый взгляд, задачу.

 

В правильности "вечного" высказывания я смог убедится на собственном опыте, когда очередной заказчик предложил решить "простенькую" задачку - создать электронный каталог на 2000 товаров. Главное условие - помимо поисков, сортировок и различных выдаваемых на печать документов, в каталоге должны быть фотографии, иллюстрирующие товар. "Простенькой" задачка перестала быть сразу, как только я понял, какой размер будет иметь база данных, созданная по рекомендациям справочной системы Access.

 

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

 

Подобную - но не эту. Специфика хранения информации в Поле объекта OLE такова, что в процессе занесения информации проводится преобразование содержимого графического файла в специальный формат, ориентированный на работу с OLE-автоматизацией. Для файлов, имеющих формат BMP, данное преобразование приводит к уменьшению объема хранимой информации, но для файлов, формат которых ориентирован на значительное сжатие информации (JPG, GIF и др.), размер хранимой в БД информации значительно возрастает. Решением проблемы для большинства приложений может быть хранение образа графического файла в виде байтового массива в поле, имеющем тип Поле объекта OLE.

 

В рассматриваемом нами примере, 2000 графических файлов в формате JPG суммарным объемом около 30 Мб не удастся импортировать в БД стандартным методом - поскольку расчетный размер БД значительно превысит допустимый в Access предел на объем файла базы данных (2048 Мб).

 

Стандартный метод

 

Стандартный метод хранения графических файлов основан на использовании поля, имеющего тип Поле объекта OLE. Этот тип поля предназначен для хранения всех зарегистрированных типов файлов сервера объектов OLE (не только графических, но и документов MS Word, таблиц Exсel и т.д.). Если опустить подробности, то механизм работы сводится к следующему. Access - на основании данных о классе содержимого поля - вызывает приложение, зарегистрированное для обработки такого класса информации. При этом все функции по манипулированию информацией, извлеченной из поля, возлагаются на вызванное приложение. По завершении приложение при необходимости возвращает измененные данные.

 

Для хранения графических файлов создадим таблицу tbdT, состоящую из двух полей: strNameFile (тип Текстовый) - для названия файла и oleFile (тип Поле объекта OLE) - для самой фотографии. Сделаем поле strNameFile ключевым, чтобы избежать дублирования одноименных файлов, полученных из разных папок.

 

Далее переходим к интерфейсу. Запустим Мастер форм и создадим форму frmT, в качестве источника данных используем таблицу tbdT. Выберем внешний вид формы и ее стиль, нажимаем кнопку "Готово" - и форма перед нами. На основе формы frmT легко получить отчет, сохранив форму в виде отчета rptТ.

 

Вновь вернемся к форме frmT и попробуем поместить фотографию. Нужно скопировать файл с жесткого диска в буфер обмена и вставить его в поле oleFile. Для помещения всех фотографий в БД таким способом эту операцию нужно будет проделать еще 1999 раз. Привлечение миловидной девушки-оператора для решения данной задачи нельзя считать оптимальным хотя бы по эстетическим соображениям. Рассмотрим решение, требующее минимума ручной работы и опирающееся на содержимое справочной системы.

 

Создаем в форме frmT поле txtPathFile (для указания пути к каталогу на диске, где хранятся фотографии) и кнопку cmdKey_Import. Снабжаем событие Нажатие (Click) кнопки cmdKey_Import следующим кодом:

 

Private Sub cmdKey_Import_ Click ()
Dim strPath As String, strFileName As String, strFullFileName As String
DoCmd.GoToRecord,, acFirst
strPath = Me!txtPathFile ' "C:\p\"
strFileName = Dir$(strPath + "*.jpg", vbNormal)
Do While strFileName <> ""
strFullFileName = strPath + strFileName
Me!strNameFile = strFullFileName
With Me. oleFile
.Class = "MSPhotoEd.3"
.OLETypeAllowed = acOLEEmbedded ' внедренный
.SourceDoc = strFullFileName
.Action = acOLECreateEmbed
End With
strFileName = Dir$
DoCmd.GoToRecord,, acNewRec
Loop
End Sub
 

Рассмотрим работу процедуры cmdKey_Import_Click подробнее. Вначале форма устанавливается на первую запись. Затем получаем из поля формы путь к каталогу с фотографиями. Берем имя первого файла из каталога при помощи функции Dir$, заполняем значения поля strNameFile и переходим к формированию данных поля oleFile. Устанавливаем класс хранимого в нем объекта MSPhotoEd.3, тип хранения Внедренный, а в качестве местоположения - полный путь к файлу. Занесение информации в БД происходит после установки значения свойства Action.

 

Получаем следующее имя файла фотографии… И так до тех пор, пока не переберем все фотографии в выбранной нами папке.

 

Сравнительные характеристики стандартного и альтернативного методов 


Стандартный 


Пространство, занимаемое БД на жестком диске (Мб) - 175
Время импорта 100 фотографий размером 2,55 Мб (сек) - 48
Средняя скорость импорта фотографий (штук в секунду) - 2

 

Альтернативный

 

Пространство, занимаемое БД на жестком диске (Мб) - 3,48
Время импорта 100 фотографий размером 2,55 Мб (сек) - 0,4
Средняя скорость импорта фотографий (штук в секунду) - 250

 

Для тестового прогона оставим в каталоге только 100 фотографий. Вводим в поле txtPathFile путь к тестовой папке, нажимаем кнопку cmdKey_Import - и через несколько минут пролистываем полученное содержимое в форме. Все в порядке. Для исключения побочных эффектов при оценке размера полученной БД проводим ее сжатие. А вот и обещанный сюрприз! БД после импорта ста графических файлов суммарным размером 2,55 Мб имеет размер 175 Мб. То есть расчетный размер БД для всех 2000 графических файлов составит приблизительно 3,5 Гб.

 

Альтернативный метод

 

В основе альтернативного метода лежит возможность хранить в полях, имеющих тип Поле объекта OLE, любых данных в виде байтового массива, а также наличие в VBA (и VB) встроенной функции LoadPicture. Образ графического файла хранится в БД в виде байтового массива. При отображении из этого образа формируется файл, который и отображается приложением.

 

Первый вариант

 

В качестве хранилища используется таблица tbdT, созданная при рассмотрении типового метода хранения. Для импорта создаем новую форму - frmT_Import. Для этого сохраним форму frmT как экспорт в текущей БД с именем frmT_Import. Затем в конструкторе удалим в ней все элементы, кроме кнопки cmdKey_Import и поля txtPathFile. Заменим процедуру обработки нажатия кнопки cmdKey_Import на следующую:

 

Option Compare Database
Option Explicit
Dim dbD As Database, recR As Recordset

Private Sub cmdKey_Import_Click ()
Dim strFileName As String, strFullFileName As String, strPath As String
Dim intNF As Integer, bytB () As Byte
strPath = Me!txtPathFile  ' "C:\p\"
strFileName = Dir$(strPath + "*.jpg", vbNormal)
Set dbD = CurrentDb
dbD.Execute "DELETE * FROM tbdT"  ' очишаем таблицу
Set recR = dbD.OpenRecordset ("tbdT", dbOpenDynaset)
Do While strFileName <> ""
strFullFileName = strPath + strFileName
intNF = FreeFile
Open strFullFileName For Binary Access Read As #intNF
ReDim bytB (FileLen (strFullFileName))
Get #intNF,, bytB
Close #intNF
recR.AddNew
recR!strNameFile = strFileName
recR!oleFile.Value = bytB
recR.Update
strFileName = Dir$
Loop
recR.Close
End Sub
 

Поскольку эта процедура использует объект Recordset, необходимо добавить ссылку на Microsoft DAO 3.6 Object Library (или другой версии), выбрав команду References в меню Tools. Если этого не сделать, модуль будет выдавать сообщение об ошибке.

 

Рассмотрим работу процедуры подробнее. Вначале мы получаем из поля формы путь к каталогу с фотографиями. Затем при помощи функции Dir$ берем имя первого файла из каталога, открываем двоичный файл и считываем в один прием содержимое всего файла в массив bytB, предварительно настроенный нами под длину файла оператором ReDim. Закрываем файл. Проводим манипуляции по добавлению записи в таблицу. Получаем следующее имя файла фотографии… И так далее, пока не переберем все фотографии в выбранной нами папке.

 

Теперь рассмотрим вопрос отображения хранимой информации. Примененная в форме frmT при рассмотрении стандартного метода присоединенная рамка объекта не годится, так как не способна интерпретировать данные из поля oleFile из-за их формата. Поэтому для отображения будет использоваться элемент Рисунок, так как он обладает возможностью непосредственной интерпретации графических файлов по их расширению.

 

Проделаем следующее: сохраним как экспорт в текущей БД форму frmT под именем frmT_View1. Удалим в ней поле oleFile и вместо него вставим в форму элемент Рисунок с именем imgFile. При создании элемента необходимо вставить в него любую картинку. Теперь, для того чтобы отображать содержимое, нужно добавить в событие Текущая запись (Current) формы frmT_View1 следующий код:

 

Option Compare Database
Option Explicit
Dim dbD As Database, recR As Recordset
Const strTempPath As String = "c:\temp_mdb"

Private Sub Form_ Current ()
Dim intNF As Integer, bytB () As Byte
Dim strPath As String
If IsNull (Me!strNameFile.Value) Then Exit Sub
strPath = Dir$(TempPath, vbDirectory)
If strPath = "" Then MkDir strTempPath
Set dbD = CurrentDb
Set recR = dbD.OpenRecordset ("select * from tbdT
where strNameFile=""" + strNameFile + """", _ dbOpenDynaset)
If Not recR.EOF Then
strPath = strTempPath + "\" & Me!strNameFile.Value
If Dir$(strPath, vbNormal) <> "" Then Kill strPath
intNF = FreeFile
Open strPath For Binary Access Write As #intNF
bytB = recR!oleFile.Value
Put #intNF,, bytB
Close #intNF
Else
strPath = ""
End If
recR.Close
If strPath = "" Then Exit Sub
With Me.imgFile
.Visible = False
.Picture = strPath
.Visible = True
End With
Kill strPath
End Sub
 

Событие Текущая запись (Current) вызывается каждый раз, когда мы переходим к другой записи, и позволяет обновлять внешний вид рисунка на основании данных из БД. Рассмотрим этот процесс подробнее. В форме frmT_View1 имеется поле strNameFile, содержащее имя файла из таблицы tbdT и соответствующее просматриваемому рисунку. В нашем примере оно показывает, какая именно запись из таблицы tbdT в данный момент отображается (разумеется, в конкретной реализации можно было бы использовать какое-нибудь другое поле - и совсем необязательно текстового типа). Получив значение этого поля, проверяем, не имеет ли оно значение Null. Затем проверяем, имеется ли на жестком диске каталог TempPath, используемый для временного хранения файла фотографии. Если каталога с заданным именем на диске нет - создаем его. Находим в таблице запись, имеющую в поле strNameFile ту же информацию, что и в поле strNameFile формы. Выгружаем содержимое поля oleFile найденной записи на диск в виде файла. Закрываем файл. Присваиваем свойству Картинка (Picture) поля imgFile путь к созданному нами файлу. Сразу после этого можно удалить этот файл, поскольку хранившаяся в нем информация теперь хранится (и отображается) элементом Рисунок с именем imgFile. Запустив форму, можно наблюдать, как меняются картинки при переходе от записи к записи.

 

Для иллюстрации работы этого механизма в отчетах создадим отчет Report1. Для этого сохраним форму frmT_View1 в виде отчета с именем Report1. Поскольку аналогом события Текущая запись (Current) формы в отчете является событие Форматирование (Format) раздела ОбластьДанных, для того чтобы обеспечить отображение фотографий, необходимо поместить в процедуру обработки тот же код, который при сохранении был помещен в модуль отчета - но в процедуру Form_Current. Для этого в модуле отчета выбираем раздел ОбластьДанных, событие Форматирование (Format). После формирования заголовка процедуры обработки этого события необходимо перенести в нее код из процедуры Form_Current, а ее заголовок удалить. Отчет готов к использованию.

 

Таким образом, полученные форма и отчет с точки зрения пользователя выполняют практически те же функции по отображению фотографий, что и при стандартном методе хранения графической информации. Единственным неприятным моментом является периодически появляющееся окошко импорта картинки при отображении новой фотографии в форме. Чтобы избавиться от этого побочного эффекта, следует использовать другой объект отображения и иной механизм отображения файлов.

 

Второй вариант

 

В качестве хранилища используется таблица tbdT, созданная при рассмотрении типового метода хранения. Для импорта используется форма frmT_Import, созданная при рассмотрении первого варианта альтернативного метода хранения.

 

Функции по отображению графических файлов элемента Рисунок, примененного при рассмотрении первого варианта альтернативного метода хранения, возлагаются на элемент ActivX Microsoft Forms 2.0 Image с именем picFile, так как он обладает свойством Picture. Это свойство представляет собой растр и обладает возможностью загрузки в него графических файлов встроенной функцией VBA (и VB) LoadPicture. Данная функция обладает возможностью непосредственной интерпретации графических файлов не только по их расширению, но и по формату содержимого графического файла.

 

Проделаем следующее: сохраним как экспорт в текущей БД форму frmT, но уже под именем frmT_View2. Удалим в ней поле oleFile и вместо него вставим в форму элемент ActivX Microsoft Forms 2.0 Image с именем picFile. Теперь, для того чтобы отображать содержимое, нам необходимо добавить в событие Текущая запись (Current) формы frmT_View2 следующий код:

 

Option Compare Database
Option Explicit

Private Sub Form_Current ()
Dim strPath As String
If IsNull (Me!strNameFile.Value) Then Exit Sub
strPath = Img_To_File (Me!strNameFile.Value)
Set Me.picFile. Picture = LoadPicture (strPath)
Kill strPath
End Sub
 

Как видно из приведенного текста, код стал меньше, потому что общая для формы и отчета часть кода оформлена в виде функции Img_To_File и перенесена в специально созданный модуль Img_Util. Отчет Report2, иллюстрирующий работу этого механизма в отчетах, создается аналогично отчету Report1, но за основу берется форма frmT_View2. Модуль Img_Util содержит следующий код:

 

Option Compare Database
Option Explicit
Const TempPath As String = "c:\temp_mdb"

Public Function Img_To_File (ByVal strFileName As String) As String
Dim recR As Recordset, intNF As Integer, bytB () As Byte
Dim strPath As String
strPath = Dir$(TempPath, vbDirectory)
If strPath = "" Then MkDir TempPath
Set recR = CurrentDb.OpenRecordset ("select * from tbdT
where strNameFile=""" + _strFileName + """", dbOpenDynaset)
If Not recR.EOF Then
strPath = TempPath + "\temp.dat"
If Dir$(strPath, vbNormal) <> "" Then Kill strPath
intNF = FreeFile
Open strPath For Binary Access Write As #intNF
bytB = recR!oleFile.Value
Put #intNF,, bytB
Close #intNF
Else
strPath = ""
End If
recR.Close
Img_To_File = strPath
End Function
 

Эта процедура аналогична рассмотренной в первом варианте и выполняет те же функции, но немного по-другому. В первом варианте мы каждый раз создавали файл с новым именем, и обязательным было наличие расширения файла графики (в нашем случае JPG). Во втором варианте временный файл создается с одним и тем же именем и его расширение не соответствует расширению графических файлов (используется расширение DAT). Подобная особенность может быть полезна при защите информации.

 

Продолжение возможно

 

Метод хранения фотоматериалов в виде связанных объектов OLE - хотя он и рекомендуется в справочной системе Access - нельзя рассматривать в качестве альтернативы изложенному в статье методу по следующим причинам: при переносе БД как целого с одного носителя на другой потребуется ее корректировка. Не стоит даже пояснять насколько это "легко" при условии расположения БД на компакт-диске. Кроме того, файлы, хранящиеся вне БД, нельзя обеспечить даже элементарной защитой от несанкционированного доступа. И если для рассмотренного в статье примера защита от несанкционированного доступа, возможно, неактуальна, то при создании приложения для отдела кадров, где данными являются графические образы документов и фотографии персонала, это не так.

 

Рассмотренный альтернативный метод хранения графических файлов работоспособен не только в Access 97 и 2000, но и в программах-надстройках, написанных на VB и VC++ (второй вариант). В целом альтернативный метод открывает дополнительные возможности по защите, но эта тема выходит за рамки данной статьи.

2004.05.20
19.03.2009
В IV квартале 2008 г. украинский рынок серверов по сравнению с аналогичным периодом прошлого года сократился в денежном выражении на 34% – до $30 млн (в ценах для конечных пользователей), а за весь календарный год – более чем на 5%, до 132 млн долл.


12.03.2009
4 марта в Киеве компания Telco провела конференцию "Инновационные телекоммуникации", посвященную новым эффективным телекоммуникационным технологиям для решения задач современного бизнеса.


05.03.2009
25 февраля в Киеве компания IBM, при информационной поддержке "1С" и Canonical, провела конференцию "Как сохранить деньги в условиях кризиса?"


26.02.2009
18-19 февраля в Киеве прошел юбилейный съезд ИТ-директоров Украины. Участниками данного мероприятия стали ИТ-директора, ИТ-менеджеры, поставщики ИТ-решений из Киева, Николаева, Днепропетровска, Чернигова и других городов Украины...


19.02.2009
10 февраля в Киеве состоялась пресс-конференция, посвященная итогам деятельности компании "DiaWest – Комп’ютерний світ" в 2008 году.


12.02.2009
С 5 февраля 2009 г. в Киеве начали работу учебные курсы по использованию услуг "электронного предприятия/ учреждения" на базе сети информационно-маркетинговых центров (ИМЦ).


04.02.2009
29 января 2009 года в редакции еженедельника "Computer World/Украина" состоялось награждение победителей акции "Оформи подписку – получи приз!".


29.01.2009
22 января в Киеве компания "МУК" и представительство компании Cisco в Украине провели семинар для партнеров "Обзор продуктов и решений Cisco Small Business"

 

 
 
Copyright © 1997-2008 ИД "Комиздат".