jeecg导入excel 含图片(嵌入式,浮动式)

news2024/12/23 7:17:30

jeecgboot的excel导入 含图片(嵌入式,浮动式)

  • 一、啰嗦
  • 二、准备
  • 三、 代码
    • 1、代码(修改覆写的ExcelImportServer)
    • 2、代码(修改覆写的PoiPublicUtil)
    • 3、代码(新增类SAXParserHandler)
    • 4、代码(PoiPublicUtil文件中加入convertPackagePartToPictureData方法)
  • 四、总结
    • 1、ExcelImportServer代码
    • 2、PoiPublicUtil代码
    • 3、SAXParserHandler代码

看流程可以往下看看,使用代码直接调到四、总结

一、啰嗦

不写公共用的了,就直接写出自己遇到的。
原本以为跟导入一样,就只是一个简单的导出,鬼知道在excel中加入图片有两种方式(嵌入式和浮动式)在这里插入图片描述
然而项目支持浮动式,可是当我使用浮动式插入图片时(没对比没伤害),他直接撑开了,下面这个还好,我还有一个直接占满我整个屏幕跟我壁纸一样了都,要是插入多了。光调节大小就得烦死了。 在这里插入图片描述
又得写代码,我一搜,文档没有,框架相关的文章也没有,只能自己写,我这个cv工程师直接崩溃(得亏还有ChatGPT,项目需要,直接拼拼凑凑开始干叭)
在这里插入图片描述

二、准备

说是准备其实也没啥,就是啰嗦+1了
目前excel的版本有两种Excel 2003 的.xlsExcel 2007 .xls.xlsx,也不知道说的标准不标准
这里使用的就是Excel 2007这个版本的,因为03版的还没有方法访问内置文件,07的可以进行访问(能访问也就能获取到其中的图片,也可能是我技术不到,反正我得问AI,还是简单来)
具体需要导入的数据如下(反正都差不多,我怕在涉及一些数据,就随便改了改,反正主要是导入图片,不要在意这些细节了)
在这里插入图片描述

三、 代码

啰里啰嗦的终于要了代码环节了
在这里插入图片描述

具体导入在JeecgController类里的importExcel这个方法,复制出来修改为自己的就可以(都是大佬肯定嘎嘎简单)

具体使用的就是这个方法

List<T> list = ExcelImportUtil.importExcel(file.getInputStream(), clazz, params);

点击他会跳转到下面的方法

/**
 * Excel 导入 数据源IO流,不返回校验结果 导入 字段类型 Integer,Long,Double,Date,String,Boolean
 * 
 * @param file
 * @param pojoClass
 * @param params
 * @return
 * @throws Exception
 */
public static <T> List<T> importExcel(InputStream inputstream, Class<?> pojoClass, ImportParams params) throws Exception {
	return new ExcelImportServer().importExcelByIs(inputstream, pojoClass, params).getList();
}

然后再次点击又跳到下面这个方法

/**
 * Excel 导入 field 字段类型 Integer,Long,Double,Date,String,Boolean
 * 
 * @param inputstream
 * @param pojoClass
 * @param params
 * @return
 * @throws Exception
 */
public ExcelImportResult importExcelByIs(InputStream inputstream, Class<?> pojoClass, ImportParams params) throws Exception {
	if (LOGGER.isDebugEnabled()) {
		LOGGER.debug("Excel import start ,class is {}", pojoClass);
	}
	List<T> result = new ArrayList<T>();
	Workbook book = null;
	boolean isXSSFWorkbook = false;
	if (!(inputstream.markSupported())) {
		inputstream = new PushbackInputStream(inputstream, 8);
	}
	//begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------
	//------poi4.x begin----
//		FileMagic fm = FileMagic.valueOf(FileMagic.prepareToCheckMagic(inputstream));
//		if(FileMagic.OLE2 == fm){
//			isXSSFWorkbook=false;
//		}
	book = WorkbookFactory.create(inputstream);
	if(book instanceof XSSFWorkbook){
		isXSSFWorkbook=true;
	}
	LOGGER.info("  >>>  poi3升级到4.0兼容改造工作, isXSSFWorkbook = " +isXSSFWorkbook);
	//end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------

	//begin-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------
	//获取导入文本的sheet数
	//update-begin-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
	if(params.getSheetNum()==0){
		int sheetNum = book.getNumberOfSheets();
		if(sheetNum>0){
			params.setSheetNum(sheetNum);
		}
	}
	//update-end-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
	//end-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------
	createErrorCellStyle(book);
	Map<String, PictureData> pictures;

	//update-begin-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数
	for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()
			+ params.getSheetNum(); i++) {
	//update-end-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug(" start to read excel by is ,startTime is {}", System.currentTimeMillis());
		}
		if (isXSSFWorkbook) {
			pictures = PoiPublicUtil.getSheetPictrues07((XSSFSheet) book.getSheetAt(i), (XSSFWorkbook) book);
		} else {
			pictures = PoiPublicUtil.getSheetPictrues03((HSSFSheet) book.getSheetAt(i), (HSSFWorkbook) book);
		}
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug(" end to read excel by is ,endTime is {}", new Date().getTime());
		}
		result.addAll(importExcel(result, book.getSheetAt(i), pojoClass, params, pictures));
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug(" end to read excel list by pos ,endTime is {}", new Date().getTime());
		}
	}
	if (params.isNeedSave()) {
		saveThisExcel(params, pojoClass, isXSSFWorkbook, book);
	}
	return new ExcelImportResult(result, verfiyFail, book);
}

1、代码(修改覆写的ExcelImportServer)

上面的importExcelByIs这个方法需要进行一些修改,但因为是源码包,无法进行修改,所以这时候就要把他复制出来,存放相同的地址,然后就完成替换了
包名为package org.jeecgframework.poi.excel.imports;下的ExcelImportServer文件
直接一顿cv完工
在这里插入图片描述

然后修改的代码如下(有可以导入嵌入式图片 又不能影响之前导入使用)
具体就是:
1、加入了OPCPackage 进行读取07版的excel的包
2、之前运行完book = WorkbookFactory.create(inputstream);会把输入流进行关闭,这里将输入流复制到字节数组ByteArrayOutputStream,然后在获取到OPCPackage 之后手动关闭流

/**
     * Excel 导入 field 字段类型 Integer,Long,Double,Date,String,Boolean
     *
     * @param inputstream
     * @param pojoClass
     * @param params
     * @return
     * @throws Exception
     */
    public ExcelImportResult importExcelByIs(InputStream inputstream, Class<?> pojoClass, ImportParams params) throws Exception {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Excel import start ,class is {}", pojoClass);
        }
        List<T> result = new ArrayList<T>();
        Workbook book = null;
        OPCPackage opc = null;
        boolean isXSSFWorkbook = false;
        if (!(inputstream.markSupported())) {
            inputstream = new PushbackInputStream(inputstream, 8);
        }
        //begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------
        //------poi4.x begin----
//		FileMagic fm = FileMagic.valueOf(FileMagic.prepareToCheckMagic(inputstream));
//		if(FileMagic.OLE2 == fm){
//			isXSSFWorkbook=false;
//		}
        // 将输入流内容复制到字节数组
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = inputstream.read(buffer)) != -1) {
            byteArrayOutputStream.write(buffer, 0, bytesRead);
        }
        byte[] workbookData = byteArrayOutputStream.toByteArray();

        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(workbookData)) {
            // 创建Workbook,不会直接关闭原始输入流
            book = WorkbookFactory.create(byteArrayInputStream);
            if (book instanceof XSSFWorkbook) {
                isXSSFWorkbook = true;
                opc = PackageHelper.open(new ByteArrayInputStream(workbookData));
            }
        } finally {
            try {
                //之前操作自动关闭了输入流,由于输入流内容复制到字节数组没有进行关闭,所以这里进行关闭
                inputstream.close();
            } catch (IOException e) {
                LOGGER.info("Error closing input stream: " + e.getMessage());
            }
        }
//        book = WorkbookFactory.create(nonClosingInputStream);
//        if (book instanceof XSSFWorkbook) {
//            isXSSFWorkbook = true;
//            opc = PackageHelper.open(inputstream);
//        }
        LOGGER.info("  >>>  poi3升级到4.0兼容改造工作, isXSSFWorkbook = " + isXSSFWorkbook);
        //end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------

        //begin-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------
        //获取导入文本的sheet数
        //update-begin-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
        if (params.getSheetNum() == 0) {
            int sheetNum = book.getNumberOfSheets();
            if (sheetNum > 0) {
                params.setSheetNum(sheetNum);
            }
        }
        //update-end-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
        //end-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------
        createErrorCellStyle(book);
        Map<String, PictureData> pictures;

        //update-begin-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数
        for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()
                + params.getSheetNum(); i++) {
            //update-end-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(" start to read excel by is ,startTime is {}", System.currentTimeMillis());
            }
            //03版xls因为文件限制暂时无法识别内嵌图片
            //07版xlsx 此处以进行适配内嵌图片,内嵌和浮动自动识别返回
            if (isXSSFWorkbook) {
                pictures = PoiPublicUtil.getAllPictures(opc,(XSSFSheet) book.getSheetAt(i), (XSSFWorkbook) book);
//                pictures = PoiPublicUtil.getSheetPictrues07((XSSFSheet) book.getSheetAt(i), (XSSFWorkbook) book);
            } else {
                pictures = PoiPublicUtil.getSheetPictrues03((HSSFSheet) book.getSheetAt(i), (HSSFWorkbook) book);
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(" end to read excel by is ,endTime is {}", new Date().getTime());
            }
            result.addAll(importExcel(result, book.getSheetAt(i), pojoClass, params, pictures));
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(" end to read excel list by pos ,endTime is {}", new Date().getTime());
            }
        }
        if (params.isNeedSave()) {
            saveThisExcel(params, pojoClass, isXSSFWorkbook, book);
        }
        return new ExcelImportResult(result, verfiyFail, book);
    }

