Недавно пришлось писать DLL, одна из функций которой должна была:
Для начала я создал процедуру, которая будет вставлять файл в базу данных и возвращать идентификатор вставленного файла.
1. сформировать отчет в FastReport;Предположим, что исходная структура таблицы для хранения файла в базе данных была такая (MS SQLServer 2000):
2. экспортировать отчет в файл PDF-формата и сохранить его в базе данных;
3. возвратить идентификатор сохраненного в базе данных файла.
CREATE TABLE X.FILES (где FILE_BODY – это поле, в которое сохраняется файл, а ID – идентификатор файла.
ID BIGINT IDENTITY NOT NULL,
FILE_BODY IMAGE NULL,
CONSTRAINT PK_FILES PRIMARY KEY (ID)),
Для начала я создал процедуру, которая будет вставлять файл в базу данных и возвращать идентификатор вставленного файла.
CREATE PROCEDURE X.InsertFile
@FILE image,
@ID bigint OUTPUT
AS
INSERT INTO X.FILES(FILE_BODY) VALUES(@FILE)
SET @ID = SCOPE_IDENTITY()
GO
Потом сел писать код DLL. В Delphi вся работа с файлами реализована с помощью потоков, поэтому я немного удивился, когда оказалось, что метод Export у TfrxReport экспортирует отчет только в файл (честно сказать, я ожидал увидеть, что-то в стиле ExportToStream). Т.е. вместо прямой передачи отчета через поток в базу данных, необходимо было сначала сохранить отчет во временный файл, а потом этот файл загрузить в базу данных. Мне это не понравилось, т.к. файловые операции (сначала записи, потом чтения) должны были хоть немного, но тормозить работу. Но нужно было срочно отдать DLL, поэтому я не стал разбираться с экспортом и сделал через файл:
...
db: TSDDatabase; // база данных SQLDirect
spInsertFile: TSDStoredProc; // вызов X.InsertFile
frxReport: TfrxReport;
frxPDFExport: TfrxPDFExport;
...
Function GetDoc(const sDotName: String; ...): Integer;
begin
Result := -1;
// загружаем шаблон отчета
If frxReport.LoadFromFile(sDotName)
then begin
// устанавливаем параметры отчета
...
If frxReport.PrepareReport
then try
// получаем имя временного файла
frxPDFExport.FileName := GetTempFileName;
Try
// экспортируем отчет во временный файл
frxReport.Export(frxPDFExport);
// загружаем PDF-файл в параметр процедуры
// из временного файла
spInsertFile.Params[1].LoadFromFile(frxPDFExport.FileName, ftBlob);
Except
on E: Exception do
WriteErrorMessage(E.Message);
End;
// сохраняем PDF-файл в базу данных
Try
db.StartTransaction;
spInsertFile.ExecProc;
db.Commit;
Result := spInsertFile.Params[2].AsInteger;
Except
on E: ESDEngineError do
begin
db.Rollback;
WriteErrorMessage(E.Message);
end;
End;
Finally
// удаляем временный файл
DeleteFile(frxPDFExport.FileName)
End
else WriteErrorMessage('Ошибка подготовки отчета')
end
else WriteErrorMessage('Файл шаблона не найден')
end;
После передачи DLL другому программисту, мысли о лишних файловых операциях при сохранении отчета в базу через временный файл, не давала мне покоя. Вечером, не найдя решения в документации по FastReport, я, прежде чем смотреть исходный код экспорта, решил еще раз пройтись по методам и свойствам экспорта. Глаз сразу же зацепился за свойство Stream. Я создал для этого свойства поток и метод Export у TfrxReport выгрузил отчет не в файл, а в поток.
Function GetDoc(const sDotName: String; ...): Integer;
begin
Result := -1;
// загружаем шаблон отчета
If frxReport.LoadFromFile(sDotName)
then begin
// устанавливаем параметры отчета
...
If frxReport.PrepareReport
then begin
Try
Try
// создаём поток в памяти
frxPDFExport.Stream := TMemoryStream.Create;
// экспортируем отчет в поток
frxReport.Export(frxPDFExport);
// загружаем PDF-файл в параметр процедуры
// из потока в памяти
spInsertFile.Params[1].LoadFromStream(frxPDFExport.Stream, ftBlob);
Except
on E: Exception do
WriteErrorMessage(E.Message);
End;
Finally
frxPDFExport.Stream.Free;
End;
// сохраняем PDF-файл в базу данных
Try
db.StartTransaction;
spInsertFile.ExecProc;
db.Commit;
Result := spInsertFile.Params[2].AsInteger;
Except
on E: ESDEngineError do
begin
db.Rollback;
WriteErrorMessage(E.Message);
end;
End;
end
else WriteErrorMessage('Ошибка подготовки отчета')
end
else WriteErrorMessage('Файл шаблона не найден')
end;
Часто решение задачи лежит на поверхности и нет нужды копаться в чужом коде. Нужно лишь быть внимательным и никогда не сдавайтесь – "ищите и обрящите".
В FR так сделано специально. Дело в том, что некоторые форматы экспорта позволяют создавать несколько файлов при экспорте одного шаблона (в основном это картинки - по файлу на печатную страницу отчёта).
ОтветитьУдалитьЯ же, в своей практике, позволяю конечному пользователю настроить формат экспорта, а после экспорта все временные файлы архивируются и отсылаются на e-mail...
Документацию по FastReport можно не смотреть - там мало, что есть. Могли бы для хорошего генератора отчетов и хорошую документацию сделать ;)
ОтветитьУдалитьСпасибо за полезную информацию! 2 дня уже потратил разбираясь с экспортом в Stream
ОтветитьУдалить