POI 导入导出实践

根据一个朋友的需求,其需要处理一个 Excel 模板,把其中某一列的数据进行降序排序,并增加一列存放某两列的乘积

经过一段时间的折腾,基本功能算实现完了。发现对于我来说,难点在于单元格的合并。
1)排序后,前面的序号乱了,需写逻辑处理序号并合并相应单元格
2)对于excel内容的合并

处理前:
在这里插入图片描述
处理后(原料含量排序、增加一列实际含量,实际含量=原料含量*原料成分含量):
在这里插入图片描述

完整代码如下:
创建一个 springboot 项目
建表(表字段见实体类或mapper类)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.xiao</groupId>
	<artifactId>handerPF</artifactId>
	<version>xiao.1.0</version>
	<name>handerPF</name>
	<description>handerPFexcel</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<dependency>
		    <groupId>org.mybatis.spring.boot</groupId>
		    <artifactId>mybatis-spring-boot-starter</artifactId>
		    <version>2.1.4</version>
		</dependency>
		<dependency>
	  		<groupId>org.springframework.boot</groupId>
	  		<artifactId>spring-boot-starter-jdbc</artifactId>
	  	</dependency>
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
		    <groupId>org.apache.poi</groupId>
		    <artifactId>poi</artifactId>
		    <version>3.16</version>
		</dependency>
		<dependency>
		    <groupId>org.apache.poi</groupId>
		    <artifactId>poi-ooxml</artifactId>
		    <version>3.16</version>
		</dependency>
		<dependency>
		    <groupId>net.sf.json-lib</groupId>
		    <artifactId>json-lib</artifactId>
		    <version>2.4</version>
		    <classifier>jdk15</classifier>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

application.properties

server.port=8080

## 配置数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/xiao_database202105?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
#spring.datasource.password=mysql8

## 配置数据源
#spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
#spring.datasource.url=jdbc:oracle:thin:@localhost:1521:ORCL
#spring.datasource.username=SCOTT
#spring.datasource.password=xiao

# mybatis 全局配置/主配置文件
mybatis.config-location=classpath:mybatis/mybatis-config.xml
# mybatis mapper映射文件
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

logging.file.name=logs/handlePF.log

三个实体类

public class Pffile {
    
    
	private String id;
	private String fjmc;
	private Date cjsj;
	private String pfmc;
	private String pfxh;
	
	//getter、setter方法
}
public class Ylinfo {
    
    
	private String id;
	private int xh;
	private String ylmc;
	private String inci;
	private BigDecimal ylhl;
	private BigDecimal ylcfhl;
	private BigDecimal ylsjhl;
	private String symd;
	private String bz;
	private String pfid;
	private int ylxh;
	
	private String pfmc; // 仅传值使用

	//getter、setter方法
}
// 处理表格时使用
public class PoiModel {
    
    
    //内容
    private Object content;
    //行标
    private int rowIndex;
    //列标
    private int cellIndex;
    //标记
    public int flag;
    
    //getter、setter方法
}

mapper.xml

