xhchc

          危波帆墻,笑談只在桃花上;與誰共尚,風(fēng)吹萬里浪; 相依相偎,不做黃泉想;莫惆悵,碧波潮生,一蕭自狂放……

           

          教您怎樣在Web應(yīng)用中動態(tài)創(chuàng)建PDF文件

          在一個最近的后勤項目中,客戶要求我們建一個能讓用戶能從一個遺留系統(tǒng)查詢出貨信息的Web站點,有三個主要的需求:

          1.出貨信息必須以PDF文檔的格式返回;

          2.PDF文件必須能通過瀏覽器下載;

          3.PDF文件必須能用Adobe Acrobat Reader閱讀;

          盡管我們的團隊有很多J2EE Web應(yīng)用的開發(fā)經(jīng)驗,但在PDF文檔處理上卻沒有多少經(jīng)驗。我們需要找一個能在服務(wù)器端Web應(yīng)用里產(chǎn)生復(fù)雜的PDF文檔的純Java類庫。最后,我們發(fā)現(xiàn)iText(http://www.lowagie.com/iText/)能完全滿足我們的需要。

          1.iText類庫

          iText是一個創(chuàng)建和處理PDF文檔的開源純Java類庫。Bruno Lowagie和Paulo Soares領(lǐng)導(dǎo)著這個項目。IText API能讓Java開發(fā)人員以編程的方式創(chuàng)建PDF文檔。iText提供了很多的特性:

          支持PDF和FDF文檔
          各種頁面尺寸
          橫向和豎向布局
          頁邊距
          表格
          斷字
          頁頭
          頁腳
          頁碼
          條形碼
          字體
          顏色
          文檔加密
          JPEG,GIF,PNG和WMF圖片
          有序和無序列表
          陰影
          水印
          文檔模板

          軟件開發(fā)網(wǎng) www.mscto.com



          iText是一個開源庫。在寫本文的時候,iText可以在兩個許可協(xié)議下使用:Mozilla Public License和LGPL。如果想了解詳細信息,請參考iText站點。在本文中,你將會看到iText API的應(yīng)用。我們將闡述如何在服務(wù)器端應(yīng)用中使用iText和servlet動態(tài)生成PDF文檔。

          2、開始(Getting Started)

          首先,你需要一個iText Jar文件。訪問iText站點并下載最新的版本。在寫本文時,最新的版本是使0.99。iText站點提供了API文檔和一個全面的指南。

          除了iText,我們還要用servlet.如果你不熟悉servlet,你可以通過Jason Hunter的書《Java Servlet Programming》來學(xué)習(xí)它。你需要一個J2EE應(yīng)用服務(wù)器或可以獨立運行的servlet引擎。開源軟件Tomcat,Jetty和Jboss是不錯的選擇。下文假設(shè)你使用的是Jakarta Tomcat 4.1。

          1.iText API

          iText API簡單易用。通過使用iText,你能創(chuàng)建自定義的PDF文檔。iText庫由下邊的一些包組成:

          com.lowagie.servlets
          com.lowagie.text
          com.lowagie.text.html
          com.lowagie.text.markup
          com.lowagie.text.pdf
          com.lowagie.text.pdf.codec
          com.lowagie.text.pdf.hyphenation
          com.lowagie.text.pdf.wmf
          com.lowagie.text.rtf
          com.lowagie.text.xml
          com.lowagie.tools  

          為了生成PDF文件,你只需要com.lowagie.text和com.lowagie.text.pdf兩個包。 軟件開發(fā)網(wǎng) www.mscto.com

          我們的例子使用了這些iText類:

           

          com.lowagie.text.pdf.PdfWriter
          com.lowagie.text.Document
          com.lowagie.text.HeaderFooter
          com.lowagie.text.Paragraph
          com.lowagie.text.Phrase
          com.lowagie.text.Table
          com.lowagie.text.Cell
          

          軟件開發(fā)網(wǎng) www.mscto.com

          關(guān)鍵的類是Document和PdfWriter。在創(chuàng)建PDF文檔時,你將經(jīng)常使用這兩個類。Document是PDF文檔基于對象的描述。你可以通過調(diào)用Document類提供的方法往文檔中加入內(nèi)容。PdfWriter對象通過java.io.OutputStream對象與Document關(guān)聯(lián)在一起。

           

          3、在Web應(yīng)用中使用iText

          在設(shè)計階段,你必須決定如何使用iText。我們使用了下邊的技術(shù)開發(fā)了我們的Web應(yīng)用。

          1.A技術(shù)

          在服務(wù)器文件系統(tǒng)上創(chuàng)建PDF文件。應(yīng)用使用java.io.FileOutputStream把文件寫到服務(wù)器文件系統(tǒng)上。用戶通過HTTP GET方法下載該文件。

          2.B技術(shù)

          使用java.io.ByteArrayOutputStream在內(nèi)存中創(chuàng)建PDF文件。應(yīng)用通過servlet的輸出流將該PDF文件字節(jié)發(fā)送到客戶端。

          由于應(yīng)用不需要把文件寫到文件系統(tǒng)上,這樣能保證在集群服務(wù)環(huán)境中能正常工作,所以我更傾向于使用B技術(shù)。如果你的應(yīng)用運行在集群環(huán)境中且服務(wù)器集群不提供會話親和的功能,A技術(shù)可能會導(dǎo)致失敗。

          3、例子:PDFServlet

          我們的例子應(yīng)用由一個類組成:PDFServlet。這個servlet采用B技術(shù)。輸出流OutputStream是java.io.ByteArryOutputStream。用ByteArrayOutputStream,PDF文檔字節(jié)將存儲在內(nèi)存中。

          當(dāng)PDFServlet接收到一個HTTP請求時,它將動態(tài)地生成一個PDF文檔并將該文檔發(fā)送到客戶端。PDFServlet類擴展了javax.servlet.http.HttpServlet類并導(dǎo)入了兩個iText包:com.lowagie.text和com.lowagie.text.pdf。



          doGet方法

          大多數(shù)servlet覆蓋了doPost和doGet方法中的一個方法。我們的servlet沒有什么不同。PDFServlet類覆蓋了doGet方法。該servlet將在接收到HTTP GET請求后生成一個PDF文件。

          在核心部分,servlet的doGet方法做了如下的工作:

          1.創(chuàng)建一個包含PDF文檔字節(jié)的ByteArrayOutputStream對象;

          2.在reponse對象上設(shè)置HTTP響應(yīng)頭內(nèi)容;

          3.得到servlet輸出流;

          4.把文檔字節(jié)寫到servlet的輸出流中;

          5.刷新servlet輸出流;

          generatePDFDocumentBytes方法

          generatePDFDocumentBytes方法負責(zé)創(chuàng)建PDF文檔。在這個方法中三個最重要的對象是Document對象,ByteArrayOutputStream對象和PdfWriter對象。PdfWriter使用ByteArrayOutputStream關(guān)聯(lián)Document。

          軟件開發(fā)網(wǎng) www.mscto.com

           

          Document doc = new Document();
          ByteArrayOutputStream
          baosPDF = new ByteArrayOutputStream();
          PdfWriter docWriter = null;
          docWriter
          = PdfWriter.getInstance(doc, baosPDF);
          // ...
          用add方法把內(nèi)容添加到Document中。
          doc.add(new Paragraph(
          "This document was
          created by a class named: "
          this.getClass().getName()));
          doc.add(new Paragraph(
          "This document was created on "
          new java.util.Date())); 軟件開發(fā)網(wǎng) www.mscto.com 

          當(dāng)你添加完內(nèi)容后,要關(guān)閉Document和PdfWriter對象。

           

          doc.close();
          docWriter.close();  

          當(dāng)關(guān)閉文檔后,ByteArrayOutputStream對象返回到調(diào)用者。

           

           

          return baosPDF;  

          ByteArrayOutputStream包含了PDF文檔的所有字節(jié)。

          HTTP響應(yīng)頭

          在這個應(yīng)用中,我們僅僅關(guān)注四個HTTP 響應(yīng)頭:Content-type,Content-disposition,Content-length,和Cache-control。如果你從沒有使用過HTTP頭,請參考HTTP 1.1規(guī)范。

          研究在PDFServlet中的doGet方法,你會注意到要在任何數(shù)據(jù)寫到servlet輸出流之前設(shè)置HTTP響應(yīng)頭內(nèi)容,這是很重要的,也是細微的一點。讓我們更詳細地說明一下每個響應(yīng)頭的含義。

           

          Content-type

           

          在servlet中,HttpServletResponse有一個表明響應(yīng)所包含內(nèi)容類型的參數(shù)。對PDF文件而言,內(nèi)容類型是application/pdf。如果servlet沒有設(shè)置類型,web瀏覽器很難決定如何處理這個文件。

           

          PDFServlet用下邊的代碼設(shè)置內(nèi)容類型:

           

           

          resp.setContentType("application/pdf");
          

          Content-disposition

           

          Content-disposition頭提供給瀏覽器確定HTTP響應(yīng)內(nèi)容的信息。當(dāng)瀏覽器讀到這些頭信息后,它能確定:

          HTTP響應(yīng)包含一個文件;

           

          包含在響應(yīng)中的文件名;

           

          該文件是顯示在瀏覽器主窗口中還是要用外部的應(yīng)用查看;

          RFC 2183中有對Content-disposition頭完整的解釋。

           

          通過合適地設(shè)置Content-disposition的值,servlet能指示瀏覽器是“內(nèi)嵌”顯示文件還是把它當(dāng)作附件處理。

          軟件開發(fā)網(wǎng) www.mscto.com

           

          例1.內(nèi)嵌顯示一個文件

           

          Content-disposition: inline;
          filename=foobar.pdf  

          例2.往response里附加一個文件

          軟件開發(fā)網(wǎng) www.mscto.com

           

          軟件開發(fā)網(wǎng) www.mscto.com

           

          Content-disposition: attachment;
          filename=foobar.pdf  

          下邊的偽碼說明了如何設(shè)置頭信息:

           

          軟件開發(fā)網(wǎng) www.mscto.com

           

          public void doGet(HttpServletRequest req,
          HttpServletResponse resp)
          {
          // ...
          resp.setHeader(
          "Content-disposition",
          "inline; filename=foobar.pdf" );
          // ...
          }  

          Cache-Control

          根據(jù)你應(yīng)用的特性不同,你可以讓瀏覽器緩存或者不緩存你正在生成的PDF文件。服務(wù)器端應(yīng)用可以有很多種HTTP 頭來控制內(nèi)容緩存。下邊是一些例子:











           

          Cache-Control: no-cache
          Cache-Control: no-store
          Cache-Control: must-revalidate
          Cache-Control: max-age=30
          Pragma: no-cache
          

          Expires: 0

           

          關(guān)于Cache-Control頭的全面解釋見HTTP 1.1規(guī)范。

           

          PDFServlet把Cache-Control設(shè)置為max-age=30。這個頭信息告訴瀏覽器緩存這個文件的最長時間為30秒。

           

          Content-length

           

          Content-length頭必須設(shè)置成PDF文件中字節(jié)的數(shù)值。如果Content-length沒有設(shè)置正確,瀏覽器可能不能正確地顯示該文件。下邊是例子代碼:

          ByteArrayOutputStream
          baos = getByteArrayOutputStream();
          resp.setContentLength(baos.size());  

          把PDF文檔送到Web瀏覽器

          軟件開發(fā)網(wǎng) www.mscto.com

           

          PDFServlet通過把字節(jié)流寫到servlet的輸出流的方式把PDF文檔送到客戶端。它通過調(diào)用HttpServletResponse對象的getOutputStream方法來獲得輸出流。getOutputStream方法返回一個javax.servlet.ServletOutputStream類型的對象。

           

          ServletOutputStream sos;
          sos = resp.getOutputStream();
          baos.writeTo(sos);
          sos.flush();
          

          在把所有的數(shù)據(jù)寫到流之后,調(diào)用flush()方法把所有的字節(jié)發(fā)送到客戶端。

           

          打包和部署

          為了在Tomcat中運行PDFServlet,你需要把應(yīng)用打包在WAR文件中。iText JAR文件(itext-0.99.jar)必須放在WAR文件的lib目錄下邊。如果你忘了把iText JAR文件打包進去,servlet會報一個java.lang.NoClassDefFoundError的錯誤并停止運行。

          運行應(yīng)用

          在WAR文件部署之后,你已經(jīng)準(zhǔn)備好了測試servlet了。Jakarta Tomcat在8080端口上監(jiān)聽請求。

           

          在瀏覽器中請求http://hostname:8080/pdfservlet/createpdf。servlet將會執(zhí)行并返回瀏覽器一個PDF文檔。

          4、iText之外的方案

           

          iText提供了許多產(chǎn)生PDF文檔的底層API。然而,它不是對任何應(yīng)用都有效。在我的日常工作中,我結(jié)合Microsoft Word和Adobe Acrobat使用iText。首先,我們的團隊使用Microsoft Word設(shè)計了一個出貨表單。之后,我們用Acrobat把Word文檔轉(zhuǎn)換成PDF文檔。然后,我們使用iText的模板的功能,我們把PDF文件裝入到我們的應(yīng)用中。從這里,把數(shù)據(jù)填入表格和輸出最終的PDF文檔是相當(dāng)容易的。對基于報表的Web應(yīng)用,像JasperReports這樣的工具,它提供了比iText更高層次的抽象。

          posted on 2008-08-12 13:58 chu 閱讀(201) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(2)

          隨筆檔案

          我的鏈接

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 桑植县| 永和县| 博罗县| 惠州市| 抚宁县| 绵竹市| 罗甸县| 万山特区| 凤凰县| 邓州市| 于都县| 神池县| 体育| 平武县| 天长市| 霍城县| 嘉鱼县| 青河县| 安阳市| 泸水县| 全州县| 靖州| 洛宁县| 和林格尔县| 玛纳斯县| 广水市| 镇远县| 刚察县| 阿巴嘎旗| 大悟县| 汉川市| 兴仁县| 北碚区| 姚安县| 渝中区| 临汾市| 乌什县| 冀州市| 当雄县| 崇阳县| 北安市|