2、代码(修改覆写的PoiPublicUtil)

上述代码在这里使用方法进行识别图片,具体加入的方法是getAllPictures(OPCPackage opc, XSSFSheet sheet, XSSFWorkbook workbook)

//03版xls因为文件限制暂时无法识别内嵌图片
            //07版xlsx 此处以进行适配内嵌图片,内嵌和浮动自动识别返回
            if (isXSSFWorkbook) {
                pictures = PoiPublicUtil.getAllPictures(opc,(XSSFSheet) book.getSheetAt(i), (XSSFWorkbook) book);
//                pictures = PoiPublicUtil.getSheetPictrues07((XSSFSheet) book.getSheetAt(i), (XSSFWorkbook) book);
            } else {
                pictures = PoiPublicUtil.getSheetPictrues03((HSSFSheet) book.getSheetAt(i), (HSSFWorkbook) book);
            }

点击会getSheetPictrues03或getSheetPictrues07也调到源码路径
包名package org.jeecgframework.poi.util;下的PoiPublicUtil文件
这里模仿上面也复制出来一份
在这里插入图片描述

加入以下方法,浮动式调用的值自带的,嵌入式使用了一个手写的方法

public static Map<String, PictureData> getAllPictures(OPCPackage opc, XSSFSheet sheet, XSSFWorkbook workbook) throws Exception {
		Map<String, PictureData> sheetIndexPicMap = new HashMap<>();
		// 处理嵌入式图片
		List<PackagePart> parts = opc.getParts();
		sheetIndexPicMap.putAll(getEmbedPictures(parts));
		// 处理浮动式图片
		sheetIndexPicMap.putAll(getSheetPictrues07(sheet, workbook));
		return sheetIndexPicMap;
	}

getEmbedPictures方法

/**
	 * 获取嵌入式图片
	 * @param parts
	 * @return
	 * @throws JDOMException
	 * @throws IOException
	 * @throws ParserConfigurationException
	 */
	private static Map<String, PictureData> getEmbedPictures(List<PackagePart> parts) throws JDOMException, IOException, ParserConfigurationException, SAXException {
		Map<String, Set<String>> mapImg = new HashMap<>();
		Map<String, String> mapImgPath = new HashMap<>();
		Map<Integer, List<String>> dataMap = new HashMap<>();

		for (PackagePart part : parts) {
//            System.out.println(part.getPartName());
			PackagePartName partName = part.getPartName();
			String name = partName.getName();
			if ("/xl/cellimages.xml".equals(name)) {
				SAXBuilder builder = new SAXBuilder();
				// 获取文档
				Document doc = builder.build(part.getInputStream());
				// 获取根节点
				Element root = doc.getRootElement();
				List<Element> cellImageList = root.getChildren();
				for (Element imgEle : cellImageList) {
					Element xdrPic = imgEle.getChildren().get(0);
					Element xdrNvPicPr = xdrPic.getChildren().get(0);
					Element xdrBlipFill = xdrPic.getChildren().get(1);
					Element aBlip = xdrBlipFill.getChildren().get(0);
					Attribute attr = aBlip.getAttributes().get(0);
					String imgId = xdrNvPicPr.getChildren().get(0).getAttributeValue("name");
					String id = attr.getValue();
					if (mapImg.containsKey(id)) {
						mapImg.get(id).add(imgId);
					} else {
						Set<String> set = new HashSet<>();
						set.add(imgId);
						mapImg.put(id, set);
					}
				}
			}

			if ("/xl/_rels/cellimages.xml.rels".equals(name)) {
				SAXBuilder builder = new SAXBuilder();
				// 获取文档
				Document doc = builder.build(part.getInputStream());
				// 获取根节点
				Element root = doc.getRootElement();
				List<Element> relationshipList = root.getChildren();
				for (Element relationship : relationshipList) {
					String id = relationship.getAttributeValue("Id");
					String target = relationship.getAttributeValue("Target");
					mapImgPath.put(id, target);
				}
			}

			if (name.contains("/xl/worksheets/sheet")) {
				// 获取文档
				String sheetNoStr = name.replace("/xl/worksheets/sheet", "").replace(".xml", "");
				Integer sheetNo = Integer.valueOf(sheetNoStr) - 1;
				// 步骤1:创建SAXParserFactory实例
				SAXParserFactory factory = SAXParserFactory.newInstance();
				// 步骤2:创建SAXParser实例
				SAXParser saxParser = factory.newSAXParser();
				SAXParserHandler saxParserHandler = new SAXParserHandler();
				saxParser.parse(part.getInputStream(), saxParserHandler);

				List<String> rows = saxParserHandler.getRows();

				dataMap.put(sheetNo, rows);
			}

		}

		Map<String, String> imgMap = new HashMap<>();
		for (String id : mapImg.keySet()) {
			Set<String> imgIds = mapImg.get(id);
			String path = mapImgPath.get(id);
			for (String imgId : imgIds) {
				imgMap.put(imgId, path);
			}
		}
		for (Integer key : dataMap.keySet()) {
			List<String> rows = dataMap.get(key);
			for (int i = 0; i < rows.size(); i++) {
				String imgId = rows.get(i);
				String imgId_index = imgId.substring(0, imgId.indexOf(":"));
				String imgId_url = imgId.substring(imgId.indexOf(":")+1);
				if (imgMap.containsKey(imgId_url)) {
					rows.set(i, imgId_index+":"+imgMap.get(imgId_url));
				}
			}
		}

		Map<String, PictureData> map = new HashMap<>();
		for (Integer key : dataMap.keySet()) {
			List<String> pathList = dataMap.get(key);
			for (int i = 0; i < pathList.size(); i++) {
				String path = pathList.get(i);
				String path_index = path.substring(0, path.indexOf(":"));
				String path_url = path.substring(path.indexOf(":")+1);
				if (StringUtils.isNotEmpty(path_url)) {
					for (PackagePart part : parts) {
						PackagePartName partName = part.getPartName();
						String name = partName.getName();
						if (name.contains(path_url)) {
							//进行转换
							PictureData pictureData = convertPackagePartToPictureData(part,new XSSFWorkbook());
							map.put(path_index, pictureData);
							break;
						}
					}
				}
			}
		}
		return map;
	}

上面的方法
1、调用了一个xml解析的自定义类SAXParserHandler
2、调用了一个PackagePart转换为PictureData的方法convertPackagePartToPictureData

3、代码(新增类SAXParserHandler)

实体类SAXParserHandler,我跟上面一样也这样建了
在这里插入图片描述

package org.jeecgframework.poi.xml;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.List;

//xml解析
public class SAXParserHandler extends DefaultHandler {

    String value = null;

    List<String> rows = new ArrayList<>();

    int rowIndex = 0;

    int currentColumn = 0;

    public List<String> getRows() {
        return rows;
    }

    //用来标识解析开始
    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
    }

    //用来标识解析结束
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }

    //解析xml元素
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        if (qName.equals("row")) {
            value = "";
            // 遇到新的行时,重置列号为0
            currentColumn = 0;
        } else if (qName.equals("c")) {
            // 获取列号
            String columnRef = attributes.getValue("r");
            if (columnRef != null) {
                currentColumn = parseColumnIndex(columnRef.replaceAll("[^A-Z]", ""));
            }
        }

    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if (qName.equals("c")) {
            if (value != null && value.contains("DISPIMG")) {
                value = value.substring(value.lastIndexOf("DISPIMG(")).replace("DISPIMG(\"", "");
                value = value.substring(0, value.indexOf("\""));
//                rows.add(rowIndex, value);
                rows.add(rowIndex + "_" + currentColumn + ":" + value);
            } else {
//                rows.add(rowIndex, null);
                rows.add(rowIndex + "_" + currentColumn + ":null");
            }
            value = "";
        }else if (qName.equals("row")) {
            rowIndex++;
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        value += new String(ch, start, length);
    }

    // 解析列号
    private int parseColumnIndex(String columnRef) {
        int result = 0;
        for (int i = 0; i < columnRef.length(); i++) {
            result = result * 26 + (columnRef.charAt(i) - 'A' + 1);
        }
        return result - 1;
    }
}

4、代码(PoiPublicUtil文件中加入convertPackagePartToPictureData方法)

具体加入的方法convertPackagePartToPictureData