<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xiao.dao.PffileMapper">
	<sql id="columns">
		ID,FJMC,CJSJ,PFMC,PFXH
	</sql>
	
	<select id="queryPffile" resultType="com.xiao.entity.Pffile" parameterType="java.lang.String">
		select <include refid="columns"/>
		from XIAO_PFFILE_INFO
		where FJMC = #{fjmc, jdbcType=VARCHAR}
		order by PFXH ASC
	</select>	

	<insert id="insertPffile" parameterType="com.xiao.entity.Pffile">
		insert into XIAO_PFFILE_INFO 
		values(#{id},#{fjmc},#{cjsj},#{pfmc},#{pfxh})
	</insert>
	
	<delete id="deletePffileByFjmc" parameterType="java.lang.String">
		DELETE FROM XIAO_PFFILE_INFO WHERE FJMC = #{fjmc, jdbcType=VARCHAR}
	</delete>
</mapper>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xiao.dao.YlinfoMapper">
	<sql id="columns">
		ID,XH,YLMC,INCI,YLHL,YLCFHL,YLSJHL,SYMD,BZ,PFID
	</sql>
	
	<select id="queryYlInfo" resultType="com.xiao.entity.Ylinfo" parameterType="java.lang.String">
		select <include refid="columns"/>
		from XIAO_YLINFO
		where pfid = #{pfid, jdbcType=VARCHAR}
		order by ylhl desc,xh asc,ylcfhl desc
	</select>	

	<insert id="insertYlInfo" parameterType="com.xiao.entity.Ylinfo">
		insert into XIAO_YLINFO 
		values(#{id},#{xh},#{ylmc},#{inci},#{ylhl},#{ylcfhl},#{ylsjhl},#{symd},#{bz},#{pfid})
	</insert>
	
	<!-- oracle批量插入 -->
	<insert id="batchSave" parameterType="java.util.List">
		INSERT INTO XIAO_YLINFO
		(<include refid="columns"/>)
		<foreach item="item" index="index" collection="list" separator="union all">
         (
			SELECT 
				#{item.id},
				#{item.xh},
				#{item.ylmc},
				#{item.inci},
				#{item.ylhl},
				#{item.ylcfhl},
				#{item.ylsjhl},
				#{item.symd},
				#{item.bz},
				#{item.pfid}
			FROM DUAL
         )
         </foreach>
	</insert>
	
	<!-- mysql批量插入 -->
	<insert id="batchSaveMysql" parameterType="java.util.List">
		INSERT INTO XIAO_YLINFO
		(<include refid="columns"/>)
		VALUES
		<foreach item="item" index="index" collection="list" separator=",">
         (
			#{item.id},
			#{item.xh},
			#{item.ylmc},
			#{item.inci},
			#{item.ylhl},
			#{item.ylcfhl},
			#{item.ylsjhl},
			#{item.symd},
			#{item.bz},
			#{item.pfid}
         )
         </foreach>
	</insert>
	
	<delete id="deleteYlinfoByPfid" parameterType="java.lang.String">
		DELETE FROM XIAO_YLINFO WHERE PFID = #{pfid, jdbcType=VARCHAR}
	</delete>
</mapper>

DAO

public interface PffileMapper {
    
    
	/**
	 * 保存配方文件数据
	 * @param pffile
	 */
	void insertPffile(Pffile pffile);
	
	/**
	 * 查询配方文件数据
	 * @param fjmc
	 * @return
	 */
	List<Pffile> queryPffile(String fjmc);
	
	/**
	 * 删除配方文件数据
	 * @param fjmc
	 */
	void deletePffileByFjmc(String fjmc);
}
public interface YlinfoMapper {
    
    

	/**
	 * 保存原料数据
	 * @param ylpo
	 */
	void insertYlInfo(Ylinfo ylpo);
	
	/**
	 * 批量保存原料数据--oracle
	 * @param list
	 */
	void batchSave(List<Ylinfo> list);
	
	/**
	 * 批量保存原料数据--mysql
	 * @param list
	 */
	void batchSaveMysql(List<Ylinfo> list);
	
	/**
	 * 查询原料数据
	 * @param pfid
	 * @return
	 */
	List<Ylinfo> queryYlInfo(String pfid);
	
	/**
	 * 删除原料数据
	 * @param pfid
	 * @return
	 */
	void deleteYlinfoByPfid(String pfid);
}

工具类

public class CommonUtil {
    
    
	// 获取uuid
	public static String getUUid() {
    
    
		String uuid = UUID.randomUUID().toString();
		uuid = uuid.replace("-", "");
		return uuid;
	}
}
public class ExcelUtil {
    
    
	
	@SuppressWarnings("finally")
	public static String readPF(File file, String[] key) {
    
    
		boolean error = false;
		List<JSONObject> pfJsonList = new ArrayList<JSONObject>();
		
		JSONObject json = new JSONObject();
		InputStream is = null;
		try {
    
    
			Workbook wb = getWorkBook(file);
			int sheetNumberSheet = 0;
			
			if (wb.getNumberOfSheets() <= 10) {
    
    
				sheetNumberSheet = wb.getNumberOfSheets();
			} else {
    
    
				json.put("msg", "配方太多,建议分批导入,每次不超过10个");
				json.put("flag", "tooManyPF");
				return json.toString();
			}
			for (int i1 = 0; i1 < sheetNumberSheet; i1++) {
    
    
				JSONObject pfJson = new JSONObject();
				Sheet sheet = wb.getSheetAt(i1);
				String sheetName = sheet.getSheetName();
				pfJson.put("pfmc", sheetName);
				pfJson.put("pfxh", i1);
				//pfJson.put("fjmc", file.getName());
				// 如果全是Sheet就不读取
				if (sheetName.length() > 5 && sheetName.substring(0, 5).equals("Sheet")) {
    
    
					error = true;
					json.put("flag", "fileErrorPfName");
					json.put("msg", "导入失败,表名【" + sheet.getSheetName()+ "】不可以作为名称,\n请重命名,若是多余表请删除!");
					break;
				}
				// 如果是空行就不读取
				if (sheet.getPhysicalNumberOfRows() == 0) {
    
    
					json.put("msg", "导入失败,表" + (i1 + 1) + "中无可用的行!");
					json.put("flag", "fileErrorwork");
					break;
				}
				JSONArray ary = new JSONArray();
				loop: for (int i = 1; i < sheet.getPhysicalNumberOfRows(); i++) {
    
    // 从第几行开始
					Row row = sheet.getRow(i);
					if (row != null) {
    
    
						JSONObject coljson = new JSONObject();
						for (int j = 0; j < key.length; j++) {
    
    
							Cell cell = row.getCell(j);
							String str = "";
							if (cell != null) {
    
    
								// 判断是否是合并单元格,是合并单元格时返回该合并单元格首行首列的坐标,
								// 非合并单元格是则返回该单元格坐标
								int[] result = isRegion(sheet, cell);
								str = getStringCellValue(sheet.getRow(result[0]).getCell(result[1])).trim();
								if (result[0] == i && result[1] == 0) {
    
    
									if (StringUtils.isBlank(str)) {
    
    
										break loop;
									}
								}
								coljson.put(key[j], str);
//								if (result[1] == 3 && result[0] != i) { // 原料含量列 合并行,特殊处理,多行合并,值只存在第一行
//									coljson.put(key[j], "");
//								}
								// 把每页的sheet当作是名称
								// coljson.put("pfmc", sheet.getSheetName());
								// coljson.put("pfxh", i);
								json.put("flag", "fileSucc");
								json.put("msg", "导入成功!");
							} else {
    
    
								break loop;
							}
						}
						ary.add(coljson);
					} else {
    
    
						// 如果是空的就不需要再循环;跳出本次循环进入下次循环
						break;
					}
					//如果发现超长,结束循环
					if(error) {
    
    
						break;
					}
				}
				//如果发现超长,结束循环
				if(error) {
    
    
					break;
				}
				pfJson.put("pfb", ary);
				if (ary.size() < 1) {
    
    
					json.put("flag", "fileErrorGS");
					json.put("msg",
							"导入失败,Excel文件格式错误!可能情况:\n1、模板错误;\n2、单元格格式错误;\n3、空行;\n4、多余表;");
				}
				pfJsonList.add(pfJson);
			}
			wb = null;// 快速释放
		} catch (Exception e) {
    
    
			json.put("flag", "fileGS");
			json.put("msg", "导入失败,文件格式错误");
		} finally {
    
    
			try {
    
    
				if (is != null) {
    
    
					is.close();
				}
				is = null;
			} catch (IOException e) {
    
    

			}
			// 处理
			if (error) {
    
    
				return json.toString();
			}
			json.put("data", pfJsonList);
			return json.toString();
		}
	}

	public static Workbook getWorkBook(File file) {
    
    
    	//获得文件名  
    	String fileName = file.getName();  
    	//创建Workbook工作薄对象,表示整个excel  
    	Workbook workbook = null;  
    	try {
    
      
    		//获取excel文件的io流  
    		InputStream is = new FileInputStream(file);
    		//根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象  
    		if(fileName.endsWith("xls")){
    
     
    			//2003  
    			workbook = new HSSFWorkbook(is);  
    		}else if(fileName.endsWith("xlsx")){
    
     
    			//2007  
    			workbook = new XSSFWorkbook(is);  
    		}  
    	} catch (IOException e) {
    
      
    		e.printStackTrace();
    	}  
    	return workbook;  
	}
	
	public static int[] isRegion(Sheet sheet, Cell cell) {
    
    
		int mergedRegionsNum = sheet.getNumMergedRegions();
		int[] result = new int[2];
		boolean isMergedRegion = false;
		loop: for (int i = 0; i < mergedRegionsNum; i++) {
    
    
			CellRangeAddress cellRangeAddress = sheet.getMergedRegion(i);
			if (cell.getRowIndex() >= cellRangeAddress.getFirstRow()
					&& cell.getRowIndex() <= cellRangeAddress.getLastRow()
					&& cell.getColumnIndex() >= cellRangeAddress
							.getFirstColumn()
					&& cell.getColumnIndex() <= cellRangeAddress
							.getLastColumn()) {
    
    
				result[0] = cellRangeAddress.getFirstRow();
				result[1] = cellRangeAddress.getFirstColumn();
				isMergedRegion = true;
			}
			if (isMergedRegion)
				break loop;
		}
		if (!isMergedRegion) {
    
    
			result[0] = cell.getRowIndex();
			result[1] = cell.getColumnIndex();
		}
		return result;
	}
	
	/**
	 * 获取单元格数据内容为字符串类型的数据
	 * 
	 * @param cell
	 * @return String 单元格数据内容
	 */
	public static String getStringCellValue(Cell cell) {
    
    
		String strCell = "";
		switch (cell.getCellType()) {
    
    
		case HSSFCell.CELL_TYPE_STRING:
			strCell = cell.getStringCellValue();
			break;
		case HSSFCell.CELL_TYPE_NUMERIC:
			DataFormatter dataFormatter = new DataFormatter(); 
			strCell = dataFormatter.formatCellValue(cell);
			break;
		case HSSFCell.CELL_TYPE_BOOLEAN:
			strCell = String.valueOf(cell.getBooleanCellValue());
			break;
		case HSSFCell.CELL_TYPE_FORMULA: //公式类型
		    try {
    
    
		    	strCell = String.valueOf(cell.getNumericCellValue());    
		    } catch (IllegalStateException e) {
    
    
		    	strCell = String.valueOf(cell.getRichStringCellValue());
		    }
		     break;
		case HSSFCell.CELL_TYPE_BLANK:
			strCell = "";
			break;
		default:
			strCell = "";
			break;
		}
		if (StringUtils.isBlank(strCell))
			return "";
		return strCell;
	}
}
public class ExportExcelUtil {
    
    

	public static Workbook createExcelSheet(List<List<Ylinfo>> list, String[] columnName, String[] columnData, String fileName) {
    
    
        /*初始化excel模板*/
		Workbook workbook = getWorkBook(fileName);
		CellStyle titleStyle = workbook.createCellStyle();
		titleStyle.setAlignment(HorizontalAlignment.CENTER);
		titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
		titleStyle.setBorderBottom(BorderStyle.MEDIUM);
		titleStyle.setBorderLeft(BorderStyle.MEDIUM);
		titleStyle.setBorderRight(BorderStyle.MEDIUM);
		titleStyle.setBorderTop(BorderStyle.MEDIUM);
		Font font = workbook.createFont();
		font.setBold(true);
		titleStyle.setFont(font);
		
		CellStyle contentStyle = workbook.createCellStyle();
		contentStyle.setAlignment(HorizontalAlignment.CENTER);
		contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
		contentStyle.setBorderBottom(BorderStyle.MEDIUM);
		contentStyle.setBorderLeft(BorderStyle.MEDIUM);
		contentStyle.setBorderRight(BorderStyle.MEDIUM);
		contentStyle.setBorderTop(BorderStyle.MEDIUM);
		// 指定当单元格内容显示不下时自动换行
		//contentStyle.setWrapText(true);
		int len = list.size();
		for(int x = 0; x < list.size(); x++) {
    
    
			
			List<Ylinfo> ylList= list.get(x);
			Ylinfo po = ylList.get(0);
			Sheet sheet = workbook.createSheet(po.getPfmc());
			
			//给表头添加行
			Integer[] colWidth = new Integer[] {
    
    8 * 256, 40 * 256, 40 * 256, 15 * 256, 20 * 256, 20 * 256, 15 * 256, 40 * 256};
			Row row = sheet.createRow(0);
			row.setHeight((short)600);
			for (int i = 0, k = 0; i < columnName.length; i++, k++) {
    
    
				sheet.setColumnWidth(k, colWidth[k]);
				Cell cell = row.createCell(k);
				cell.setCellValue(columnName[i]);
				cell.setCellStyle(titleStyle);
			}
			//填入数据
			List<PoiModel> poiModels=new ArrayList<>();
			//循环每一条数据
			for (int i = 0; i < ylList.size(); i++) {
    
    
				Row nrow = sheet.createRow(i + 1);
				nrow.setHeight((short)300);
				//Map<String, Object> map = objectToMap(ylList.get(i));
				Map<String, Object> map = poToMap(ylList.get(i));
				if (map == null) {
    
    
					continue;
				}
				//j控制每一行的格子里填入的数据,每条数据的各个属性
				for (int j = 0; j < columnData.length; j++) {
    
    
					Cell cell = nrow.createCell(j);
					cell.setCellStyle(contentStyle);
					String column = columnData[j];
					Object value = map.get(column);
					if (value == null) {
    
    
						cell.setCellValue("");
					} else if (value instanceof BigDecimal) {
    
    
						cell.setCellValue(((BigDecimal) value).doubleValue());
					} else if (value instanceof String) {
    
    
						cell.setCellValue((String) value);
					} else if (value instanceof Integer) {
    
    
						cell.setCellValue((Integer) value);
					}
					
					// 处理单元格合并--只根据第一列的序号相同来进行合并,已知 j=0 3 6 时需合并
					if (j==0){
    
    
						//当进行到每列的第一行时,创建辅助类用来记录数据用来判断
						if (i==0){
    
    
							PoiModel poiModel = new PoiModel();
							poiModel.setContent(value);
							poiModel.setRowIndex(i+1);//行数
							poiModel.setCellIndex(j);//列数
							poiModels.add(poiModel);
							
						}else{
    
    
							//第一列时不需要考虑前一列内容不一样时不进行合并的情况,所以当内容不同直接进行合并
							if (j==0){
    
    
								if(!value.equals(poiModels.get(j).getContent())) {
    
    
									//当合并时CellRangeAddress c1 = new CellRangeAddress();的参数不允许为2 2 0 0这种情况,所以进行判断
									if (poiModels.get(j).getRowIndex()!=i){
    
    
										CellRangeAddress c1 = new CellRangeAddress(poiModels.get(j).getRowIndex(), i, poiModels.get(j).getCellIndex(), poiModels.get(j).getCellIndex());
										sheet.addMergedRegion(c1);
										CellRangeAddress c2 = new CellRangeAddress(poiModels.get(j).getRowIndex(), i, 3, 3);
										sheet.addMergedRegion(c2);
										CellRangeAddress c3 = new CellRangeAddress(poiModels.get(j).getRowIndex(), i, 6, 6);
										sheet.addMergedRegion(c3);
									}
									//每当合并时把第一列的flag修改为1
									//poiModels.get(j).setFlag(1);
									poiModels.get(j).setContent(value);
									poiModels.get(j).setRowIndex(i+1);
								}
							}
							//当进行到最后一行时,处理最后一行和上面的行合并
							if (i==ylList.size()-1 && j==0){
    
    
								if (poiModels.get(j).getRowIndex()!=i+1){
    
    
									CellRangeAddress c1 = new CellRangeAddress(poiModels.get(j).getRowIndex(), i+1,poiModels.get(j).getCellIndex() , poiModels.get(j).getCellIndex());
									sheet.addMergedRegion(c1);
									CellRangeAddress c2 = new CellRangeAddress(poiModels.get(j).getRowIndex(), i+1, 3, 3);
									sheet.addMergedRegion(c2);
									CellRangeAddress c3 = new CellRangeAddress(poiModels.get(j).getRowIndex(), i+1, 6, 6);
									sheet.addMergedRegion(c3);
								}
							}
						}
					}
				}
			}
		}
        return workbook;
	}

	private static Map<String, Object> poToMap(Ylinfo data) {
    
    
		Map<String, Object> map = new HashMap<>();
		try {
    
    
			Class obj = data.getClass();
			Field[] fields = obj.getDeclaredFields();
			for (Field field : fields) {
    
    
				field.setAccessible(true);
				map.put(field.getName(), field.get(data));
			}
		} catch (IllegalAccessException e) {
    
    
			e.printStackTrace();
		}
		return map;
	}
	
	@SuppressWarnings("unused")
	private static Map<String, Object> objectToMap(Object data) {
    
    
		Map<String, Object> map = new HashMap<>();
		try {
    
    
			Class obj = data.getClass();
			Field[] fields = obj.getDeclaredFields();
			for (Field field : fields) {
    
    
				field.setAccessible(true);
				map.put(field.getName(), field.get(data));
			}
		} catch (IllegalAccessException e) {
    
    
			e.printStackTrace();
		}
		return map;
	}

	public static Workbook getWorkBook(String fileName) {
    
    
		// 创建Workbook工作薄对象,表示整个excel
		Workbook workbook = null;
		// 根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
		if (fileName.endsWith(".xls")) {
    
    
			// 2003
			workbook = new HSSFWorkbook();
		} else if (fileName.endsWith(".xlsx")) {
    
    
			// 2007
			workbook = new XSSFWorkbook();
		}
		return workbook;
	}

}

Controller

@RestController
public class YlinfoController {
    
    

	@Autowired
	YlinfoServiceImpl ylinfoServiceImpl;
	
	@RequestMapping("/handlePF")
	public String handlePF() {
    
    
		String msg = ylinfoServiceImpl.handlePF();
		
		return msg;
	}
	
	/**
	 * 单文件上传
	 * @param file
	 * @return
	 * @throws IOException 
	 */
    @RequestMapping("/uploadFile")
    @ResponseBody
    public Pffile uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
    
    
    	Pffile pffile = new Pffile();
        if (file.isEmpty()) {
    
    
            pffile.setPfmc("上传失败,请选择文件"); // 作为返回消息
            return pffile;
        }

        String fileName = file.getOriginalFilename();
//        System.out.println("originalFilename==" + fileName); // 文件名.文档类型
//        System.out.println("name==" + file.getName());// 获取的是input中 name 的值
//        System.out.println("size==" + file.getSize());// 文件大小,单位:B
        
        String filePath = "/tmp/handlePF/beforeHandle/";
        File dest = new File(filePath + fileName);
        try {
    
    
        	// 方式一
            file.transferTo(dest);
            // 方式二
            //IOUtils.copy(file.getInputStream(), new FileOutputStream(dest));
            String msg = ylinfoServiceImpl.handlePFSingle(filePath, fileName);
            pffile.setFjmc(fileName);
            pffile.setPfmc(msg);
            return pffile;
        } catch (IOException e) {
    
    
            //LOGGER.error(e.toString(), e);
        }
        pffile.setPfmc("上传失败!");
        return pffile;
    }
    
    /**
	 * 文件下载
	 * .doc .docx .pdf .xls .xlsx .jpg .png .gif .txt .js .css .html .java的文件均可下载成功
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	@SuppressWarnings("unused")
	@ResponseBody
	@RequestMapping("/downFile/{fileName}")
	public ResponseEntity<FileSystemResource> down(@PathVariable String fileName)throws UnsupportedEncodingException{
    
    
		String uploadPath = "/tmp/handlePF/afterHandle/";
		String realimgurl = uploadPath + fileName;
		System.out.println(realimgurl);
		
        File file = new File(realimgurl);
        if (file == null){
    
    
            return null;
        }
        
        String suffixType =realimgurl.substring(realimgurl.lastIndexOf("."));
        String newfilename = fileName.substring(0, fileName.lastIndexOf(".")) + suffixType;
        
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Content-Disposition", "attachment; filename=" +  URLEncoder.encode(newfilename, "UTF-8"));
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
 
        return ResponseEntity
                .ok()
                .headers(headers)
                .contentLength(file.length())
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .body(new FileSystemResource(file));
		
	}
	
}

Service

@Service
public class YlinfoServiceImpl {
    
    
	@Autowired
	YlinfoMapper ylMapper;
	@Autowired
	PffileMapper pffileMapper;
	
	/**
	 * 读取Excel文件
	 * 处理数据并写出到Excel文件
	 * @return
	 */
	public String handlePF() {
    
    
		String msg = "";
		String path = "C:\\beforeHandle\\";
		File file = new File(path);
		if(file.exists()) {
    
    
			File[] files = file.listFiles();
			if(files.length < 1) {
    
    
				return "空文件夹,未找到待处理的Excel文件";
			}
			for(File fe : files) {
    
    
				String name = fe.getName();
				if(name.endsWith(".xls") || name.endsWith(".xlsx")) {
    
    
					// 读取Excel文件数据
					File excelfile = new File(path + name);
					String[] PF = {
    
    "xh","ylmc","inci","ylhl","ylcfhl","symd","bz"};
					
					String jsondata = ExcelUtil.readPF(excelfile, PF);
					JSONObject json =  JSONObject.fromObject(jsondata);
					
					if("fileSucc".equals(json.getString("flag"))) {
    
    
						List<JSONObject> pfObjList = (List<JSONObject>) json.get("data");
						List<Ylinfo> ylList = pfSave(pfObjList, name);
						//ylMapper.batchSave(ylList); // oracle
						ylMapper.batchSaveMysql(ylList); // mysql
					} else {
    
    
						msg = json.getString("msg");
						return msg;
					}
					
					// 取数据并处理数据
					List<List<Ylinfo>> list = getHandledList(name);
					// 写出到excel文件
					String tpath = "C:\\afterHandle\\";
					File targetFile = new File(tpath);
					if(!targetFile.exists()) {
    
    
						targetFile.mkdir();
					}
					FileOutputStream fos = null;
					try {
    
    
						fos = new FileOutputStream(tpath + name);
						String[] columnName = {
    
    "序号","标准中文名称","inci名称","原料含量(%)","原料中成分含量(%)","实际成分含量(%)","使用目的","备注"};
						String[] columnData = {
    
    "xh","ylmc","inci","ylhl","ylcfhl","ylsjhl","symd","bz"};
						Workbook workbook = ExportExcelUtil.createExcelSheet(list, columnName, columnData, name);
						
						workbook.write(fos);
						
					} catch (Exception e) {
    
    
						e.printStackTrace();
						msg = "写数据到Excel时出错";
					} finally {
    
    
						// 无论如何都要删除配方数据
						deleteData(name);
						try {
    
    
							fos.close();
						} catch (IOException e) {
    
    
							e.printStackTrace();
						}
					}
				}
			}
			msg = "处理完成,请到C盘的 afterHandle 文件夹查看";
		}else {
    
    
			msg = "请在C盘创建名为 beforeHandle 的文件夹,并把待处理的Excel文件放在该文件夹";
		}
		
		
		return msg;
	}
	
	/**
	 * 上传单个附件
	 * @param filePath
	 * @param fileName
	 * @return
	 */
	public String handlePFSingle(String filePath, String fileName) {
    
    
		String msg = "";
		if(fileName.endsWith(".xls") || fileName.endsWith(".xlsx")) {
    
    
			// 读取Excel文件数据
			File excelfile = new File(filePath + fileName);
			String[] PF = {
    
    "xh","ylmc","inci","ylhl","ylcfhl","symd","bz"};
			
			String jsondata = ExcelUtil.readPF(excelfile, PF);
			JSONObject json =  JSONObject.fromObject(jsondata);
			
			if("fileSucc".equals(json.getString("flag"))) {
    
    
				List<JSONObject> pfObjList = (List<JSONObject>) json.get("data");
				List<Ylinfo> ylList = pfSave(pfObjList, fileName);
				//ylMapper.batchSave(ylList); // oracle
				ylMapper.batchSaveMysql(ylList); // mysql
			} else {
    
    
				msg = json.getString("msg");
				return msg;
			}
			
			// 取数据并处理数据
			List<List<Ylinfo>> list = getHandledList(fileName);
			// 写出到excel文件
			String tpath = "/tmp/handlePF/afterHandle/";
			File targetFile = new File(tpath);
			if(!targetFile.exists()) {
    
    
				targetFile.mkdir();
			}
			FileOutputStream fos = null;
			try {
    
    
				fos = new FileOutputStream(tpath + fileName);
				String[] columnName = {
    
    "序号","标准中文名称","inci名称","原料含量(%)","原料中成分含量(%)","实际成分含量(%)","使用目的","备注"};
				String[] columnData = {
    
    "xh","ylmc","inci","ylhl","ylcfhl","ylsjhl","symd","bz"};
				Workbook workbook = ExportExcelUtil.createExcelSheet(list, columnName, columnData, fileName);
				
				workbook.write(fos);
				msg = "文件生成成功";
			} catch (Exception e) {
    
    
				e.printStackTrace();
				msg = "写数据到Excel时出错";
			} finally {
    
    
				// 无论如何都要删除配方数据
				deleteData(fileName);
				try {
    
    
					fos.close();
				} catch (IOException e) {
    
    
					e.printStackTrace();
				}
			}
		}
		
		
		return msg;
	}
	
	// 获取数据并处理数据
	private List<List<Ylinfo>> getHandledList(String name) {
    
    
		List<List<Ylinfo>> llist = new ArrayList<>();
		List<Pffile> flist = pffileMapper.queryPffile(name);
		for(int m = 0; m < flist.size(); m++) {
    
    
			Pffile fpo = flist.get(m);
			List<Ylinfo> list = ylMapper.queryYlInfo(fpo.getId());
			int tempXh = 0;
			for(int i = 0; i < list.size(); i++) {
    
    
				Ylinfo po = list.get(i);
				// 处理实际成分含量
				BigDecimal temphl = po.getYlcfhl().multiply(po.getYlhl());
				BigDecimal sjcfhl = temphl.divide(new BigDecimal(100));
				po.setYlsjhl(sjcfhl);
				
				po.setPfmc(fpo.getPfmc()); // sheet页名称
				
				int curXh = po.getXh(); // 暂存当前原料的序号,被修改之前的序号
				// 处理序号
				if(i > 0) {
    
    
					int preXh = list.get(i-1).getXh();
					if(po.getXh() == tempXh) {
    
    
						//说明跟上一条原料是合并的,则序号应同上一条原料的序号
						po.setXh(preXh);
						//并把原料含量清空
						po.setYlhl(null);
					} else {
    
    
						//这条原料不是跟上一条合并的,则应在上一条原料的序号+1
						po.setXh(preXh + 1);
					}
				} else {
    
    
					// 排序后的第一条数据,但是原文件序号 xh 可能并不是1
					po.setXh(1);
				}
				tempXh = curXh; // 存放到临时变量,供后一条数据比较,以此判断是否是合并数据
				
			}
			
			llist.add(list);
		}
		return llist;
	}

	// 保存配方数据
	private List<Ylinfo> pfSave(List<JSONObject> pfObjList, String fjmc) {
    
    
		List<Ylinfo> ylList = new ArrayList<>();
		//循环每个配方
		for(JSONObject pfobj : pfObjList) {
    
    
			// 每个配方
			Pffile fpo = new Pffile();
			fpo.setFjmc(fjmc);
			String fid = CommonUtil.getUUid();
			fpo.setId(fid);
			fpo.setPfmc(pfobj.getString("pfmc"));
			fpo.setCjsj(new Date());
			fpo.setPfxh(pfobj.getString("pfxh"));
			pffileMapper.insertPffile(fpo);
			
			List<JSONObject> mPoList = (List<JSONObject>) pfobj.get("pfb");
			for(JSONObject obj : mPoList) {
    
    
				// 每个原料
				Ylinfo ylpo = new Ylinfo();
				ylpo.setId(CommonUtil.getUUid());
				ylpo.setXh(obj.getInt("xh"));
				ylpo.setYlmc(obj.getString("ylmc"));
				ylpo.setInci(obj.getString("inci"));
				ylpo.setYlhl(new BigDecimal(obj.getString("ylhl")));
				ylpo.setYlcfhl(new BigDecimal(obj.getString("ylcfhl")));
				ylpo.setYlsjhl(new BigDecimal(0));
				ylpo.setSymd(obj.getString("symd"));
				ylpo.setBz(obj.getString("bz"));
				ylpo.setPfid(fid);
				
				ylList.add(ylpo);
			}
		}
		return ylList;
	}
	
	//根据文件名称删除所有数据
	private void deleteData(String fname) {
    
    
		List<Pffile> list = pffileMapper.queryPffile(fname);
		for(Pffile fpo : list) {
    
    
			// 删除子表数据
			ylMapper.deleteYlinfoByPfid(fpo.getId());
		}
		//删主表数据
		pffileMapper.deletePffileByFjmc(fname);
	}
}

一开始我是只写了 /handlePF 这个接口,这个接口是直接读取 C 盘指定文件夹的 Excel 模板文件,依次处理并把处理后的 Excel 文件放到另外的指定文件夹,想想就很方便。
但是,有这个需求的朋友并没有相应的开发环境,所以这种方式行不通。
所以写了后面那种方式,通过上传一个文件,处理后写出到文件以供下载

增加的前端页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>处理配方模板V1.0</title>
<script type="text/javascript" src="/js/jquery-3.6.0.min.js"></script>
<style type="text/css">
.btnCls{
     
     
	position: relative;
	display: inline-block;
	padding:3px 4px;
	width: 75px;
	height: 25px;
	overflow: hidden;
	text-align: center;
	font-size: 16px;
	line-height: 25px;
	vertical-align: center;
	border: 1px solid #23c6c8;
	background-color: #23c6c8;
	color: #fff;
	border-radius: 3px;
}
.btnCls:hover{
     
     
	border: 1px solid #23babc;
	background-color: #23babc;
}
.btnCls input{
     
     
	position: absolute;
	left: 0;
	top: 0;
	opacity: 0;
}
a{
     
     
    display:inline-block;
    padding:3px 4px;
    font-size:16px;
    outline:none;
    text-align:center;
    width:75px;
    line-height:25px;
    cursor: pointer;
    text-decoration:none;
}
a.btn_a{
     
     
    color:white;
    background-color: #23c6c8;
    border: 1px solid #23c6c8;
    border-radius: 3px;
}
a.btn_a:hover{
     
     
    color:white;
    background-color: #23babc;
}
</style>
</head>
<body>
	<h3>文件上传</h3>
	<label type="button" class="btnCls" >
		<span>选择文件</span>
		<input id="cert" type="file" onchange="doUpload();" />
	</label>
	<hr align="left" width="30%"/>
	<span id="msg" style="color: red;">请上传文件</span><br/>
	<h3>文件下载</h3>
	<a href="#" id="downlink" class="btn_a">下载文件</a>
	<hr align="left" width="30%"/>
	
	
	
	<script type="text/javascript">
	function doUpload() {
     
     
		var type = "file";
	    var id = "cert";
	    var formData = new FormData();
	    formData.append(type, $("#"+id)[0].files[0]);
	    $.ajax({
     
     
	        type: "POST",
	        url: "/uploadFile",
	        data: formData,
	        processData: false,
	        contentType: false,
	        success: function (data) {
     
     
	            $("#msg").text(data.pfmc);
	            $("#fileName").val(data.fjmc);
	            $("#downlink").attr("href", "/downFile/" + data.fjmc);
	        }
	    });
	}
	</script>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/Alias_fa/article/details/108562209