這幾天,任務中有一個難點是把一個打印頁面自動給生成PDF文檔,並且上傳至服務器,然而公司框架只有手動上傳文檔,打印時可以保存爲PDF在本地吧,所以感到很頭疼,剛開始沒有方向,所以只有surf the Internet了,網上看了很多資料,漸漸的從一點方向也不懂,到慢慢開始瞭解怎麼着手去做,廢話就不說了,
我看網上大概介紹了三種方式:Jasper Report 、 iText 、 flying sauser
jasper report和flying sauser感覺上要比iText的實現要強大一點,但是我實際用的時候對CSS沒有太大的需求,因爲是一個很簡單的表格形式,(如果對pdf的樣式有很高的要求,可以去看看flying sauser,這東西能解析HTML和CSS,而且能輸出成image,PDF等格式),我用的則是iText,我用的jar是:
itext-asian-5.2.0、itextpdf-5.5.1、xmlworker-5.5.4、jsoup-1.10.2(此包是java的html解析器)
1、自動生成PDF,
下面CreatePdfDocument.java、AsianFontProvider.java是兩個工具類,AsianFontProvider 是在CreatePdfDocument裏面調用的,而CreatePdfDocument則會在待會兒的實現裏面調用。
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import org.jsoup.Jsoup;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
/**
* Created by Sinoprof Codeproducer
* User: ck
* Date: 2017-10-31
* Time: 09:25:06
* 實現生成PDF文件
*/
public class CreatePdfDocument {
/**
* 根據URL提前blog的基本信息,返回結果
* @param URL 例:http://localhost:8080/scm/scm/po/gather/pdftest/pdftesthtml3.html(能直接返回某個html的URL,
* 我開始傳url時候被struts1攔截了,應爲get不到session的登錄人信息,所以得到的是登錄頁面的html)
* @return
* @throws Exception
*/
public static String[] extractHtmlInfo(String URL) throws Exception {
/*這裏爲什麼用數組,是因爲返回的時候不僅可以返回選擇的html,
還有從document提取其他的信息單獨存在數組裏返回,然後利用iText在pdf裏面組裝數據,可以在網上查*/
String[] info = new String[1];
// 直接把URL解析成document,然後調用document.html()解析爲html
org.jsoup.nodes.Document doc = Jsoup.connect(URL).get();
// 此doc.select是用來選擇完整的html中某一部分這裏爲第一個div的css爲entry的部分,所以你的html上要有div的class爲entry哦
org.jsoup.nodes.Element entry = doc.select("div.entry").first();
info[0] = entry.html();
return info;
}
/**
* 直接通過得到html來取得想要的部分html
* @param html
* @return
* @throws Exception
*/
public static String[] extractHtmlInfo2(String html) throws Exception {
String[] info = new String[1];
// 把html轉換爲document
org.jsoup.nodes.Document doc = Jsoup.parse(html);
// 此doc.select是用來選擇完整的html中某一部分這裏爲第一個div的css爲entry的部分,所以你的html上要有div的class爲entry哦
org.jsoup.nodes.Element entry = doc.select("div.entry").first();
info[0] = entry.html();
return info;
}
/**
* 把String 轉爲 InputStream
* @param content
* @return
*/
public static InputStream parse2Stream(String content) {
try {
ByteArrayInputStream stream = new ByteArrayInputStream(
content.getBytes("GBK"));
return stream;
} catch (Exception e) {
return null;
}
}
/**
* 直接把網頁內容轉爲PDF文件
*
* @param
* @throws Exception
*/
public static String parseURL2PDFFile(String pdfFile, String html) {
String returnVal = "";
try {
BaseFont bfCN = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H",
false);
// 中文字體定義
Font chFont = new Font(bfCN, 14, Font.NORMAL, BaseColor.BLUE);
Font secFont = new Font(bfCN, 12, Font.NORMAL, new BaseColor(0, 204,
255));
Font textFont = new Font(bfCN, 12, Font.NORMAL, BaseColor.BLACK);
Document document = new Document(PageSize.A4);
// 設置pdf的背景圖片
Image image = Image.getInstance("D:/移動背景圖片.jpg");
image.setAlignment(image.UNDERLYING);
image.setAbsolutePosition(0,0);
image.scaleAbsolute(595,842);
PdfWriter pdfwriter = PdfWriter.getInstance(document,
new FileOutputStream(pdfFile));
pdfwriter.setViewerPreferences(PdfWriter.HideToolbar);
document.open();
document.add(image);
//得到解析的html
String[] blogInfo = extractHtmlInfo2(html);
/*html文件轉換爲pdf文檔
AsianFontProvider()函數是用來解決XMLWorkerHelper.getInstance().parseXHtml()轉pdf中文不顯示問題*/
XMLWorkerHelper.getInstance().parseXHtml(pdfwriter, document,parse2Stream(blogInfo[0]),null, new AsianFontProvider());
document.close();
returnVal = "YES";
} catch (Exception e) {
returnVal = "NO";
e.printStackTrace();
} finally {
return returnVal;
}
}
}
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Font;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
/**
* Created by Sinoprof Codeproducer
* User: ck
* Date: 2017-10-31
* Time: 09:25:06
* 用來解決XMLWorkerHelper.getInstance().parseXHtml()轉pdf中文不顯示問題
*/
public class AsianFontProvider extends XMLWorkerFontProvider{
public Font getFont(final String fontname, final String encoding,
final boolean embedded, final float size, final int style,
final BaseColor color) {
BaseFont bf = null;
try {
bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
} catch (Exception e) {
e.printStackTrace();
}
Font font = new Font(bf, size, style, color);
font.setColor(color);
return font;
}
}
/** * @param args */ public static void main(String[] args) throws Exception { // 網頁必須是可以直接訪問的URL, String blogURL = "http://localhost:8080/scm/scm/po/gather/pdftest/pdftesthtml3.html"; // 傳入自己的html,注意html要符合w3c的標準html,因爲itext對html格式有點嚴格, String html = ""; // PDF最後的輸出文檔,注意d:/test/itext這些folder要先建好 String pdfFile = "d:/test/itext/demo-URL.pdf"; // 直接把網頁內容轉爲PDF文件 但是這個網頁必須是可以直接訪問的URL,注意在CreatePdfDocument.java的parseURL2PDFFile方法中要調用extractHtmlInfo方法 Demo4URL2PDF.parseURL2PDFFile(pdfFile, blogURL); // 直接傳入html的,注意在CreatePdfDocument.javaparseURL2PDFFile方法中要調用extractHtmlInfo2方法 Demo4URL2PDF.parseURL2PDFFile(pdfFile, html); }
這樣子基本就能在你指定的位置生成PDF文檔了,但注意前面說過iText對html的樣式支持的很少,所以生成的pdf文檔比較簡單,jtext-asian-5.2.0、itextpdf-5.5.1、xmlworker-5.5.4,這三個jar是我在網上找的支持table標籤的,(剛開始找的低版本的jar不支持table,所以我的表格出不來),還有就是AsianFontProvider.java這個類對中文的支持,因爲iText的XMLWorkerHelper.getInstance().parseXHtml轉PDF的時候,中文不顯示,
(網上說是什麼沒有默認的中文字體,我看網上有人修改xmlworker源碼的,使其默認一個字體),
但是我沒有成功,機緣巧合之下我找到了這種不用修改源碼的就像這樣
XMLWorkerHelper.getInstance().parseXHtml(pdfwriter,document,parse2Stream(blogInfo[0]),null,newAsianFontProvider());
剛好能幫我解決我的需求,結果圖片如下:
下面說說任務中的遇到的困難,上面說自動生成PDF時可以直接傳入string類型的html或者URL,但這個URL要是課可以直接訪問的,比如說網頁的網址,但是我們的項目中有struts1會攔截沒有登陸的,沒有登陸就會跳轉到登錄頁面,所以每次我用URL的去測試的時候,返回的始終是一個登錄頁面的html,其實我需要的就是一個動態的jsp生成PDF。
剛好,又讓我找到了現成的方法,其實原理是:得到數據後,讓jsp頁面不輸在瀏覽器上顯示,而是輸出到字節流最後以字符的形式返回,這樣我就可以得到動態的jsp組裝好後輸出的靜態的html,這樣就得到了我想要的html。
具體的我直接po博主的博客吧http://www.cnblogs.com/Iran1112/p/6013474.html,這裏面我學習了怎麼讓動態的jsp以流的方式輸出html。
至於上傳至服務器,要看看自己服務器要怎麼存儲了,一般的思路:先讀寫文件比如這樣:
// 輸入流
FileInputStream fi = new FileInputStream(fPath);
BufferedInputStream bi = new BufferedInputStream(fi);
// 輸出流
FileOutputStream fo = new FileOutputStream(newFile);
BufferedOutputStream bo = new BufferedOutputStream(fo);
// 先定義一個字節緩衝區,減少I/O次數,提高讀寫效率
byte[] buf = new byte[1024];
int len = 0;
while ((len=bi.read(buf))!=-1){
bo.write(buf,0,len);
// 使緩衝的輸出字節 被寫到底層輸出流中 避免電腦斷電等特殊情況導致緩衝區中的數據被清空
bo.flush();
}
fi.close();
fo.close();
然後拿到文件的信息,比如說:文件名稱、存儲路徑、大小...,再insert(存入)自己的表裏面,基本上大概就是這樣。