/**
	 * 将PackagePart转换为PictureData
	 * @param packagePart
	 * @param workbook
	 * @return
	 */
	public static PictureData convertPackagePartToPictureData(PackagePart packagePart, XSSFWorkbook workbook) {
		if (packagePart == null) {
			throw new IllegalArgumentException("PackagePart cannot be null");
		}
		if (workbook == null) {
			throw new IllegalArgumentException("Workbook cannot be null");
		}

		try (InputStream inputStream = packagePart.getInputStream()) {
			// 从PackagePart读取数据
			byte[] data = IOUtils.toByteArray(inputStream);

			// 确定内容,创建PictureType
			String contentType = packagePart.getContentType();
			// 默认PNG
			int pictureType = XSSFWorkbook.PICTURE_TYPE_PNG;
			if (contentType.equals("image/jpeg")) {
				pictureType = XSSFWorkbook.PICTURE_TYPE_JPEG;
			} else if (contentType.equals("image/png")) {
				pictureType = XSSFWorkbook.PICTURE_TYPE_PNG;
			}

			// 将图片数据添加到工作簿
			int pictureIndex = workbook.addPicture(data, pictureType);
			return workbook.getAllPictures().get(pictureIndex);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

四、总结

具体使用的文件为下面四个
在这里插入图片描述

1、ExcelImportServer代码

/**
 * Copyright 2013-2015 JEECG (jeecgos@163.com)
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jeecgframework.poi.excel.imports;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ooxml.util.PackageHelper;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.jeecgframework.core.util.ApplicationContextUtil;
import org.jeecgframework.poi.excel.annotation.ExcelTarget;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams;
import org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;
import org.jeecgframework.poi.excel.entity.result.ExcelImportResult;
import org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult;
import org.jeecgframework.poi.excel.imports.base.ImportBaseService;
import org.jeecgframework.poi.excel.imports.base.ImportFileServiceI;
import org.jeecgframework.poi.excel.imports.verifys.VerifyHandlerServer;
import org.jeecgframework.poi.exception.excel.ExcelImportException;
import org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum;
import org.jeecgframework.poi.util.ExcelUtil;
import org.jeecgframework.poi.util.PoiPublicUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Excel 导入服务
 *
 * @author JEECG
 * @date 2014年6月26日 下午9:20:51
 */
@SuppressWarnings({"rawtypes", "unchecked", "hiding"})
public class ExcelImportServer extends ImportBaseService {

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

    private CellValueServer cellValueServer;

    private VerifyHandlerServer verifyHandlerServer;

    private boolean verfiyFail = false;
    /**
     * 异常数据styler
     */
    private CellStyle errorCellStyle;

    public ExcelImportServer() {
        this.cellValueServer = new CellValueServer();
        this.verifyHandlerServer = new VerifyHandlerServer();
    }

    /***
     * 向List里面继续添加元素
     *
     * @param object
     * @param param
     * @param row
     * @param titlemap
     * @param targetId
     * @param pictures
     * @param params
     */
    private void addListContinue(Object object, ExcelCollectionParams param, Row row, Map<Integer, String> titlemap, String targetId, Map<String, PictureData> pictures, ImportParams params) throws Exception {
        Collection collection = (Collection) PoiPublicUtil.getMethod(param.getName(), object.getClass()).invoke(object, new Object[]{});
        Object entity = PoiPublicUtil.createObject(param.getType(), targetId);
        String picId;
        boolean isUsed = false;// 是否需要加上这个对象
        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
            Cell cell = row.getCell(i);
            String titleString = (String) titlemap.get(i);
            if (param.getExcelParams().containsKey(titleString)) {
                if (param.getExcelParams().get(titleString).getType() == 2) {
                    picId = row.getRowNum() + "_" + i;
                    saveImage(object, picId, param.getExcelParams(), titleString, pictures, params);
                } else {
                    saveFieldValue(params, entity, cell, param.getExcelParams(), titleString, row);
                }
                isUsed = true;
            }
        }
        if (isUsed) {
            collection.add(entity);
        }
    }

    /**
     * 获取key的值,针对不同类型获取不同的值
     *
     * @Author JEECG
     * @date 2013-11-21
     * @param cell
     * @return
     */
    private String getKeyValue(Cell cell) {
        if (cell == null) {
            return null;
        }
        Object obj = null;
        switch (cell.getCellTypeEnum()) {
            case STRING:
                obj = cell.getStringCellValue();
                break;
            case BOOLEAN:
                obj = cell.getBooleanCellValue();
                break;
            case NUMERIC:
                obj = cell.getNumericCellValue();
                break;
            case FORMULA:
                obj = cell.getCellFormula();
                break;
        }
        return obj == null ? null : obj.toString().trim();
    }

    /**
     * 获取保存的真实路径
     *
     * @param excelImportEntity
     * @param object
     * @return
     * @throws Exception
     */
    private String getSaveUrl(ExcelImportEntity excelImportEntity, Object object) throws Exception {
        String url = "";
        if (excelImportEntity.getSaveUrl().equals("upload")) {
            if (excelImportEntity.getMethods() != null && excelImportEntity.getMethods().size() > 0) {
                object = getFieldBySomeMethod(excelImportEntity.getMethods(), object);
            }
            url = object.getClass().getName().split("\\.")[object.getClass().getName().split("\\.").length - 1];
            return excelImportEntity.getSaveUrl() + "/" + url.substring(0, url.lastIndexOf("Entity"));
        }
        return excelImportEntity.getSaveUrl();
    }

    //update-begin--Author:xuelin  Date:20171205 for:TASK #2098 【excel问题】 Online 一对多导入失败--------------------
    private <T> List<T> importExcel(Collection<T> result, Sheet sheet, Class<?> pojoClass, ImportParams params, Map<String, PictureData> pictures) throws Exception {
        List collection = new ArrayList();
        Map<String, ExcelImportEntity> excelParams = new HashMap<String, ExcelImportEntity>();
        List<ExcelCollectionParams> excelCollection = new ArrayList<ExcelCollectionParams>();
        String targetId = null;
        if (!Map.class.equals(pojoClass)) {
            Field fileds[] = PoiPublicUtil.getClassFields(pojoClass);
            ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);
            if (etarget != null) {
                targetId = etarget.value();
            }
            getAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null);
        }
        ignoreHeaderHandler(excelParams, params);
        Iterator<Row> rows = sheet.rowIterator();
        Map<Integer, String> titlemap = getTitleMap(sheet, rows, params, excelCollection);
        //update-begin-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex
        Set<String> keys = excelParams.keySet();
        for (String key : keys) {
            if (key.startsWith("FIXED_")) {
                String[] arr = key.split("_");
                titlemap.put(Integer.parseInt(arr[1]), key);
            }
        }
        //update-end-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex
        Set<Integer> columnIndexSet = titlemap.keySet();
        Integer maxColumnIndex = Collections.max(columnIndexSet);
        Integer minColumnIndex = Collections.min(columnIndexSet);
        Row row = null;
        //跳过表头和标题行
        for (int j = 0; j < params.getTitleRows() + params.getHeadRows(); j++) {
            row = rows.next();
        }
        Object object = null;
        String picId;
        while (rows.hasNext() && (row == null || sheet.getLastRowNum() - row.getRowNum() > params.getLastOfInvalidRow())) {
            row = rows.next();
            //update-begin--Author:xuelin  Date:20171017 for:TASK #2373 【bug】表改造问题,导致 3.7.1批量导入用户bug-导入不成功--------------------
            // 判断是集合元素还是不是集合元素,如果是就继续加入这个集合,不是就创建新的对象
            //update-begin--Author:xuelin  Date:20171206 for:TASK #2451 【excel导出bug】online 一对多导入成功, 但是现在代码生成后的一对多online导入有问题了
            Cell keyIndexCell = row.getCell(params.getKeyIndex());
            if (excelCollection.size() > 0 && StringUtils.isEmpty(getKeyValue(keyIndexCell)) && object != null && !Map.class.equals(pojoClass)) {
                //update-end--Author:xuelin  Date:20171206 for:TASK #2451 【excel导出bug】online 一对多导入成功, 但是现在代码生成后的一对多online导入有问题了
                for (ExcelCollectionParams param : excelCollection) {
                    addListContinue(object, param, row, titlemap, targetId, pictures, params);
                }

            } else {
                object = PoiPublicUtil.createObject(pojoClass, targetId);
                try {
                    //update-begin-author:taoyan date:20200303 for:导入图片
                    int firstCellNum = row.getFirstCellNum();
                    if (firstCellNum > minColumnIndex) {
                        firstCellNum = minColumnIndex;
                    }
                    int lastCellNum = row.getLastCellNum();
                    if (lastCellNum < maxColumnIndex + 1) {
                        lastCellNum = maxColumnIndex + 1;
                    }
                    for (int i = firstCellNum, le = lastCellNum; i < le; i++) {
                        Cell cell = row.getCell(i);
                        String titleString = (String) titlemap.get(i);
                        if (excelParams.containsKey(titleString) || Map.class.equals(pojoClass)) {
                            if (excelParams.get(titleString) != null && excelParams.get(titleString).getType() == 2) {
                                picId = row.getRowNum() + "_" + i;
                                saveImage(object, picId, excelParams, titleString, pictures, params);
                            } else {
                                if (params.getImageList() != null && params.getImageList().contains(titleString)) {
                                    if (pictures != null) {
                                        picId = row.getRowNum() + "_" + i;
                                        PictureData image = pictures.get(picId);
                                        if (image != null) {
                                            byte[] data = image.getData();
                                            params.getDataHanlder().setMapValue((Map) object, titleString, data);
                                        }
                                    }
                                } else {
                                    saveFieldValue(params, object, cell, excelParams, titleString, row);
                                }
                                //update-end-author:taoyan date:20200303 for:导入图片
                            }
                        }
                    }

                    for (ExcelCollectionParams param : excelCollection) {
                        addListContinue(object, param, row, titlemap, targetId, pictures, params);
                    }
                    //update-begin-author:taoyan date:20210526 for:autopoi导入excel 如果单元格被设置边框,即使没有内容也会被当做是一条数据导入 #2484
                    if (isNotNullObject(pojoClass, object)) {
                        collection.add(object);
                    }
                    //update-end-author:taoyan date:20210526 for:autopoi导入excel 如果单元格被设置边框,即使没有内容也会被当做是一条数据导入 #2484
                } catch (ExcelImportException e) {
                    if (!e.getType().equals(ExcelImportEnum.VERIFY_ERROR)) {
                        throw new ExcelImportException(e.getType(), e);
                    }
                }
            }
            //update-end--Author:xuelin  Date:20171017 for:TASK #2373 【bug】表改造问题,导致 3.7.1批量导入用户bug-导入不成功--------------------
        }
        return collection;
    }

    /**
     * 判断当前对象不是空
     * @param pojoClass
     * @param object
     * @return
     */
    private boolean isNotNullObject(Class pojoClass, Object object) {
        try {
            Method method = pojoClass.getMethod("isNullObject");
            if (method != null) {
                Object flag = method.invoke(object);
                if (flag != null && true == Boolean.parseBoolean(flag.toString())) {
                    return false;
                }
            }
        } catch (NoSuchMethodException e) {
            LOGGER.debug("未定义方法 isNullObject");
        } catch (IllegalAccessException e) {
            LOGGER.warn("没有权限访问该方法 isNullObject");
        } catch (InvocationTargetException e) {
            LOGGER.warn("方法调用失败 isNullObject");
        }
        return true;
    }

    /**
     * 获取忽略的表头信息
     * @param excelParams
     * @param params
     */
    private void ignoreHeaderHandler(Map<String, ExcelImportEntity> excelParams, ImportParams params) {
        List<String> ignoreList = new ArrayList<>();
        for (String key : excelParams.keySet()) {
            String temp = excelParams.get(key).getGroupName();
            if (temp != null && temp.length() > 0) {
                ignoreList.add(temp);
            }
        }
        params.setIgnoreHeaderList(ignoreList);
    }

    /**
     * 获取表格字段列名对应信息
     *
     * @param rows
     * @param params
     * @param excelCollection
     * @return
     */
    private Map<Integer, String> getTitleMap(Sheet sheet, Iterator<Row> rows, ImportParams params, List<ExcelCollectionParams> excelCollection) throws Exception {
        Map<Integer, String> titlemap = new HashMap<Integer, String>();
        Iterator<Cell> cellTitle = null;
        String collectionName = null;
        ExcelCollectionParams collectionParams = null;
        Row headRow = null;
        int headBegin = params.getTitleRows();
        //update_begin-author:taoyan date:2020622 for:当文件行数小于代码里设置的TitleRows时headRow一直为空就会出现死循环
        int allRowNum = sheet.getPhysicalNumberOfRows();
        //找到首行表头,每个sheet都必须至少有一行表头
        while (headRow == null && headBegin < allRowNum) {
            headRow = sheet.getRow(headBegin++);
        }
        if (headRow == null) {
            throw new Exception("不识别该文件");
        }
        //update-end-author:taoyan date:2020622 for:当文件行数小于代码里设置的TitleRows时headRow一直为空就会出现死循环

        //设置表头行数
        if (ExcelUtil.isMergedRegion(sheet, headRow.getRowNum(), 0)) {
            params.setHeadRows(2);
        } else {
            params.setHeadRows(1);
        }
        cellTitle = headRow.cellIterator();
        while (cellTitle.hasNext()) {
            Cell cell = cellTitle.next();
            String value = getKeyValue(cell);
            if (StringUtils.isNotEmpty(value)) {
                titlemap.put(cell.getColumnIndex(), value);//加入表头列表
            }
        }

        //多行表头
        for (int j = headBegin; j < headBegin + params.getHeadRows() - 1; j++) {
            headRow = sheet.getRow(j);
            cellTitle = headRow.cellIterator();
            while (cellTitle.hasNext()) {
                Cell cell = cellTitle.next();
                String value = getKeyValue(cell);
                if (StringUtils.isNotEmpty(value)) {
                    int columnIndex = cell.getColumnIndex();
                    //当前cell的上一行是否为合并单元格
                    if (ExcelUtil.isMergedRegion(sheet, cell.getRowIndex() - 1, columnIndex)) {
                        collectionName = ExcelUtil.getMergedRegionValue(sheet, cell.getRowIndex() - 1, columnIndex);
                        if (params.isIgnoreHeader(collectionName)) {
                            titlemap.put(cell.getColumnIndex(), value);
                        } else {
                            titlemap.put(cell.getColumnIndex(), collectionName + "_" + value);
                        }
                    } else {
                        //update-begin-author:taoyan date:20220112 for: JT640 【online】导入 无论一对一还是一对多 如果子表只有一个字段 则子表无数据
                        // 上一行不是合并的情况下另有一种特殊的场景: 如果当前单元格和上面的单元格同一列 即子表字段只有一个 所以标题没有出现跨列
                        String prefixTitle = titlemap.get(cell.getColumnIndex());
                        if (prefixTitle != null && !"".equals(prefixTitle)) {
                            titlemap.put(cell.getColumnIndex(), prefixTitle + "_" + value);
                        } else {
                            titlemap.put(cell.getColumnIndex(), value);
                        }
                        //update-end-author:taoyan date:20220112 for: JT640 【online】导入 无论一对一还是一对多 如果子表只有一个字段 则子表无数据
                    }
					/*int i = cell.getColumnIndex();
					// 用以支持重名导入
					if (titlemap.containsKey(i)) {
						collectionName = titlemap.get(i);
						collectionParams = getCollectionParams(excelCollection, collectionName);
						titlemap.put(i, collectionName + "_" + value);
					} else if (StringUtils.isNotEmpty(collectionName) && collectionParams.getExcelParams().containsKey(collectionName + "_" + value)) {
						titlemap.put(i, collectionName + "_" + value);
					} else {
						collectionName = null;
						collectionParams = null;
					}
					if (StringUtils.isEmpty(collectionName)) {
						titlemap.put(i, value);
					}*/
                }
            }
        }
        return titlemap;
    }
    //update-end--Author:xuelin  Date:20171205 for:TASK #2098 【excel问题】 Online 一对多导入失败--------------------

    /**
     * 获取这个名称对应的集合信息
     *
     * @param excelCollection
     * @param collectionName
     * @return
     */
    private ExcelCollectionParams getCollectionParams(List<ExcelCollectionParams> excelCollection, String collectionName) {
        for (ExcelCollectionParams excelCollectionParams : excelCollection) {
            if (collectionName.equals(excelCollectionParams.getExcelName())) {
                return excelCollectionParams;
            }
        }
        return null;
    }

    /**
     * Excel 导入 field 字段类型 Integer,Long,Double,Date,String,Boolean
     *
     * @param inputstream
     * @param pojoClass
     * @param params
     * @return
     * @throws Exception
     */
    public ExcelImportResult importExcelByIs(InputStream inputstream, Class<?> pojoClass, ImportParams params) throws Exception {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Excel import start ,class is {}", pojoClass);
        }
        List<T> result = new ArrayList<T>();
        Workbook book = null;
        OPCPackage opc = null;
        boolean isXSSFWorkbook = false;
        if (!(inputstream.markSupported())) {
            inputstream = new PushbackInputStream(inputstream, 8);
        }
        //begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------
        //------poi4.x begin----
