現(xiàn)象:
- 在web頁面導入excel文件,excel中包含圖片,在后臺進行解析的時候,有時候會報NullPointerException根盒。
問題排查:
- 由于POI里面并沒有把這里的錯誤信息輸出到日志文件,在服務器上報錯后物蝙,從后臺日志文件中根本看不出什么炎滞。
在本地調(diào)試的時候,又沒有報錯诬乞,搞得我們一直以為是臨時文件的沒有正常讀取導致的册赛,而且我們把tomcat的temp目錄刪除后重啟,竟然神奇又不報錯了震嫉。
哪知道昨天客戶現(xiàn)場又報這個錯森瘪,但是我剛剛刪除了temp目錄,重啟了tomcat捌倍隆6蟛恰!悴势!受不了窗宇,我死活都要找到原因,說干就干特纤,把線上的temp目錄覆蓋我本地的temp目錄军俊,
本地環(huán)境啟起來,拿那個現(xiàn)場出錯的excel一試捧存,錯誤沒有辜負我的期望粪躬,如期而遇,控制臺錯誤日志如下(吐槽POI昔穴,這個錯誤日志竟然只輸出控制臺):
java.lang.NullPointerException
at org.apache.poi.ss.util.ImageUtils.getResolution(ImageUtils.java:117)
at org.apache.poi.ss.util.ImageUtils.getImageDimension(ImageUtils.java:79)
at org.apache.poi.ss.util.ImageUtils.setPreferredSize(ImageUtils.java:141)
at org.apache.poi.xssf.usermodel.XSSFPicture.getPreferredSize(XSSFPicture.java:221)
at org.apache.poi.xssf.usermodel.XSSFPicture.getPreferredSize(XSSFPicture.java:210)
at org.apache.poi.xssf.usermodel.XSSFPicture.getPreferredSize(XSSFPicture.java:200)
at com.hentre.all580.core.util.excel.ExcelUtilXlsx.getPicValue(ExcelUtilXlsx.java:51)
at com.hentre.all580.core.util.excel.ExcelUtil.getCellValue(ExcelUtil.java:118)
at com.hentre.all580.core.util.excel.ExcelHandler.getValue(ExcelHandler.java:155)
at com.hentre.all580.core.util.excel.JourneyExcelHandler.templateResolve(JourneyExcelHandler.java:64)
at com.hentre.all580.core.util.excel.JourneyExcelHandler.templateResolve(JourneyExcelHandler.java:115)
at com.hentre.all580.coreplatform.controller.upload.ImportItineraryHandler.doExecute(ImportItineraryHandler.java:41)
at com.hentre.all580.coreplatform.controller.upload.AbstractImportHandler.execute(AbstractImportHandler.java:41)
at com.hentre.all580.coreplatform.controller.upload.BaseUploadController.upload(BaseUploadController.java:184)
...
- 找到報錯地方的代碼如下:
public static int[] getResolution(ImageReader r) throws IOException {
int hdpi = 96;
int vdpi = 96;
double mm2inch = 25.4D;
Element node = (Element)r.getImageMetadata(0).getAsTree("javax_imageio_1.0"); //這一行拋錯
NodeList lst = node.getElementsByTagName("HorizontalPixelSize");
if(lst != null && lst.getLength() == 1) {
hdpi = (int)(mm2inch / (double)Float.parseFloat(((Element)lst.item(0)).getAttribute("value")));
}
lst = node.getElementsByTagName("VerticalPixelSize");
if(lst != null && lst.getLength() == 1) {
vdpi = (int)(mm2inch / (double)Float.parseFloat(((Element)lst.item(0)).getAttribute("value")));
}
return new int[]{hdpi, vdpi};
}
- 從上面的現(xiàn)象看镰官,一直以為是臨時文件的讀取問題,反復調(diào)試臨時文件的創(chuàng)建和讀取吗货,此處略過10000字泳唠,poi里面解析圖片的代碼復雜。卿操。警检。。害淤。
- 偶然在調(diào)試在上面拋錯那里的代碼時扇雕,發(fā)現(xiàn)r的類型竟然是不一樣的,正常的情況下是JPEGImageReader窥摄,報錯的情況竟然是CMYKJPEGImageReader镶奉。
找到CMYKJPEGImageReader的getImageMetadata()方法,代碼如下:
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
return null;
}
- 瞬間明白了,但是這坑爹的poi怎么會把圖片解析成這個類型呢哨苛?為什么同一圖片鸽凶,有時候是,有時候又不是呢建峭?百度了一下玻侥,大概說明如下:
CMYK也稱作印刷色彩模式,顧名思義就是用來印刷的亿蒸。
它和RGB相比有一個很大的不同:RGB模式是一種發(fā)光的色彩模式凑兰,你在一間黑暗的房間內(nèi)仍然可以看見屏幕上的內(nèi)容;
CMYK是一種依靠反光的色彩模式边锁,我們是怎樣閱讀報紙的內(nèi)容呢姑食?是由陽光或燈光照射到報紙上,再反射到我們的眼中茅坛,才看到內(nèi)容音半。它需要有外界光源,如果你在黑暗房間內(nèi)是無法閱讀報紙的贡蓖。
只要在屏幕上顯示的圖像曹鸠,就是RGB模式表現(xiàn)的。只要是在印刷品上看到的圖像摩梧,就是CMYK模式表現(xiàn)的物延。比如期刊、雜志仅父、報紙、宣傳畫等浑吟,都是印刷出來的笙纤,那么就是CMYK模式的了。
總結(jié):
- 發(fā)現(xiàn)如果用POI從excel中獲取圖片的話组力,那么上面的問題是無解的省容。咱惹不起,只能躲了燎字,果斷在導入模板里面把圖片刪除了腥椒,然后把這個原則深深的記在了腦海里!
我真的是太機智了候衍。笼蛛。。