Я использую Coldfusion 10 и CFWheels для своего сайта. В основном на моем сайте есть куча разных типов форм со своими собственными контроллерами и представлениями. Для каждой формы у пользователя есть возможность динамически создать PDF-файл формы и загрузить его. В основном он загружает данные контроллера, но когда он попадает в представление с параметром «pdf», он делает следующее, что создает PDF и открывает документ в браузере:
<cfdocument format="PDF" saveAsName="#formtype#_#id#.pdf">
#includePartial("/printView")#
</cfdocument>
Каждый из этих PDF-файлов может иметь несколько страниц в зависимости от того, сколько позиций добавлено. Как я сказал в начале, существует несколько типов форм, поэтому у них будет свой собственный контроллер и представления, а также генерация PDF с их представлениями для печати. Все эти формы настроены и связаны вместе с идентификатором, например, shippingID. Таким образом, у меня может быть одна поставка, содержащая 2 формы типа A и 1 форму типа B, 3 формы типа C и т. д. Что мне нужно сделать, так это создать 1 PDF-файл со всеми формами, объединенными вместе, на основе поставки. Итак, в моем примере объединенный PDF-файл для отправки будет содержать 2 формы типа A, 1 форму типа B и 3 формы типа C, все они объединены.
В настоящее время я делаю HTTP-вызов «GET» для каждой из динамически сгенерированных страниц PDF, сохраняю их во временном каталоге, а затем объединяю их в конце.
Я загружаю посылку и для каждого типа формы делаю следующее, где urlPath — это путь к представлению, которое создает динамический PDF:
var httpService = new http();
httpService.setMethod("GET");
httpService.setUrl(urlPath);
invoice = httpService.send().getPrefix().filecontent.toByteArray();
var fullPath = "#filePath##arguments.type#_#id#.pdf";
//write files in temp directory
FileWrite(fullPath, invoice);
После того, как я получу PDF-файл и запишу его в файл, я сохраняю путь в массиве для справки, чтобы я мог просмотреть и объединить все файлы, на которые есть ссылки в массиве, а затем удалить временный каталог, в котором были сохранены файлы.
Причина, по которой я делаю это таким образом, заключается в том, что контроллеры и представления уже установлены и генерируют отдельные PDF-файлы на лету, как есть. Если я попытаюсь загрузить (все связанные формы) и поместить все в один файл, мне придется добавить все ту же логику контроллера для загрузки каждого конкретного материала формы и связанных представлений, но они уже существуют для отдельного представления страницы.
Есть лучший способ сделать это? Он отлично работает, если есть только несколько PDF-файлов, но если в посылке много разных форм, например 20, это очень медленно, и, поскольку у нас нет CF Enterprise, я считаю, что cfdocument является однопоточным. Формы должны генерироваться динамически, чтобы они содержали самые последние данные.
ОБНОВЛЕНИЕ для Криса
Я добавил немного кода, чтобы показать, как могут выглядеть различные формы. Я проверяю и загружаю кучу других вещей, но я урезал их, чтобы получить общее представление:
controllers/Invoices.cfc
Путь может быть примерно таким: /shipments/[shipmentkey]/invoices/[key]
public void function show(){
// load shipment to display header details on form
shipment = model("Shipment").findOne(where="id = #params.shipmentkey#");
// load invoice details to display on form
invoice = model("Invoice").findOne(where="id = #params.key#");
// load associated invoice line items to display on form
invoiceLines = model("InvoiceLine").findAll(where="invoiceId = #params.key#");
// load associated containers to display on form
containers = model("Container").findAll(where="invoiceid = #params.key#");
// load associated snumbers to display on form
scnumbers = model("Scnumber").findAll(where="invoiceid = #params.key#");
}
controllers/Permits.cfc
Путь может выглядеть примерно так: /shipments/[shipmentkey]/permits/[key]
public void function show(){
// load shipment to display header details on form
shipment = model("Shipment").findOne(where="id = #params.shipmentkey#");
// load permit details to display on form
permit = model("Permit").findOne(where="id = #params.key#");
// load associated permit line items to display on form
permitLines = model("PermitLine").findAll(where="permitId = #params.key#");
}
контроллеры/Nafta.cfc
Путь может быть примерно таким: /shipments/[shipmentkey]/naftas/[key]
public void function show(){
// load shipment to display header details on form
shipment = model("Shipment").findOne(where="id = #params.shipmentkey#");
// load NAFTA details to display on form
nafta = model("NAFTA").findOne(where="id = #params.key#");
// load associated NAFTA line items to display on form
naftaLines = model("NaftaLine").findAll(where="naftaId = #params.key#");
}
В настоящее время мое представление основано на параметре URL, называемом «просмотр», где значения могут быть «печать» или «pdf».
print — отображает представление для печати, которое представляет собой урезанную версию формы без верхних/нижних колонтитулов веб-страницы и т. д.
pdf - вызывает код cfdocument, который я вставил в начало вопроса, который использует printView для создания PDF.
Я не думаю, что мне нужно публиковать код «show.cfm», так как это будет просто набор разделов и таблиц, отображающих конкретную информацию для каждой конкретной рассматриваемой формы.
Имейте в виду, что это только 3 примера типов форм, и существует более 10 типов, которые могут быть связаны с 1 отправкой, и PDF-файлы необходимо будет объединить. Каждый тип может повторяться несколько раз в пределах одной отправки. Например, отгрузка может содержать 10 различных счетов-фактур с 5 разрешениями и 3 НАФТА.
Чтобы немного усложнить ситуацию, посылка может иметь 2 типа: для отправки в США или для отправки в Канаду, и в зависимости от этого к отправке могут быть привязаны различные типы форм. Таким образом, в счете-фактуре для Канады будут совершенно другие поля, чем в счете-фактуре для США, поэтому модели/таблицы будут другими.
В настоящее время для слияния у меня есть контроллер, который делает что-то вроде следующего (обратите внимание, что я удалил много проверок, загрузив другие объекты для упрощения)
public any function displayAllShipmentPdf(shipmentId){
// variable to hold the list of full paths of individual form PDFs
formList = "";
shipment = model("shipment").findOne(where="id = #arguments.shipmentId#");
// path to temporarily store individual form PDFs for later merging
filePath = "#getTempDirectory()##shipment.clientId#/";
if(shipment.bound eq 'CA'){
// load all invoices associated to shipment
invoices = model("Invoice").findAll(where="shipmentId = #shipment.id#");
// go through all associated invoices
for(invoice in invoices){
httpService = new http();
httpService.setMethod("get");
// the following URL loads the invoice details in the Invoice controller and since I'm passing in "view=pdf" the view will display the PDF inline in the browser.
httpService.setUrl("http://mysite/shipments/#shipment.id#/invoices/#invoice.id#?view=pdf");
invoicePdf = httpService.send().getPrefix().fileContent.toByteArray();
fullPath = "#filePath#invoice_#invoice.id#.pdf";
// write the file so we can merge later
FileWrite(fullPath, invoicePdf);
// append the fullPath to the formList as reference for later merging
formList = ListAppend(formList, fullPath);
}
// the above code would be similarly repeated for every other form type (ex. Permits, NAFTA, etc.). So it would call the path with the "view=pdf" which will load the specific form Controller and display the PDF inline which we capture and create a temporary PDF file and add the path to the formList for later merging. You can see how this can be a long process as you have several types of forms associated to a shipment and there can be numerous forms of each type in the shipment and I don't want to have to repeat each form Controller data loading logic.
}else if(shipment.bound eq 'US'){
// does similar stuff to the CA except with different forms
}
// merge the PDFs in the formList
pdfService = new pdf();
// formList contains all the paths to the different form PDFs to be merged
pdfService.setSource(formList);
pdfService.merge(destination="#filePath#shipment_#shipment.id#.pdf");
// read the merged PDF
readPdfService = new pdf();
mergedPdf = readPdfService.read(source="#filePath#shipment_#shipment.id#.pdf");
// delete the temporarily created PDF files and directory
DirectoryDelete(filePath, "true");
// convert to binary to display inline in browser
shipmentPdf = toBinary(mergedPdf);
// set the response to display the merged PDF
response = getPageContext().getFusionContext().getResponse();
response.setContentType('application/pdf');
response.setHeader("Content-Disposition","filename=shipment_#shipment.id#_#dateFormat(now(),'yyyymmdd')#T#timeFormat(now(),'hhmmss')#.pdf");
response.getOutputStream().writeThrough(shipmentPdf);
}
provides
и настраиваете свои маршруты для приема.[format]
, вам не нужно писать собственную логику, чтобы решить, отображать ли PDF в представлении. Посмотрите этот скринкаст: vimeo.com/channels/cfwheels/17933706 - person Chris Peters   schedule 03.09.2015