//		FileMagic fm = FileMagic.valueOf(FileMagic.prepareToCheckMagic(inputstream));
//		if(FileMagic.OLE2 == fm){
//			isXSSFWorkbook=false;
//		}
        // 将输入流内容复制到字节数组
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = inputstream.read(buffer)) != -1) {
            byteArrayOutputStream.write(buffer, 0, bytesRead);
        }
        byte[] workbookData = byteArrayOutputStream.toByteArray();

        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(workbookData)) {
            // 创建Workbook,不会直接关闭原始输入流
            book = WorkbookFactory.create(byteArrayInputStream);
            if (book instanceof XSSFWorkbook) {
                isXSSFWorkbook = true;
                opc = PackageHelper.open(new ByteArrayInputStream(workbookData));
            }
        } finally {
            try {
                //之前操作自动关闭了输入流,由于输入流内容复制到字节数组没有进行关闭,所以这里进行关闭
                inputstream.close();
            } catch (IOException e) {
                LOGGER.info("Error closing input stream: " + e.getMessage());
            }
        }
//        book = WorkbookFactory.create(nonClosingInputStream);
//        if (book instanceof XSSFWorkbook) {
//            isXSSFWorkbook = true;
//            opc = PackageHelper.open(inputstream);
//        }
        LOGGER.info("  >>>  poi3升级到4.0兼容改造工作, isXSSFWorkbook = " + isXSSFWorkbook);
        //end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------

        //begin-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------
        //获取导入文本的sheet数
        //update-begin-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
        if (params.getSheetNum() == 0) {
            int sheetNum = book.getNumberOfSheets();
            if (sheetNum > 0) {
                params.setSheetNum(sheetNum);
            }
        }
        //update-end-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错
        //end-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------
        createErrorCellStyle(book);
        Map<String, PictureData> pictures;

        //update-begin-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数
        for (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()
                + params.getSheetNum(); i++) {
            //update-end-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(" start to read excel by is ,startTime is {}", System.currentTimeMillis());
            }
            //03版xls因为文件限制暂时无法识别内嵌图片
            //07版xlsx 此处以进行适配内嵌图片,内嵌和浮动自动识别返回
            if (isXSSFWorkbook) {
                pictures = PoiPublicUtil.getAllPictures(opc,(XSSFSheet) book.getSheetAt(i), (XSSFWorkbook) book);
//                pictures = PoiPublicUtil.getSheetPictrues07((XSSFSheet) book.getSheetAt(i), (XSSFWorkbook) book);
            } else {
                pictures = PoiPublicUtil.getSheetPictrues03((HSSFSheet) book.getSheetAt(i), (HSSFWorkbook) book);
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(" end to read excel by is ,endTime is {}", new Date().getTime());
            }
            result.addAll(importExcel(result, book.getSheetAt(i), pojoClass, params, pictures));
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(" end to read excel list by pos ,endTime is {}", new Date().getTime());
            }
        }
        if (params.isNeedSave()) {
            saveThisExcel(params, pojoClass, isXSSFWorkbook, book);
        }
        return new ExcelImportResult(result, verfiyFail, book);
    }

    /**
     *
     * @param is
     * @return
     * @throws IOException
     */
    public static byte[] getBytes(InputStream is) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();

        int len;
        byte[] data = new byte[100000];
        while ((len = is.read(data, 0, data.length)) != -1) {
            buffer.write(data, 0, len);
        }

        buffer.flush();
        return buffer.toByteArray();
    }

    /**
     * 保存字段值(获取值,校验值,追加错误信息)
     *
     * @param params
     * @param object
     * @param cell
     * @param excelParams
     * @param titleString
     * @param row
     * @throws Exception
     */
    private void saveFieldValue(ImportParams params, Object object, Cell cell, Map<String, ExcelImportEntity> excelParams, String titleString, Row row) throws Exception {
        Object value = cellValueServer.getValue(params.getDataHanlder(), object, cell, excelParams, titleString);
        if (object instanceof Map) {
            if (params.getDataHanlder() != null) {
                params.getDataHanlder().setMapValue((Map) object, titleString, value);
            } else {
                ((Map) object).put(titleString, value);
            }
        } else {
            ExcelVerifyHanlderResult verifyResult = verifyHandlerServer.verifyData(object, value, titleString, excelParams.get(titleString).getVerify(), params.getVerifyHanlder());
            if (verifyResult.isSuccess()) {
                setValues(excelParams.get(titleString), object, value);
            } else {
                Cell errorCell = row.createCell(row.getLastCellNum());
                errorCell.setCellValue(verifyResult.getMsg());
                errorCell.setCellStyle(errorCellStyle);
                verfiyFail = true;
                throw new ExcelImportException(ExcelImportEnum.VERIFY_ERROR);
            }
        }
    }

    /**
     *
     * @param object
     * @param picId
     * @param excelParams
     * @param titleString
     * @param pictures
     * @param params
     * @throws Exception
     */
    private void saveImage(Object object, String picId, Map<String, ExcelImportEntity> excelParams, String titleString, Map<String, PictureData> pictures, ImportParams params) throws Exception {
        if (pictures == null || pictures.get(picId) == null) {
            return;
        }
        PictureData image = pictures.get(picId);
        byte[] data = image.getData();
        String fileName = "pic" + Math.round(Math.random() * 100000000000L);
        fileName += "." + PoiPublicUtil.getFileExtendName(data);
        //update-beign-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159
        int saveType = excelParams.get(titleString).getSaveType();
        if (saveType == 1) {
            String path = PoiPublicUtil.getWebRootPath(getSaveUrl(excelParams.get(titleString), object));
            File savefile = new File(path);
            if (!savefile.exists()) {
                savefile.mkdirs();
            }
            savefile = new File(path + "/" + fileName);
            FileOutputStream fos = new FileOutputStream(savefile);
            fos.write(data);
            fos.close();
            setValues(excelParams.get(titleString), object, getSaveUrl(excelParams.get(titleString), object) + "/" + fileName);
        } else if (saveType == 2) {
            setValues(excelParams.get(titleString), object, data);
        } else {
            ImportFileServiceI importFileService = null;
            try {
                importFileService = ApplicationContextUtil.getContext().getBean(ImportFileServiceI.class);
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
            if (importFileService != null) {
                String dbPath = importFileService.doUpload(data);
                setValues(excelParams.get(titleString), object, dbPath);
            }
        }
        //update-end-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159
    }

    private void createErrorCellStyle(Workbook workbook) {
        errorCellStyle = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setColor(Font.COLOR_RED);
        errorCellStyle.setFont(font);
    }

}

