0

サイトに 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);
}
4

1 に答える 1