1.pom.xml
<!--poi.start -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${org-apache-poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-excelant</artifactId>
<version>${org-apache-poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>
<!--poi.end -->
2.读取工具类
package com.common.utils;
import lombok.Data;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 读取excel工具类
*/
public class ExcelReadUtils {
private XSSFWorkbook workbook;
private XSSFSheet sheet;
/**
* 工作表下标
*/
private int sheetIndex = 0;
public ExcelReadUtils(){}
public ExcelReadUtils(File file) throws IOException {
FileInputStream inputStream = new FileInputStream(file);
this.workbook = new XSSFWorkbook(inputStream);
inputStream.close();
this.changeSheet();
}
public ExcelReadUtils(File file, int sheetIndex) throws IOException {
FileInputStream inputStream = new FileInputStream(file);
this.workbook = new XSSFWorkbook(inputStream);
inputStream.close();
this.sheetIndex = sheetIndex;
this.changeSheet();
}
/**
* 获取某行的单元格的值-字符串数组
* @param rowIndex
* @return
*/
public List<String> getRowStrArr(int rowIndex){
List<String> res = new ArrayList<>();
XSSFRow row = this.sheet.getRow(rowIndex);
for (int j = 0; j < row.getLastCellNum(); j++) {
XSSFCell dataCell = row.getCell(j);
String cellValue = this.convertCellValueToString(dataCell);
res.add(cellValue);
}
return res;
}
/**
* 获取某行的单元格的值-根据keyArr列顺序拼接成map
* @param rowIndex
* @param keyArr
* @return
*/
public Map<String, String> getRowStrMap(int rowIndex, List<String> keyArr){
List<String> list = this.getRowStrArr(rowIndex);
Map<String, String> res = new HashMap<>();
if( CollectionUtils.isNotEmpty(list) ){
for(int i=0; i<list.size(); i++){
String key = "column_"+i;
if(CollectionUtils.isNotEmpty(keyArr) && i<keyArr.size()) {
key = keyArr.get(i);
}
res.put(key, list.get(i));
}
}
return res;
}
/**
* 获取所有行的数据-根据keyArr列顺序拼接成map
* @param keyArr
* @return
*/
public List<Map<String, String>> getAllDataMap(List<String> keyArr){
List<Map<String, String>> res = new ArrayList<>();
for (int i = 0; i <= this.sheet.getLastRowNum(); i++) {
Map<String, String> item = this.getRowStrMap(i, keyArr);
res.add(item);
}
return res;
}
/**
* 更换工作表
* @return
*/
private void changeSheet(){
this.sheet = this.workbook.getSheetAt(this.sheetIndex);
}
/**
* 将单元格内容转化为字符串
*/
private String convertCellValueToString(Cell cell) {
if (null == cell) {
return null;
}
String returnValue = null;
switch (cell.getCellTypeEnum()) {
//字符串
case STRING:
returnValue = cell.getStringCellValue();
break;
//数字
case NUMERIC:
double numericCellValue = cell.getNumericCellValue();
boolean isInteger = isIntegerForDouble(numericCellValue);
if (isInteger) {
DecimalFormat df = new DecimalFormat("0");
returnValue = df.format(numericCellValue);
} else {
returnValue = Double.toString(numericCellValue);
}
break;
//布尔
case BOOLEAN:
boolean booleanCellValue = cell.getBooleanCellValue();
returnValue = Boolean.toString(booleanCellValue);
break;
//空值
case BLANK:
break;
//公式
case FORMULA:
cell.getCellFormula();
break;
//故障
case ERROR:
break;
default:
break;
}
return returnValue;
}
/**
* 判断是否为整数,是返回true,否则返回false.
*/
public static boolean isIntegerForDouble(Double num) {
double eqs = 1e-10; //精度范围
return num - Math.floor(num) < eqs;
}
/**
* 使用完要关闭流
*/
public void close() throws IOException {
this.workbook.close();
}
public int getSheetIndex() {
return sheetIndex;
}
public ExcelReadUtils setSheetIndex(int sheetIndex) {
this.sheetIndex = sheetIndex;
this.changeSheet();
return this;
}
}
3.写入工具类
package com.common.utils;
import com.common.exceptions.BaseException;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
/**
* 写excel工具类
*/
public class ExcelWriteUtils {
private XSSFWorkbook workbook;
private List<XSSFSheet> sheetArr;
private List<String> sheetNameArr;
private int currSheetIndex = 0;
private int currRowIndex = 0;
public ExcelWriteUtils(){
this.sheetNameArr = Arrays.asList("sheet1");
this.initSheetArr();
}
public ExcelWriteUtils(String sheetName) {
this.sheetNameArr = Arrays.asList(sheetName);
this.initSheetArr();
}
public ExcelWriteUtils(List<String> sheetNameArr) {
this.sheetNameArr = sheetNameArr;
this.initSheetArr();
}
/**
* 初始化sheet
* @throws Exception
*/
private void initSheetArr() {
if( CollectionUtils.isEmpty(this.sheetNameArr) ){
throw BaseException.logicException("sheet没有设置名称");
}
//XSSFWorkbook:07版本的xlsx
this.workbook = new XSSFWorkbook();
//构造sheet
this.sheetArr = new ArrayList<>();
this.sheetNameArr.forEach(sheetName->{
XSSFSheet sheet = this.workbook.createSheet(sheetName);
this.sheetArr.add(sheet);
});
}
/**
* 获取当前sheet
* @return
*/
private XSSFSheet getCurrSheet(){
return this.sheetArr.get(this.currSheetIndex);
}
/**
* 写入数据到某行
* @param valueArr
*/
public void writeRow(int rowIndex, List<String> valueArr){
XSSFRow row = this.getCurrSheet().getRow(rowIndex);
if( row==null ){
row = this.getCurrSheet().createRow(rowIndex);
}
if( CollectionUtils.isNotEmpty(valueArr) ){
for(int i=0; i<valueArr.size(); i++){
XSSFCell cell = row.createCell(i);
cell.setCellValue(valueArr.get(i));
}
}
}
/**
* 写入数据到当前行
* @param valueArr
*/
public void writeCurrRow(List<String> valueArr){
this.writeRow(this.currRowIndex, valueArr);
}
/**
* 批量写入数据到多行
* @param valueArrArr
*/
public void writeBatchRow(List<List<String>> valueArrArr){
if( CollectionUtils.isNotEmpty(valueArrArr) ){
valueArrArr.forEach(valueArr->{
if( CollectionUtils.isNotEmpty(valueArr) ){
this.writeCurrRow(valueArr);
}
this.currRowIndex++;
});
}
}
public int getCurrSheetIndex() {
return currSheetIndex;
}
public void setCurrSheetIndex(int currSheetIndex) {
this.currSheetIndex = currSheetIndex;
}
public int getCurrRowIndex() {
return currRowIndex;
}
public void setCurrRowIndex(int currRowIndex) {
this.currRowIndex = currRowIndex;
}
public XSSFWorkbook getWorkbook() {
return workbook;
}
}
4.测试
上传文件,读取excel内容,log打印,然后生成新的excel,最后下载这个excel文件
4.1 控制器
package com.demo.mydemo.controller;
import com.common.utils.AjaxResult;
import com.common.utils.StringUtils;
import com.demo.mydemo.entity.dto.ExcelParamDTO;
import com.demo.mydemo.service.IExcelService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
/**
* <p>
* excel
* </p>
*
* @author szy
* @since 2022-05-06
*/
@Controller
@RequestMapping("/excel")
public class ExcelController {
@Autowired
private IExcelService excelService;
/**
* http://localhost:20001/szy-tools/excel/index
* @return
*/
@RequestMapping("/index")
public String index() {
return "excel_index";
}
/**
* http://localhost:20001/szy-tools/excel/upload
*/
//限制只能post访问
@RequestMapping(value="/upload", method= RequestMethod.POST)
@ResponseBody
public AjaxResult uploadData(
@ApiParam(value = "参数") ExcelParamDTO param) {
String filePath = excelService.upload(param);
return AjaxResult.success("成功", filePath);
}
@RequestMapping("/download")
@ApiOperation(value = "下载文件")
public void download(
HttpServletResponse response,
@ApiParam(value = "文件路径") @RequestParam(value="filePath", required = true) String filePath
) throws IOException {
File fileUpload = new File(filePath);
String fileName = fileUpload.getName();
//encode后替换 解决空格问题
fileName = URLEncoder.encode(fileName, "UTF-8");
fileName = fileName.replace("+", "%20");
// 读到流中
// URL url = new URL(fileUpload.getFileUrl());
// URLConnection conn = url.openConnection();
// InputStream inputStream = conn.getInputStream();
InputStream inputStream = new FileInputStream(fileUpload);
response.reset();
response.setContentType("application/octet-stream");
response.addHeader("Content-Disposition", "attachment; filename=" + fileName);
ServletOutputStream outputStream = response.getOutputStream();
byte[] b = new byte[1024];
int len;
//从输入流中读取一定数量的字节,并将其存储在缓冲区字节数组中,读到末尾返回-1
while ((len = inputStream.read(b)) > 0) {
outputStream.write(b, 0, len);
}
inputStream.close();
}
}
4.2 服务类/业务类
接口类
package com.demo.mydemo.service;
import com.demo.mydemo.entity.dto.ExcelParamDTO;
import com.demo.mydemo.entity.dto.SmbParamDTO;
import java.io.File;
public interface IExcelService {
String upload(ExcelParamDTO param);
}
实现类
package com.demo.mydemo.service.impl;
import com.common.exceptions.BaseException;
import com.common.utils.*;
import com.demo.mydemo.entity.dto.*;
import com.demo.mydemo.service.IExcelService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.*;
import static java.util.regex.Pattern.compile;
@Service
@Slf4j
public class ExcelServiceImpl implements IExcelService {
@Override
public String upload(ExcelParamDTO param){
File uploadFile = null;
try {
//上传文件
uploadFile = this._uploadFile(param.getFiles(), "");
//读取excel
ExcelReadUtils readUtils = new ExcelReadUtils(uploadFile);
List<String> keyArr = Arrays.asList("10-19", "20-29", "30-39", "40-49", "50-59", "60-69");
List<Map<String, String>> data = readUtils.getAllDataMap(keyArr);
if( CollectionUtils.isNotEmpty(data) ){
final int[] i = {1};
data.forEach(item->{
log.info("第"+ i[0] +"行:{}", JSONUtils.toJsonString(item));
i[0]++;
});
}
readUtils.close();
//写入excel
ExcelWriteUtils writeUtils = new ExcelWriteUtils("测试1");
if( CollectionUtils.isNotEmpty(data) ){
final int[] rowIndex = {0};
data.forEach(item->{
List<String> valueArr = new ArrayList<>();
keyArr.forEach(key->{
valueArr.add( item.get(key) );
});
writeUtils.writeRow(rowIndex[0], valueArr);
rowIndex[0]++;
});
}
File targetFile = this._buildExcelFile(writeUtils.getWorkbook(), "");
return targetFile.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
throw BaseException.logicException(e.getMessage());
} finally {
//删除上传的文件
if( uploadFile!=null && uploadFile.exists() ){
// uploadFile.delete();
}
}
}
private File _uploadFile(MultipartFile srcFile, String dir) throws Exception{
// 根路径,在 resources/static/upload
String folderPath = ResourceUtils.getURL("classpath:").getPath() + "upload/" + (StringUtils.isNotBlank(dir) ? (dir + "/") : "");
File folder = new File(folderPath);
if( !folder.exists() ){
folder.mkdirs();
}
UUID uuid = UUID.randomUUID();
File targetFile = new File(folder.getAbsolutePath(), uuid+"_"+srcFile.getOriginalFilename());
srcFile.transferTo(targetFile);
return targetFile;
}
private File _buildExcelFile(XSSFWorkbook workbook, String dir) throws IOException {
// 根路径,在 resources/static/upload
String folderPath = ResourceUtils.getURL("classpath:").getPath() + "upload/" + (StringUtils.isNotBlank(dir) ? (dir + "/") : "");
File folder = new File(folderPath);
if( !folder.exists() ){
folder.mkdirs();
}
UUID uuid = UUID.randomUUID();
File targetFile = new File(folder.getAbsolutePath(), uuid+"_build_.xlsx");
OutputStream out = new FileOutputStream(targetFile);
workbook.write(out);
workbook.close();
out.close();
return targetFile;
}
}
4.3 页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>excel测试</title>
<script src="./../scripts/jquery-3.3.1.min.js"></script>
<script src="./../scripts/jquery.form.js"></script>
</head>
<body>
<form id="form" method="post" enctype="multipart/form-data">
<table>
<tr>
<td>文件</td>
<td>
<input type="file" name="files"/>
</td>
</tr>
</table>
<input type="submit" value="上传" id="btnUpload"/>
</form>
<div id="result"></div>
<script>
$(function(){
var url = window.location.origin+"/szy-tools/excel/upload";
var download_url = window.location.origin+"/szy-tools/excel/download";
$("#form").ajaxForm({
url : url,
type : "POST",
dataType : "json",
beforeSubmit: function(){
return true;
},
success: function(res){
if( res.state=="success" ){
$("#result").text("成功:"+res.data);
var t_url = download_url+"?filePath="+urlencode(res.data);
window.open(t_url);
}else{
$("#result").text("失败:"+res.message);
}
},
fail: function(res){
$("#result").text("失败:"+res);
}
});
});
function urlencode (str) {
str = (str + '').toString();
return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
}
</script>
</body>
</html>
5.补充所有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.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>szy-tools</name>
<description>My tools project for Spring Boot</description>
<!-- 打包配置 -->
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
<!-- 分布式RPC框架Motan-->
<motan.version>1.1.6</motan.version>
<org-apache-poi.version>3.17</org-apache-poi.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- thymeleaf,for html,jsp... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- json.start -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!-- jackson默认不支持java8的时间类型,需要添加一个时间模块 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- json.end -->
<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!-- Knife4j在线API文档的依赖 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- httpclient.start -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- httpclient.end -->
<!-- smb.start -->
<!--支持文件共享SMB1协议-->
<dependency>
<groupId>org.samba.jcifs</groupId>
<artifactId>jcifs</artifactId>
<version>1.3.3</version>
</dependency>
<!--支持文件共享SMB2/3协议-->
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>smbj</artifactId>
<version>0.10.0</version>
</dependency>
<!-- smb.end -->
<!-- user-agent工具类.start -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>1.21</version>
</dependency>
<!-- user-agent工具类.end -->
<!--poi.start -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${org-apache-poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-excelant</artifactId>
<version>${org-apache-poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>
<!--poi.end -->
</dependencies>
<build>
<!-- 指定生成的jar包名称 -->
<finalName>szy-tools</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>