2、PoiPublicUtil代码

/**
 * Copyright 2013-2015 JEECG (jeecgos@163.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jeecgframework.poi.util;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URISyntaxException;
import java.util.*;

import javax.imageio.ImageIO;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFPicture;
import org.apache.poi.hssf.usermodel.HSSFPictureData;
import org.apache.poi.hssf.usermodel.HSSFShape;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.ss.usermodel.PictureData;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFPicture;
import org.apache.poi.xssf.usermodel.XSSFShape;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecgframework.poi.excel.annotation.ExcelCollection;
import org.jeecgframework.poi.excel.annotation.ExcelEntity;
import org.jeecgframework.poi.excel.annotation.ExcelIgnore;
import org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants;
import org.jeecgframework.poi.word.entity.WordImageEntity;
import org.jeecgframework.poi.word.entity.params.ExcelListEntity;
import org.jeecgframework.poi.xml.SAXParserHandler;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;
import org.xml.sax.SAXException;

/**
 * AutoPoi 的公共基础类
 *
 * @author JEECG
 * @date 2015年4月5日 上午12:59:22
 */
public final class PoiPublicUtil {

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

	private PoiPublicUtil() {

	}

	@SuppressWarnings({ "unchecked" })
	public static <K, V> Map<K, V> mapFor(Object... mapping) {
		Map<K, V> map = new HashMap<K, V>();
		for (int i = 0; i < mapping.length; i += 2) {
			map.put((K) mapping[i], (V) mapping[i + 1]);
		}
		return map;
	}

	/**
	 * 彻底创建一个对象
	 *
	 * @param clazz
	 * @return
	 */
	public static Object createObject(Class<?> clazz, String targetId) {
		Object obj = null;
		Method setMethod;
		try {
			if (clazz.equals(Map.class)) {
				return new HashMap<String, Object>();
			}
			obj = clazz.newInstance();
			Field[] fields = getClassFields(clazz);
			for (Field field : fields) {
				if (isNotUserExcelUserThis(null, field, targetId)) {
					continue;
				}
				if (isCollection(field.getType())) {
					ExcelCollection collection = field.getAnnotation(ExcelCollection.class);
					setMethod = getMethod(field.getName(), clazz, field.getType());
					setMethod.invoke(obj, collection.type().newInstance());
				} else if (!isJavaClass(field)) {
					setMethod = getMethod(field.getName(), clazz, field.getType());
					setMethod.invoke(obj, createObject(field.getType(), targetId));
				}
			}

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new RuntimeException("创建对象异常");
		}
		return obj;

	}

