天天看點

SSM整合EasyPoi,解決圖檔導出報錯ArrayIndexOutOfBoundsException 參考部落格:前言手動導入jar包圖檔導出報錯ArrayIndexOutOfBoundsException

參考部落格:

https://blog.csdn.net/qq_34988540/article/details/83050187

https://blog.csdn.net/breakaway_01/article/details/103895099

前言

最近由于JavaWeb期末作品的需求,在SSM項目中引入了EasyPoi,用于将訂單資料導出為Excel。說一下遇到的兩個坑

手動導入jar包

由于項目是群組員共同編碼的,一開始就用的手動導入jar包,沒有使用maven。EasyPoi官方文檔并沒有說手動導入應該包含哪些jar包,報錯java.lang.NoClassDefFoundError,顯然是缺少jar包。

在Eclipse,手動導入jar包,是沒有辦法追蹤源碼的。看報錯我并不知道缺少了哪個jar包。

後來沒辦法,我就建立了一個maven項目,使用maven引入EasyPoi,然後導出為war包。然後将war包解壓,發現 EasyPoi 3.2.0一共需要導入下面 jar包

commons-lang3-3.2.1.jar

guava-16.0.1.jar

easypoi-base-3.2.0.jar

easypoi-annotation-3.2.0.jar

easypoi-web-3.2.0.jar

圖檔導出報錯ArrayIndexOutOfBoundsException

至于原因,前言中的參考部落格中,講的非常詳細了,這裡我說說,在 SSM 項目中,如何替換EasyPoi的預設配置,進而解決路徑解析問題。
SSM整合EasyPoi,解決圖檔導出報錯ArrayIndexOutOfBoundsException 參考部落格:前言手動導入jar包圖檔導出報錯ArrayIndexOutOfBoundsException
package easymall.component;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import javax.imageio.ImageIO;

import org.apache.commons.io.IOUtils;

import com.google.common.cache.CacheLoader;

import cn.afterturn.easypoi.cache.manager.POICacheManager;

/**
 * 自定義EasyPoiFixedCacheLoader,以替換EasyPoi的預設CacheLoader實作
 * 解決圖檔路徑解析的bug
 * 參考部落格:https://blog.csdn.net/qq_34988540/article/details/83050187
 * 
 * @author	passerbyYSQ
 * @date	2020-12-29 15:26:04
 */
public class EasyPoiFixedCacheLoader extends CacheLoader<String, byte[]> {

	@Override
	public byte[] load(String imagePath) throws Exception {
		InputStream is = POICacheManager.getFile(imagePath);
        BufferedImage bufferImg = ImageIO.read(is);
        ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
        try {
            ImageIO.write(bufferImg,
            		// lastIndexOf 為修改源碼的地方
                    imagePath.substring(imagePath.lastIndexOf(".") + 1, imagePath.length()),
                    byteArrayOut);
            return byteArrayOut.toByteArray();
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(byteArrayOut);
        }
	}

}
           
package easymall.component;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.afterturn.easypoi.cache.manager.FileLoaderImpl;
import cn.afterturn.easypoi.cache.manager.IFileLoader;

/**
 * 解決EasyPoi網絡圖檔下載下傳并導出的問題
 * 參考部落格:https://blog.csdn.net/breakaway_01/article/details/103895099
 * 
 * @author	passerbyYSQ
 * @date	2020-12-29 15:33:22
 */
public class EasyPoiFixedFileLoaderImpl implements IFileLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(FileLoaderImpl.class);

    @Override
    public byte[] getFile(String url) {
        InputStream fileis = null;
        ByteArrayOutputStream baos = null;
        try {

            //判斷是否是網絡位址
            if (url.startsWith("http")) {
                URL urlObj = new URL(url);
                URLConnection urlConnection = urlObj.openConnection();
                urlConnection.setConnectTimeout(3 * 1000);
                urlConnection.setReadTimeout(60 * 1000);
                urlConnection.setDoInput(true);
                urlConnection.setRequestProperty("Accept-Charset", "UTF-8");
                fileis = urlConnection.getInputStream();

            } else {
                //先用絕對路徑查詢,再查詢相對路徑
                try {
                    fileis = new FileInputStream(url);
                } catch (FileNotFoundException e) {
                    //擷取項目檔案
                    fileis = FileLoaderImpl.class.getClassLoader().getResourceAsStream(url);
//                    if (fileis == null) {
//                        //最後再拿相對檔案路徑
//                        String path = PoiPublicUtil.getWebRootPath(url);
//                        fileis = new FileInputStream(path);
//                    }
                }
            }

            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fileis.read(buffer)) > -1) {
                baos.write(buffer, 0, len);
            }
            baos.flush();
            return baos.toByteArray();
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(fileis);
            IOUtils.closeQuietly(baos);
        }
        LOGGER.error(fileis + "這個路徑檔案沒有找到,請查詢");
        return null;
    }
}
           
tomcat啟動時會讀取web.xml檔案的配置,初始化我們的Spring容器,在Spring容器初始化完成之後,我們可以進行“替換”操作,修複EasyPoi的bug。是以我們需要自定義自己的ContextLoaderListener 
package easymall.component;

import java.util.concurrent.TimeUnit;

import javax.servlet.ServletContextEvent;

import org.springframework.web.context.ContextLoaderListener;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;

import cn.afterturn.easypoi.cache.ImageCache;
import cn.afterturn.easypoi.cache.manager.POICacheManager;

/**
 * @author	passerbyYSQ
 * @date	2020-12-29 15:15:41
 */
public class MyContextLoaderListener extends ContextLoaderListener {

	@Override
	public void contextInitialized(ServletContextEvent event) {
		
		super.contextInitialized(event); // 不能去掉
		
		// 容器初始化完成後,替換EasyPoi的ImageCache
		// 以解決EasyPoi源碼中導出本地圖檔的路徑解析的bug
		
		EasyPoiFixedCacheLoader cacheLoader = new EasyPoiFixedCacheLoader();
		LoadingCache<String, byte[]> loadingCache = CacheBuilder.newBuilder()
				.expireAfterWrite(1, TimeUnit.DAYS)
                .maximumSize(2000).build(cacheLoader);
		// 替換
		ImageCache.setLoadingCache(loadingCache);
		
		// 替換
		EasyPoiFixedFileLoaderImpl fileLoader = new EasyPoiFixedFileLoaderImpl();
		POICacheManager.setFileLoder(fileLoader);
		
		System.out.println("容器初始化完畢!!!");
	}

}
           

web.xml

<!-- 指定一ContextLoaderListener方式啟動Spring容器
  原本:org.springframework.web.context.ContextLoaderListener
  修改後:easymall.component.MyContextLoaderListener(自定義的ContextLoaderListener的路徑)
   -->
  <listener>
  	<listener-class>
  		easymall.component.MyContextLoaderListener
  	</listener-class>
  </listener>
           

繼續閱讀