サイトに Coldfusion 10 と CFWheels を使用しています。基本的に、私のサイトには、独自のコントローラーとビューを備えたさまざまな種類のフォームがたくさんあります。フォームごとに、ユーザーはフォームの PDF を動的に生成してダウンロードするオプションがあります。基本的にコントローラーのデータをロードしますが、「pdf」のパラメーターでビューにヒットすると、次の処理が行われ、PDF が生成され、ブラウザーでドキュメントが開きます。
<cfdocument format="PDF" saveAsName="#formtype#_#id#.pdf">
#includePartial("/printView")#
</cfdocument>
これらの各 PDF は、追加される項目の数に応じて、複数のページを持つことができます。最初に述べたように、フォームには複数の種類があるため、独自のコントローラーとビュー、および印刷ビューを使用した PDF 生成があります。これらのフォームはすべてカスタマイズされ、shippingID などの ID と関連付けられています。したがって、タイプ A の 2 つのフォームとタイプ B の 1 つのフォームとタイプ C の 3 つのフォームを含む 1 つの出荷を行うことができます。私がする必要があるのは、出荷に基づいてすべてのフォームをマージして 1 つの PDF を生成することです。したがって、私の例では、貨物のマージされた PDF には、タイプ A の 2 つのフォーム、タイプ B の 1 つのフォーム、およびタイプ C の 3 つのフォームがすべてマージされて含まれます。
現在私がやっていることは、動的に生成された PDF ページのそれぞれに対して http "GET" 呼び出しを行い、それを一時ディレクトリに保存して、最後にそれらをマージすることです。
出荷をロードし、異なるタイプのフォームごとに次のことを行います。ここで、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 をオンザフライでそのまま生成するためです。(関連付けられているすべてのフォーム) を読み込んですべてを 1 つのファイルに入れようとすると、同じコントローラー ロジックをすべて追加して、各フォーム固有のものと関連付けられたビューを読み込む必要がありますが、これらは個々のページ ビューに対して既に存在します。
これを行うより良い方法はありますか?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#");
}
コントローラー/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#");
}
現在、私のビューは、値が「print」または「pdf」のいずれかである「view」という URL パラメーターに基づいています。
print - Web ページのヘッダー/フッターなどを除いた、フォームのほとんどを取り除いたバージョンの印刷ビューを表示します。
pdf - printView を使用して PDF を生成する質問の上部に貼り付けた cfdocument コードを呼び出します。
「show.cfm」コードを投稿する必要はないと思います。これは、問題の特定のフォームごとに特定の情報を表示する一連の div とテーブルにすぎないためです。
これらは 3 つのフォーム タイプの例にすぎず、1 つの出荷に関連付けられる可能性のあるフォーム タイプが 10 以上あり、PDF をマージする必要があることに注意してください。各タイプは、出荷内で数回繰り返される場合もあります。たとえば、出荷には、5 つの許可と 3 つの NAFTA を持つ 10 の異なる請求書が含まれる場合があります。
やや複雑なことに、貨物には米国向けまたはカナダ向けの 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);
}