接上文,一封類似於下方設計的Excel報表,如何將它指定的區域導出為樣式一模一樣的JPG圖片呢?
要實現這個功能沒有現成的解決方案,谷歌度娘了好久也沒有,最終自己想了幾條思路:
思路1:將報表中的背景、邊框等截圖下來作為模型圖片,需要定時更新的數據通過JDBC讀取Oracle中的數據繪制到模型圖片上
否定原因:不具可行性,所有數據的坐標點需要有規則方便繪圖時循環繪制,工程量巨大,耦合性巨高,表格數據牽一發而動全身,並且不利於擴展。
思路2:不需要報表原型,生成Excel報表后,使用jxl或者poi一個單元一個單元讀取報表內所有單元格,包括單元格的數據和格式,邊框、寬度、高度、字體前景色、背景色都要讀取出來,然后通過JAVA繪圖,最終生成JPG格式的報表。
否定原因:具有一定的可行性,但是代碼量巨大,讀取和繪制費時費力,但是有一定的優點,可以在不忙的時間里編寫一個通用的程序,一勞永逸的解決所有導出問題。
思路3:比較奇葩,屬於博主突發奇想的招式,通過WPS或者Office打開Excel,編寫Robot機器人將鼠標移動到兩個指定坐標所覆蓋指定區域,Robot模擬敲擊Ctrl+C,接着將剪貼板上的數據繪圖,導出到JPG文件。
最終實現了思路3,有一定的好處也有弊端,好處是簡單便捷,可操作性高,代碼量低,擴展性強,換其他報表只需要提供另一組坐標參數即可
弊端是完全犧牲了JAVA的跨平台性,甚至如果運行在分辨率較低的服務器上,有可能導致復制操作無法執行
另一個弊端是線程變得不安全了,如果服務器負載較重,無法正確預計Office打開報表的時間,也有可能導致復制操作無法執行
或者多個生成報表截圖的任務同時執行時,顯然使用Robot會導致沖突。
下面提供思路3的實現代碼,以后有時間會使用思路2寫一個通用的程序
package com.newflypig.excel; import java.awt.Graphics; import java.awt.Image; import java.awt.Robot; import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import javax.imageio.ImageIO; public class OpenExcelDemo { public static void main(String[] args) throws Exception { openExcel("d:\\新增積分月報表.xlsx"); copyRectByPix(37, 207, 1215, 665); //給定兩個坐標點的數據,圈定截圖范圍 createImageFileFromClip("d:/" + getTimeStr() + ".jpg"); closeExcel((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth() - 15, 12); //需要模擬關閉事件,將Excel關閉,以便下次能正確打開報表 } private static void closeExcel(int i, int j) throws Exception { Robot robot = new Robot(); robot.delay(500); robot.mouseMove(i,j); robot.delay(500); robot.mousePress(InputEvent.BUTTON1_MASK); robot.delay(500); robot.mouseRelease(InputEvent.BUTTON1_MASK); robot.delay(500); robot.keyPress(KeyEvent.VK_ENTER); } public static void openExcel(String dir) throws Exception { Runtime.getRuntime().exec("cmd /k " + dir + ""); } public static void copyRectByPix(int fromX, int fromY, int toX, int toY) throws Exception { Robot robot = new Robot(); robot.delay(3000); // 延時3000毫秒 robot.mouseMove(fromX, fromY); robot.delay(500); robot.mousePress(InputEvent.BUTTON1_MASK); robot.delay(500); robot.mouseMove(toX, toY); robot.delay(500); robot.mouseRelease(InputEvent.BUTTON1_MASK); robot.setAutoDelay(200); robot.keyPress(KeyEvent.VK_CONTROL); robot.keyPress(KeyEvent.VK_C); robot.keyRelease(KeyEvent.VK_CONTROL); robot.keyRelease(KeyEvent.VK_C); } public static void createImageFileFromClip(String dir) throws Exception { Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard() .getContents(null); if (null != t && t.isDataFlavorSupported(DataFlavor.imageFlavor)) { Image image = (Image) t.getTransferData(DataFlavor.imageFlavor); savePic(image, dir); } } public static String savePic(Image iamge, String dir) throws Exception { int w = iamge.getWidth(null); int h = iamge.getHeight(null); // 首先創建一個BufferedImage變量,因為ImageIO寫圖片用到了BufferedImage變量。 BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR); // 再創建一個Graphics變量,用來畫出來要保持的圖片,及上面傳遞過來的Image變量 Graphics g = bi.getGraphics(); g.drawImage(iamge, 0, 0, null); // 將BufferedImage變量寫入文件中。 ImageIO.write(bi, "jpg", new File(dir)); return dir; } public static String getTimeStr() { String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); return time; } }