WAV-файл – это звуковой файл, как правило, большого размера. Наша задача – разделить этот файл на несколько частей меньшего размера. На этом примере мы наглядно покажем, как и зачем используются основные объекты для работы с двоичными данными.
Любой WAV-файл состоит из двух областей. Одна из них – заголовок файла, другая – область данных. В заголовке файла хранится информация о размере файла, количестве каналов, частоте дискретизации и др.
Чтобы один файл разделить на несколько частей, нужно разделить область данных на требуемое количество частей нужного размера, к каждой части дописать заголовок – такой же, как был у всего файла, но с другим размером данных – и затем каждую такую часть записать в отдельный WAV-файл.
Для решения этой задачи добавим команду РазделитьФайлНаФрагменты. Обработчик команды заполним следующим образом (листинг 6.104).
Листинг 6.104. Обработчик команды «РазделитьФайлНаФрагменты»
&НаКлиенте
Процедура РазделитьФайлНаФрагменты(Команда)
ОповещениеОЗавершении = Новый ОписаниеОповещения("РазделитьФайлНаСервереЗавершение", ЭтотОбъект);
НачатьПомещениеФайлаНаСервер(ОповещениеОЗавершении, , , "", , УникальныйИдентификатор);
КонецПроцедуры
В обработчике команды с помощью метода глобального контекста НачатьПомещениеФайлаНаСервер() мы начинаем помещение звукового файла из локальной файловой системы во временное хранилище. При этом пользователю предлагается диалог для выбора помещаемого файла.
В метод НачатьПомещениеФайлаНаСервер() первым параметром мы передаем описание оповещения, указывающее на экспортную процедуру РазделитьФайлНаСервереЗавершение(), которая будет выполнена, после того как выбранный файл будет помещен во временное хранилище (листинг 6.105).
Листинг 6.105. Процедура «РазделитьФайлНаСервереЗавершение()»
&НаКлиенте
Процедура РазделитьФайлНаСервереЗавершение(ОписаниеПомещенногоФайла, Дополнительно) Экспорт
Если ОписаниеПомещенногоФайла = Неопределено Тогда
Возврат;
КонецЕсли;
ОписаниеФайловФрагментов = РазделитьФайлНаФрагментыНаСервере(ОписаниеПомещенногоФайла.Адрес);
ФайлыФрагментов = Новый Массив;
Для Каждого ФайлФрагмента Из ОписаниеФайловФрагментов Цикл
ФайлыФрагментов.Добавить(Новый ОписаниеПередаваемогоФайла(ФайлФрагмента.ИмяФайла, ФайлФрагмента.АдресФайла));
КонецЦикла;
ПараметрыДиалога = Новый ПараметрыДиалогаПолученияФайлов("Выберите каталог для сохранения файлов фрагментов", Истина);
НачатьПолучениеФайловССервера(ФайлыФрагментов, ПараметрыДиалога);
КонецПроцедуры
После того как файл будет помещен, адрес данных в хранилище (ОписаниеПомещенногоФайла.Адрес) мы передаем в серверную функцию РазделитьФайлНаФрагментыНаСервере(), в которой данные файла получаются из временного хранилища и производится нужная обработка и разбиение на части звукового файла (листинг 6.106).
Листинг 6.106. Функция «РазделитьФайлНаФрагментыНаСервере()»
&НаСервереБезКонтекста
Функция РазделитьФайлНаФрагментыНаСервере(АдресФайла)
Сообщение = Новый СообщениеПользователю();
РазделенныеФайлы = Новый Массив;
ДанныеФайла = ПолучитьИзВременногоХранилища(АдресФайла);
ЧтениеДанных = Новый ЧтениеДанных(ДанныеФайла);
БуферЗаголовокИсходный = ЧтениеДанных.ПрочитатьВБуферДвоичныхДанных(44);
// Декодировать заголовок файла.
РазмерДанных = БуферЗаголовокИсходный.ПрочитатьЦелое32(4); // chunkSize
КоличествоКаналов = БуферЗаголовокИсходный.ПрочитатьЦелое16(22); // numChannels
ЧастотаДискретизации = БуферЗаголовокИсходный.ПрочитатьЦелое32(24); // sampleRate
БайтовВСекунду = БуферЗаголовокИсходный.ПрочитатьЦелое32(28); // byteRate
Сообщение.Текст = "РазмерДанных: " + РазмерДанных;
Сообщение.Сообщить();
Сообщение.Текст = "КоличествоКаналов: " + КоличествоКаналов;
Сообщение.Сообщить();
Сообщение.Текст = "ЧастотаДискретизации: " + ЧастотаДискретизации;
Сообщение.Сообщить();
Сообщение.Текст = "БайтовВСекунду: " + БайтовВСекунду;
Сообщение.Сообщить();
// Получить массив фрагментов после разделения на части - массив значений типа РезультатЧтенияДанных.
МассивФрагменты = ЧтениеДанных.РазделитьНаЧастиПо(1000);
НомерФрагмента = 0;
Для Каждого РезультатЧтенияФрагмент из МассивФрагменты Цикл
БуферЗаголовокФрагмента = БуферЗаголовокИсходный.Скопировать();
РазмерФрагмента = РезультатЧтенияФрагмент.Размер + 40;
БуферЗаголовокФрагмента.ЗаписатьЦелое32(4, РазмерФрагмента);
НомерФрагмента = НомерФрагмента + 1;
ИмяФайлаФрагмента = Строка(НомерФрагмента) + ".wav";
Поток = Новый ПотокВПамяти();
ЗаписьДанных = Новый ЗаписьДанных(Поток);
ЗаписьДанных.ЗаписатьБуферДвоичныхДанных(БуферЗаголовокФрагмента);
ЗаписьДанных.Записать(РезультатЧтенияФрагмент);
//АдресФайлаФрагмента = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ИмяФайла));
АдресФайлаФрагмента = ПоместитьВоВременноеХранилище(Поток.ЗакрытьИПолучитьДвоичныеДанные());
ДанныеФайлаФрагмента = Новый Структура("АдресФайла, ИмяФайла",);
ДанныеФайлаФрагмента.АдресФайла = АдресФайлаФрагмента;
ДанныеФайлаФрагмента.ИмяФайла = ИмяФайлаФрагмента;
РазделенныеФайлы.Добавить(ДанныеФайлаФрагмента);
КонецЦикла;
Возврат РазделенныеФайлы;
КонецФункции
В этой функции мы сначала получаем двоичные данные по адресу во временном хранилище, переданном в функцию в параметре АдресФайла. И на основе этих данных создаем объект ЧтениеДанных. Затем, чтобы иметь возможность разобрать заголовок файла, методом этого объекта ПрочитатьВБуферДвоичныхДанных() читаем первые 44 байта в буфер двоичных данных.
Из получившегося буфера исходного заголовка (БуферЗаголовокИсходный) мы читаем параметры заголовка (размер данных, количество каналов и др.) с указанием конкретной позиции, с которой должно начинаться чтение.
Например, чтобы получить размер данных из буфера исходного заголовка, мы читаем из него 32-битное целое положительное число, начиная с четвертого байта – БуферЗаголовокИсходный.ПрочитатьЦелое32(4).
На самом деле разбор заголовка файла нужен для того, чтобы вычислить, на фрагменты какого размера делить файл. Но, чтобы не усложнять пример вычислениями, мы разбираем заголовок и показываем его просто для информации (рис. 6.21), а область данных делим на одинаковые части по 1000 байт методом РазделитьНаЧастиПо() объекта ЧтениеДанных.
Рис. 6.21. Параметры заголовка звукового файла
Затем в цикле обхода массива значений типа РезультатЧтенияДанных, получившихся после деления области данных на части, мы копируем исходный заголовок методом Скопировать() объекта БуферДвоичныхДанных и записываем в него новый размер фрагмента – БуферЗаголовокФрагмента.ЗаписатьЦелое32(4, РазмерФрагмента).
После этого создаем поток в памяти и объект ЗаписьДанных на основе этого потока. С помощью методов ЗаписатьБуферДвоичныхДанных() и Записать() этого объекта мы записываем в поток сначала заголовок фрагмента, затем часть данных. В результате получаются данные одного фрагмента файла, на который мы разделили исходный звуковой файл.
Методом ЗакрытьИПолучитьДвоичныеДанные() мы закрываем поток и получаем двоичные данные, которые сохраняем во временном хранилище в качестве адреса фрагмента файла.
В заключение на каждом шаге цикла мы создаем структуру ДанныеФайлаФрагмента с полями ИмяФайла и АдресФайла и заполняем их соответственно именем (порядковым номером фрагмента) и адресом во временном хранилище данных фрагмента файла. И затем добавляем эту структуру в массив РазделенныеФайлы, который и возвращает функция после завершения обхода всех фрагментов файла в процедуру РазделитьФайлНаСервереЗавершение(), приведенную выше (см. листинг 6.105).
После этого в этой процедуре мы обходим элементы массива ОписаниеФайловФрагментов и на каждом шаге цикла создаем объект ОписаниеПередаваемогоФайла на основе значений полей структуры, описывающей файлы фрагментов. Таким образом мы создаем массив ФайлыФрагментов, содержащий описания файлов, которые мы будем получать на клиенте по адресу ссылки во временном хранилище и сохранять в локальную файловую систему с именем, заданным в описании файла.
Затем мы создаем объект ПараметрыДиалогаПолученияФайлов и задаем заголовок и признак отображения диалога.
И передаем массив получаемых файлов и параметры диалога получения файлов в метод глобального контекста НачатьПолучениеФайловССервера(), с помощью которого начинается получение файлов-фрагментов и сохранение их в локальную файловую систему. При этом пользователю показывается диалог выбора каталога для сохранения файлов.
В результате в выбранном пользователем каталоге появятся звуковые файлы-фрагменты, которые можно запустить на клиенте (рис. 6.22).
Рис. 6.22. Полученные фрагменты звукового файла