根据一个朋友的需求,其需要处理一个 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>