	/**
	 * 获取class的 包括父类的
	 *
	 * @param clazz
	 * @return
	 */
	public static Field[] getClassFields(Class<?> clazz) {
		List<Field> list = new ArrayList<Field>();
		Field[] fields;
		do {
			fields = clazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				list.add(fields[i]);
			}
			clazz = clazz.getSuperclass();
		} while (clazz != Object.class && clazz != null);
		return list.toArray(fields);
	}

	/**
	 * @param photoByte
	 * @return
	 */
	public static String getFileExtendName(byte[] photoByte) {
		String strFileExtendName = "JPG";
		if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
			strFileExtendName = "GIF";
		} else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
			strFileExtendName = "JPG";
		} else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
			strFileExtendName = "BMP";
		} else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
			strFileExtendName = "PNG";
		}
		return strFileExtendName;
	}

	/**
	 * 获取GET方法
	 *
	 * @param name
	 * @param pojoClass
	 * @return
	 * @throws Exception
	 */
	public static Method getMethod(String name, Class<?> pojoClass) throws Exception {
		StringBuffer getMethodName = new StringBuffer(PoiBaseConstants.GET);
		getMethodName.append(name.substring(0, 1).toUpperCase());
		getMethodName.append(name.substring(1));
		Method method = null;
		try {
			method = pojoClass.getMethod(getMethodName.toString(), new Class[] {});
		} catch (Exception e) {
			method = pojoClass.getMethod(getMethodName.toString().replace(PoiBaseConstants.GET, PoiBaseConstants.IS), new Class[] {});
		}
		return method;
	}

	/**
	 * 获取SET方法
	 *
	 * @param name
	 * @param pojoClass
	 * @param type
	 * @return
	 * @throws Exception
	 */
	public static Method getMethod(String name, Class<?> pojoClass, Class<?> type) throws Exception {
		StringBuffer getMethodName = new StringBuffer(PoiBaseConstants.SET);
		getMethodName.append(name.substring(0, 1).toUpperCase());
		getMethodName.append(name.substring(1));
		return pojoClass.getMethod(getMethodName.toString(), new Class[] { type });
	}

	//update-begin-author:taoyan date:20180615 for:TASK #2798 导入扩展方法,支持自定义导入字段转换规则
	/**
	 * 获取get方法 通过EXCEL注解exportConvert判断是否支持值的转换
	 * @param name
	 * @param pojoClass
	 * @param convert
	 * @return
	 * @throws Exception
	 */
	public static Method getMethod(String name, Class<?> pojoClass,boolean convert) throws Exception {
		StringBuffer getMethodName = new StringBuffer();
		if(convert){
			getMethodName.append(PoiBaseConstants.CONVERT);
		}
		getMethodName.append(PoiBaseConstants.GET);
		getMethodName.append(name.substring(0, 1).toUpperCase());
		getMethodName.append(name.substring(1));
		Method method = null;
		try {
			method = pojoClass.getMethod(getMethodName.toString(), new Class[] {});
		} catch (Exception e) {
			method = pojoClass.getMethod(getMethodName.toString().replace(PoiBaseConstants.GET, PoiBaseConstants.IS), new Class[] {});
		}
		return method;
	}

	/**
	 * 获取set方法  通过EXCEL注解importConvert判断是否支持值的转换
	 * @param name
	 * @param pojoClass
	 * @param type
	 * @param convert
	 * @return
	 * @throws Exception
	 */
	public static Method getMethod(String name, Class<?> pojoClass, Class<?> type,boolean convert) throws Exception {
		StringBuffer setMethodName = new StringBuffer();
		if(convert){
			setMethodName.append(PoiBaseConstants.CONVERT);
		}
		setMethodName.append(PoiBaseConstants.SET);
		setMethodName.append(name.substring(0, 1).toUpperCase());
		setMethodName.append(name.substring(1));
		return pojoClass.getMethod(setMethodName.toString(), new Class[] { type });
	}
	//update-end-author:taoyan date:20180615 for:TASK #2798 导入扩展方法,支持自定义导入字段转换规则

	/**
	 * 获取Excel2003图片
	 *
	 * @param sheet
	 *            当前sheet对象
	 * @param workbook
	 *            工作簿对象
	 * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
	 */
	public static Map<String, PictureData> getSheetPictrues03(HSSFSheet sheet, HSSFWorkbook workbook) {
		Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
		List<HSSFPictureData> pictures = workbook.getAllPictures();
		if (!pictures.isEmpty()) {
			for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) {
				HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
				if (shape instanceof HSSFPicture) {
					HSSFPicture pic = (HSSFPicture) shape;
					int pictureIndex = pic.getPictureIndex() - 1;
					HSSFPictureData picData = pictures.get(pictureIndex);
					String picIndex = String.valueOf(anchor.getRow1()) + "_" + String.valueOf(anchor.getCol1());
					sheetIndexPicMap.put(picIndex, picData);
				}
			}
			return sheetIndexPicMap;
		} else {
			return null;
		}
	}

	/**
	 * 获取Excel2007图片
	 *
	 * @param sheet
	 *            当前sheet对象
	 * @param workbook
	 *            工作簿对象
	 * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
	 */
	public static Map<String, PictureData> getSheetPictrues07(XSSFSheet sheet, XSSFWorkbook workbook) {
		Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();
		for (POIXMLDocumentPart dr : sheet.getRelations()) {
			if (dr instanceof XSSFDrawing) {
				XSSFDrawing drawing = (XSSFDrawing) dr;
				List<XSSFShape> shapes = drawing.getShapes();
				for (XSSFShape shape : shapes) {
					XSSFPicture pic = (XSSFPicture) shape;
					XSSFClientAnchor anchor = pic.getPreferredSize();
					CTMarker ctMarker = anchor.getFrom();
					String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol();
					sheetIndexPicMap.put(picIndex, pic.getPictureData());
				}
			}
		}
		return sheetIndexPicMap;
	}

	public static Map<String, PictureData> getAllPictures(OPCPackage opc, XSSFSheet sheet, XSSFWorkbook workbook) throws Exception {
		Map<String, PictureData> sheetIndexPicMap = new HashMap<>();
		// 处理嵌入式图片
		List<PackagePart> parts = opc.getParts();
		sheetIndexPicMap.putAll(getEmbedPictures(parts));
		// 处理浮动式图片
		sheetIndexPicMap.putAll(getSheetPictrues07(sheet, workbook));
		return sheetIndexPicMap;
	}

	public static String getWebRootPath(String filePath) {
		try {
			String path = null;
			try {
				path = PoiPublicUtil.class.getClassLoader().getResource("").toURI().getPath();
			} catch (URISyntaxException e) {
				//e.printStackTrace();
			//update-begin-author:taoyan date:20211116 for: JAR包分离 发布出空指针 https://gitee.com/jeecg/jeecg-boot/issues/I4CMHK
			}catch (NullPointerException e) {
				path =  PoiPublicUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();
			}
			//update-end-author:taoyan date:20211116 for: JAR包分离 发布出空指针 https://gitee.com/jeecg/jeecg-boot/issues/I4CMHK
			//update-begin--Author:zhangdaihao  Date:20190424 for:解决springboot 启动模式,上传路径获取为空问题---------------------
			if (path == null || path == "") {
				//解决springboot 启动模式,上传路径获取为空问题
				path = ClassUtils.getDefaultClassLoader().getResource("").getPath();
			}
			//update-end--Author:zhangdaihao  Date:20190424 for:解决springboot 启动模式,上传路径获取为空问题----------------------
			LOGGER.debug("--- getWebRootPath ----filePath--- " + path);
			path = path.replace("WEB-INF/classes/", "");
			path = path.replace("file:/", "");
			LOGGER.debug("--- path---  " + path);
			LOGGER.debug("--- filePath---  " + filePath);
			return path + filePath;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 判断是不是集合的实现类
	 *
	 * @param clazz
	 * @return
	 */
	public static boolean isCollection(Class<?> clazz) {
		return Collection.class.isAssignableFrom(clazz);
	}

	/**
	 * 是不是java基础类
	 *
	 * @param field
	 * @return
	 */
	public static boolean isJavaClass(Field field) {
		Class<?> fieldType = field.getType();
		boolean isBaseClass = false;
		if (fieldType.isArray()) {
			isBaseClass = false;
		} else if (fieldType.isPrimitive() || fieldType.getPackage() == null || fieldType.getPackage().getName().equals("java.lang") || fieldType.getPackage().getName().equals("java.math") || fieldType.getPackage().getName().equals("java.sql") || fieldType.getPackage().getName().equals("java.util")) {
			isBaseClass = true;
		}
		return isBaseClass;
	}

	/**
	 * 判断是否不要在这个excel操作中
	 *
	 * @param
	 * @param field
	 * @param targetId
	 * @return
	 */
	public static boolean isNotUserExcelUserThis(List<String> exclusionsList, Field field, String targetId) {
		boolean boo = true;
		if (field.getAnnotation(ExcelIgnore.class) != null) {
			boo = true;
		} else if (boo && field.getAnnotation(ExcelCollection.class) != null && isUseInThis(field.getAnnotation(ExcelCollection.class).name(), targetId) && (exclusionsList == null || !exclusionsList.contains(field.getAnnotation(ExcelCollection.class).name()))) {
			boo = false;
		} else if (boo && field.getAnnotation(Excel.class) != null && isUseInThis(field.getAnnotation(Excel.class).name(), targetId) && (exclusionsList == null || !exclusionsList.contains(field.getAnnotation(Excel.class).name()))) {
			boo = false;
		} else if (boo && field.getAnnotation(ExcelEntity.class) != null && isUseInThis(field.getAnnotation(ExcelEntity.class).name(), targetId) && (exclusionsList == null || !exclusionsList.contains(field.getAnnotation(ExcelEntity.class).name()))) {
			boo = false;
		}
		return boo;
	}

	/**
	 * 判断是不是使用
	 *
	 * @param exportName
	 * @param targetId
	 * @return
	 */
	private static boolean isUseInThis(String exportName, String targetId) {
		return targetId == null || exportName.equals("") || exportName.indexOf("_") < 0 || exportName.indexOf(targetId) != -1;
	}

	private static Integer getImageType(String type) {
		if (type.equalsIgnoreCase("JPG") || type.equalsIgnoreCase("JPEG")) {
			return XWPFDocument.PICTURE_TYPE_JPEG;
		}
		if (type.equalsIgnoreCase("GIF")) {
			return XWPFDocument.PICTURE_TYPE_GIF;
		}
		if (type.equalsIgnoreCase("BMP")) {
			return XWPFDocument.PICTURE_TYPE_GIF;
		}
		if (type.equalsIgnoreCase("PNG")) {
			return XWPFDocument.PICTURE_TYPE_PNG;
		}
		return XWPFDocument.PICTURE_TYPE_JPEG;
	}

	/**
	 * 返回流和图片类型
	 *
	 * @Author JEECG
	 * @date 2013-11-20
	 * @param entity
	 * @return (byte[]) isAndType[0],(Integer)isAndType[1]
	 * @throws Exception
	 */
	public static Object[] getIsAndType(WordImageEntity entity) throws Exception {
		Object[] result = new Object[2];
		String type;
		if (entity.getType().equals(WordImageEntity.URL)) {
			ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
			BufferedImage bufferImg;
			String path = Thread.currentThread().getContextClassLoader().getResource("").toURI().getPath() + entity.getUrl();
			path = path.replace("WEB-INF/classes/", "");
			path = path.replace("file:/", "");
			bufferImg = ImageIO.read(new File(path));
			//update-begin-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR
			ImageIO.write(bufferImg, entity.getUrl().substring(entity.getUrl().lastIndexOf(".") + 1, entity.getUrl().length()), byteArrayOut);
			//update-end-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR
			result[0] = byteArrayOut.toByteArray();
			type = entity.getUrl().split("/.")[entity.getUrl().split("/.").length - 1];
		} else {
			result[0] = entity.getData();
			type = PoiPublicUtil.getFileExtendName(entity.getData());
		}
		result[1] = getImageType(type);
		return result;
	}

	/**
	 * 获取参数值
	 *
	 * @param params
	 * @param map
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static Object getParamsValue(String params, Object object) throws Exception {
		if (params.indexOf(".") != -1) {
			String[] paramsArr = params.split("\\.");
			return getValueDoWhile(object, paramsArr, 0);
		}
		if (object instanceof Map) {
			return ((Map) object).get(params);
		}
		return getMethod(params, object.getClass()).invoke(object, new Object[] {});
	}

	/**
	 * 解析数据
	 *
	 * @Author JEECG
	 * @date 2013-11-16
	 * @return
	 */
	public static Object getRealValue(String currentText, Map<String, Object> map) throws Exception {
		String params = "";
		while (currentText.indexOf("{{") != -1) {
			params = currentText.substring(currentText.indexOf("{{") + 2, currentText.indexOf("}}"));
			Object obj = getParamsValue(params.trim(), map);
			// 判断图片或者是集合
			// update-begin-author:taoyan date:20210914 for:autopoi模板导出,赋值的方法建议增加空判断或抛出异常说明。 /issues/3005
			if(obj==null){
				obj = "";
			}
			// update-end-author:taoyan date:20210914 for:autopoi模板导出,赋值的方法建议增加空判断或抛出异常说明。/issues/3005
			if (obj instanceof WordImageEntity || obj instanceof List || obj instanceof ExcelListEntity) {
				return obj;
			} else {
				currentText = currentText.replace("{{" + params + "}}", obj.toString());
			}
		}
		return currentText;
	}

	/**
	 * 通过遍历过去对象值
	 *
	 * @param object
	 * @param paramsArr
	 * @param index
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	public static Object getValueDoWhile(Object object, String[] paramsArr, int index) throws Exception {
		if (object == null) {
			return "";
		}
		if (object instanceof WordImageEntity) {
			return object;
		}
		if (object instanceof Map) {
			object = ((Map) object).get(paramsArr[index]);
		} else {
			object = getMethod(paramsArr[index], object.getClass()).invoke(object, new Object[] {});
		}
		return (index == paramsArr.length - 1) ? (object == null ? "" : object) : getValueDoWhile(object, paramsArr, ++index);
	}

	/**
	 * double to String 防止科学计数法
	 *
	 * @param value
	 * @return
	 */
	public static String doubleToString(Double value) {
		String temp = value.toString();
		if (temp.contains("E")) {
			BigDecimal bigDecimal = new BigDecimal(temp);
			temp = bigDecimal.toPlainString();
		}
		//---update-begin-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------
		return ExcelUtil.remove0Suffix(temp);
		//---update-end-----autor:scott------date:20191016-------for:excel导入数字类型,去掉后缀.0------
	}

	/**
	 * 判断是否是数值类型
	 * @param xclass
	 * @return
	 */
	public static boolean isNumber(String xclass){
		if(xclass==null){
			return false;
		}
		String temp = xclass.toLowerCase();
		if(temp.indexOf("int")>=0 || temp.indexOf("double")>=0 || temp.indexOf("decimal")>=0){
			return true;
		}
		return false;
	}
	//update-begin---author:liusq  Date:20211217  for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】----
	/**
	 * 统一 key的获取规则
	 * @param key
	 * @param targetId
	 * @date  2022年1月4号
	 * @return
	 */
	public static String getValueByTargetId(String key, String targetId, String defalut) {
		if (StringUtils.isEmpty(targetId) || key.indexOf("_") < 0) {
			return key;
		}
		String[] arr = key.split(",");
		String[] tempArr;
		for (String str : arr) {
			tempArr = str.split("_");
			if (tempArr == null || tempArr.length < 2) {
				return defalut;
			}
			if (targetId.equals(tempArr[1])) {
				return tempArr[0];
			}
		}
		return defalut;
	}
	//update-end---author:liusq  Date:20211217  for:[LOWCOD-2521]【autopoi】大数据导出方法【全局】----

	/**
	 * 将PackagePart转换为PictureData
	 * @param packagePart
	 * @param workbook
	 * @return
	 */
	public static PictureData convertPackagePartToPictureData(PackagePart packagePart, XSSFWorkbook workbook) {
		if (packagePart == null) {
			throw new IllegalArgumentException("PackagePart cannot be null");
		}
		if (workbook == null) {
			throw new IllegalArgumentException("Workbook cannot be null");
		}

		try (InputStream inputStream = packagePart.getInputStream()) {
			// 从PackagePart读取数据
			byte[] data = IOUtils.toByteArray(inputStream);

			// 确定内容,创建PictureType
			String contentType = packagePart.getContentType();
			// 默认PNG
			int pictureType = XSSFWorkbook.PICTURE_TYPE_PNG;
			if (contentType.equals("image/jpeg")) {
				pictureType = XSSFWorkbook.PICTURE_TYPE_JPEG;
			} else if (contentType.equals("image/png")) {
				pictureType = XSSFWorkbook.PICTURE_TYPE_PNG;
			}

			// 将图片数据添加到工作簿
			int pictureIndex = workbook.addPicture(data, pictureType);
			return workbook.getAllPictures().get(pictureIndex);
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 获取嵌入式图片
	 * @param parts
	 * @return
	 * @throws JDOMException
	 * @throws IOException
	 * @throws ParserConfigurationException
	 */
	private static Map<String, PictureData> getEmbedPictures(List<PackagePart> parts) throws JDOMException, IOException, ParserConfigurationException, SAXException {
		Map<String, Set<String>> mapImg = new HashMap<>();
		Map<String, String> mapImgPath = new HashMap<>();
		Map<Integer, List<String>> dataMap = new HashMap<>();

		for (PackagePart part : parts) {
//            System.out.println(part.getPartName());
			PackagePartName partName = part.getPartName();
			String name = partName.getName();
			if ("/xl/cellimages.xml".equals(name)) {
				SAXBuilder builder = new SAXBuilder();
				// 获取文档
				Document doc = builder.build(part.getInputStream());
				// 获取根节点
				Element root = doc.getRootElement();
				List<Element> cellImageList = root.getChildren();
				for (Element imgEle : cellImageList) {
					Element xdrPic = imgEle.getChildren().get(0);
					Element xdrNvPicPr = xdrPic.getChildren().get(0);
					Element xdrBlipFill = xdrPic.getChildren().get(1);
					Element aBlip = xdrBlipFill.getChildren().get(0);
					Attribute attr = aBlip.getAttributes().get(0);
					String imgId = xdrNvPicPr.getChildren().get(0).getAttributeValue("name");
					String id = attr.getValue();
					if (mapImg.containsKey(id)) {
						mapImg.get(id).add(imgId);
					} else {
						Set<String> set = new HashSet<>();
						set.add(imgId);
						mapImg.put(id, set);
					}
				}
			}

			if ("/xl/_rels/cellimages.xml.rels".equals(name)) {
				SAXBuilder builder = new SAXBuilder();
				// 获取文档
				Document doc = builder.build(part.getInputStream());
				// 获取根节点
				Element root = doc.getRootElement();
				List<Element> relationshipList = root.getChildren();
				for (Element relationship : relationshipList) {
					String id = relationship.getAttributeValue("Id");
					String target = relationship.getAttributeValue("Target");
					mapImgPath.put(id, target);
				}
			}

			if (name.contains("/xl/worksheets/sheet")) {
				// 获取文档
				String sheetNoStr = name.replace("/xl/worksheets/sheet", "").replace(".xml", "");
				Integer sheetNo = Integer.valueOf(sheetNoStr) - 1;
				// 步骤1:创建SAXParserFactory实例
				SAXParserFactory factory = SAXParserFactory.newInstance();
				// 步骤2:创建SAXParser实例
				SAXParser saxParser = factory.newSAXParser();
				SAXParserHandler saxParserHandler = new SAXParserHandler();
				saxParser.parse(part.getInputStream(), saxParserHandler);

				List<String> rows = saxParserHandler.getRows();

				dataMap.put(sheetNo, rows);
			}

		}

		Map<String, String> imgMap = new HashMap<>();
		for (String id : mapImg.keySet()) {
			Set<String> imgIds = mapImg.get(id);
			String path = mapImgPath.get(id);
			for (String imgId : imgIds) {
				imgMap.put(imgId, path);
			}
		}
		for (Integer key : dataMap.keySet()) {
			List<String> rows = dataMap.get(key);
			for (int i = 0; i < rows.size(); i++) {
				String imgId = rows.get(i);
				String imgId_index = imgId.substring(0, imgId.indexOf(":"));
				String imgId_url = imgId.substring(imgId.indexOf(":")+1);
				if (imgMap.containsKey(imgId_url)) {
					rows.set(i, imgId_index+":"+imgMap.get(imgId_url));
				}
			}
		}

		Map<String, PictureData> map = new HashMap<>();
		for (Integer key : dataMap.keySet()) {
			List<String> pathList = dataMap.get(key);
			for (int i = 0; i < pathList.size(); i++) {
				String path = pathList.get(i);
				String path_index = path.substring(0, path.indexOf(":"));
				String path_url = path.substring(path.indexOf(":")+1);
				if (StringUtils.isNotEmpty(path_url)) {
					for (PackagePart part : parts) {
						PackagePartName partName = part.getPartName();
						String name = partName.getName();
						if (name.contains(path_url)) {
							//进行转换
							PictureData pictureData = convertPackagePartToPictureData(part,new XSSFWorkbook());
							map.put(path_index, pictureData);
							break;
						}
					}
				}
			}
		}
		return map;
	}

}

3、SAXParserHandler代码

package org.jeecgframework.poi.xml;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.List;

//xml解析
public class SAXParserHandler extends DefaultHandler {

    String value = null;

    List<String> rows = new ArrayList<>();

    int rowIndex = 0;

    int currentColumn = 0;

    public List<String> getRows() {
        return rows;
    }

    //用来标识解析开始
    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
    }

    //用来标识解析结束
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }

    //解析xml元素
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        if (qName.equals("row")) {
            value = "";
            // 遇到新的行时,重置列号为0
            currentColumn = 0;
        } else if (qName.equals("c")) {
            // 获取列号
            String columnRef = attributes.getValue("r");
            if (columnRef != null) {
                currentColumn = parseColumnIndex(columnRef.replaceAll("[^A-Z]", ""));
            }
        }

    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if (qName.equals("c")) {
            if (value != null && value.contains("DISPIMG")) {
                value = value.substring(value.lastIndexOf("DISPIMG(")).replace("DISPIMG(\"", "");
                value = value.substring(0, value.indexOf("\""));
//                rows.add(rowIndex, value);
                rows.add(rowIndex + "_" + currentColumn + ":" + value);
            } else {
//                rows.add(rowIndex, null);
                rows.add(rowIndex + "_" + currentColumn + ":null");
            }
            value = "";
        }else if (qName.equals("row")) {
            rowIndex++;
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        value += new String(ch, start, length);
    }

    // 解析列号
    private int parseColumnIndex(String columnRef) {
        int result = 0;
        for (int i = 0; i < columnRef.length(); i++) {
            result = result * 26 + (columnRef.charAt(i) - 'A' + 1);
        }
        return result - 1;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1860611.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法设计与分析:并查集法求图论桥问题

目录 一、实验目的 二、问题描述 三、实验要求 四、算法思想 1. 基准算法 1.1 算法思想 1.2 代码 1.3 时间复杂度 2. 使用并查集的高效算法 2.1 算法思想 2.2 代码&#xff1a; 2.3 时间复杂度&#xff1a; 五、实验结果 一、实验目的 1. 掌握图的连通性。 2. 掌…

《代码大模型安全风险防范能力要求及评估方法》正式发布

​代码大模型在代码生成、代码翻译、代码补全、错误定位与修复、自动化测试等方面为研发人员带来了极大便利的同时&#xff0c;也带来了对安全风险防范能力的挑战。基于此&#xff0c;中国信通院依托中国人工智能产业发展联盟&#xff08;AIIA&#xff09;&#xff0c;联合开源…

干货分享 | TSMaster 的 CAN UDS 诊断操作指南(下)

上期&#xff0c;我们主要介绍了 UDS 诊断模块的创建以及TSMaster 基础诊断配置。很多客户表示意犹未尽。因此我们将继续带来《TSMaster 的 CAN UDS 诊断操作指南&#xff08;下&#xff09;》的精彩内容&#xff0c;为您带来UDS on CAN/CAN FD 的功能以及详细的使用操作。 本文…

深入浅出:npm常用命令详解与实践

简介 在现代的软件开发中&#xff0c;特别是在 JavaScript 生态系统中&#xff0c;npm&#xff08;Node Package Manager&#xff09;是一个核心工具。它不仅仅是 Node.js 的包管理器&#xff0c;还扮演着项目依赖管理、脚本执行、项目发布等多重角色。理解 npm 的常用命令不仅…

【数据结构】线性表之《队列》超详细实现

队列 一.队列的概念及结构二.顺序队列与链队列1.顺序队列2.链队列 三.链队列的实现1.创建队列2.初始化队列3.入队4.出队5.获取队头元素6.获取队尾元素7.队列的大小8.队列的判空9.清空队列10.销毁队列 四.队列的盲区五.模块化源代码1.Queue.h2.Queue.c3.test.c 六.栈和队列必做O…

小白上手AIGC-基于FC部署stable-diffusion

AIGC AIGC&#xff08;人工智能创造内容&#xff09;作为一种基于人工智能技术生成内容的新型创作模式。打破了过去大家对于AI的理解都是说只能涉足部分领域而无法涉足艺术或者是其他的创作领域的定律&#xff0c;现在的AIGC也能够创作内容了&#xff0c;而不再只是单纯的返回…

MAC Address

文章目录 1. 前言2. MAC Address2.1 MAC 地址格式2.2 Locally Administered MAC Address2.3 MAC 单播 和 多播 3. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. MAC Address 2.1 MA…

Java集合框架深度解析:Hashtable、HashMap与TreeMap的较量,哪个更适合你的项目?

引言 在Java编程世界中&#xff0c;集合是编程语言的重要组成部分。它们负责存储、组织和操作数据集合&#xff0c;是开发过程中不可或缺的工具。Java集合框架提供了丰富且功能强大的数据结构&#xff0c;而其中的Hashtable、HashMap和TreeMap是使用频率极高的三种实现。 本篇文…

2024年希望杯数学竞赛各年级100道练习题及答案

链接里面有无答案版本链接&#xff1a;https://pan.baidu.com/s/1nTIVJrTEWUzb0LJNo4mI_Q 提取码&#xff1a;0548 –来自百度网盘超级会员V7的分享 一年级 二年级 三年级 四年级 五年级 六年级 七年级 八年级

如何使得Macos的剪切板感知fileURL并当fileURL被执行paste 动作时 回调到某个监听的函数 从而来填充file content

问题及尝试&#xff1a; 我在做一个跨平台文件拷贝的功能&#xff0c;文件可能是从其他操作系统比如Linux 或者Windows 拷贝到Macos上&#xff0c; 但是我试过所有可以hook NSPasteboard的方法&#xff0c;确实没有找到可以监听macos 剪切板的方法&#xff0c;因为fileURL 确实…

网络设备框架

文章目录 前言一、主要流程二、Linux网络设备驱动架构1.概述2.读入数据 总结 前言 Linux中的Ethernet驱动框架涉及到网络设备驱动程序的多个方面&#xff0c;包括初始化、注册、数据传输以及与物理层&#xff08;PHY&#xff09;的交互。以下是网络设备驱动架构的概述&#xf…

Centos7虚拟机

Centos 7 安装 1 镜像下载1.1 官网下载1.2 阿里云镜像下载 2 环境的安装2.1 打开我们的虚拟机&#xff0c;点击文件进行新建2.2 选择典型之后&#xff0c;下一步2.3 选择稍会安装操作系统2.4 勾选Linux&#xff0c;并且选择CentOS 7的版本2.5 设定我们虚拟机的名称和安装位置2.…

VOSviewer分析知网文献

VOSviewer简介 VOSviewer 是一款用于构建和可视化科学文献计量网络的软件工具。它能够帮助用户分析和可视化期刊、研究人员或单个出版物之间的关系&#xff0c;这些关系可以基于引用、共引、共著或术语共现关系来构建。VOSviewer 还提供了文本挖掘功能&#xff0c;可以用来构建…

Python 爬虫从入门到入狱之路一

实际上爬虫一共就四个主要步骤&#xff1a; 明确目标 (要知道你准备在哪个范围或者网站去搜索)爬 (将所有的网站的内容全部爬下来)取 (去掉对我们没用处的数据)处理数据&#xff08;按照我们想要的方式存储和使用&#xff09; 我们在之前写的爬虫程序中&#xff0c;都只是获取…

fidder自动测试cookie脚本

前言 工作在使用fidder抓包时&#xff0c;经常需要找到一个请求携带的cookie中&#xff0c;真正校验了那些cookie&#xff0c;从而在代码中实现写入这些cookie的请求。这个过程除了根据经验快速过滤&#xff0c;就只能一个一个删除测试了。 所以我写了这个脚本&#xff0c;自动…

阿里云云服务器、ACR镜像服务、容器化实战:搭建企业应用

一、容器化基础知识 华为云免费试用服务器&#xff1a;https://activity.huaweicloud.com/free_test/index.html 阿里云docker容器教程&#xff1a;https://edu.aliyun.com/course/3111900/lesson/341807097 查询ip地址&#xff1a;www.ip138.com 二、容器化搭建企业应用实战 2…

如何选择和优化谷歌外贸关键词?

长尾关键词是关键&#xff0c;长尾关键词是指由三个或更多词组成的更具体、更详细的搜索词组。与单个关键词相比&#xff0c;长尾关键词虽然搜索量较低&#xff0c;但往往能带来更高的转化率&#xff0c;因为它们更能精准地反映用户的搜索意图和需求 使用长尾关键词有几个优势…

海南云亿商务咨询有限公司抖音带货怎么样?

在数字化浪潮席卷全球的今天&#xff0c;电商行业正迎来前所未有的发展机遇。特别是短视频平台如抖音的崛起&#xff0c;更是为电商行业注入了新的活力。海南云亿商务咨询有限公司&#xff0c;作为抖音电商服务的佼佼者&#xff0c;凭借其专业的团队和卓越的服务&#xff0c;助…

北邮《计算机网络》蒋老师思考题及答案-传输层

蒋yj老师yyds&#xff01; 答案自制&#xff0c;仅供参考&#xff0c;欢迎质疑讨论 问题一览 传输层思考题P2P和E2E的区别使用socket的c/s模式通信&#xff0c;流控如何反映到编程模型三次握手解决什么问题举一个两次握手失败的例子为什么链路层是两次握手而非三次&#xff1f;…

HTML(24)——过渡

过渡 作用&#xff1a;可以为一个元素在不同的状态之间切换的时候添加过渡效果 属性名&#xff1a;transition(复合属性) 属性值&#xff1a;过渡的属性 花费时间(s) 提示&#xff1a; 过渡的属性可以是具体的CSS属性也可以为all&#xff08;两个状态属性值不同的所有属性…