Springboot+vue Excel导入导出(POI4.0 和 EasyPOI)
我们在进行项目构建的时候,通常在存储大规模的数据之后,我们想要得到这些数据,就需要把这些数据导出,使用一个Excel表格进行保存起来。在这里我们怎么实现呢?接着往下看。
1、POI是什么?
在这里apache提供了一个挺好用的工具,那就是POI,POI是创建和维护操作各种符合 Office Open XML(OOXML)标准和微软的 OLE2 复合文档格式(OLE2)的 Java API。
2、如何使用POI?
那如何使用呢?首先我们在一个需要使用的项目当中进行导入依赖(idea+maven),在这里我们不单单只导入POI依赖,还需要导入一个poi-ooxml支持xls操作的依赖。导入4.0.0的版本,还有其他版本也是可以进行选择的,比如说3.1.14.
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.0</version>
</dependency>
在这里导入过程所花费的时间还是比较久的。在等待导入完成之后,就是对这个POI进行使用了。你也可以前往官方文档进行预览:https://poi.apache.org/
在这里我就直接上代码了(一个简单的excel表,后面还会对excel表如何设置样式进行说明);在代码当中提供了相对应的注释:
@PostMapping("/toExcel")
public void toExcel(HttpServletResponse response) throws IOException {
//获得数据库当中的数据,这里是已经写好的一个方法,返回的是一个select * 的list集合。
List<User> list = userService.findAll();
HSSFWorkbook workbook = new HSSFWorkbook();
//创建一个表,是指一个xsl文件可以创建三个表其中的一个表的表名,设置为user info
HSSFSheet sheet = workbook.createSheet("user info");
//设置一行(第一行)
HSSFRow row =null;
row = sheet.createRow(0); //创建第一个单元格
row.setHeight((short)(26.5 * 20)); //设置行高,在这里26.5就是这一行的行高
row.createCell(0).setCellValue("用户信息"); //这个单元格的值
//设置单元格合并,单元格合并的位置,0到0行,0到4列
//excel表也是从0行0列开始算起,注意自己设置合并的大小,避免已经设置值了的单元格也被合并导致代码报错
CellRangeAddress rowRegion = new CellRangeAddress(0,0,0,4);
sheet.addMergedRegion(rowRegion); //进行合并
//另起一行,也就是第二行
row =sheet.createRow(1);
row.setHeight((short)(22.5*20));
row.createCell(0).setCellValue("id");//给单元格进行赋值
row.createCell(1).setCellValue("name");
row.createCell(2).setCellValue("birthday");
row.createCell(3).setCellValue("sex");
row.createCell(4).setCellValue("address");
//对前面得到的list集合进行遍历
for (int i =0 ;i<list.size();i++){
row = sheet.createRow(i+2); //重新找到这一行数据的行数,也就是在前端的基础上再另起一行
User user =list.get(i); //得到集合遍历的每一行数据
row.createCell(0).setCellValue(user.getId()); //将值给到单元格
row.createCell(1).setCellValue(user.getName()); //user是一个实体类对象,获取get方法进行赋值
row.createCell(2).setCellValue(user.getBirthday());
row.createCell(3).setCellValue(user.getSex());
row.createCell(4).setCellValue(user.getAddress());
}
//设置自适应宽度
for (int i =0;i<=13 ;i++){
sheet.autoSizeColumn(i);
}
//response进行相应数据
response.setContentType("application/vnd.ms-excel;charset=utf-8");
OutputStream os = response.getOutputStream();
//这里进行设置了一个文件名,其实也可以不要设置了,
//在前端进行下载的时候需要重新给定一个文件名进行下载
response.setHeader("Content-disposition","attachment;filename=User.xls");
workbook.write(os);
os.flush();
os.close();
}
在这里就是在后端进行获取到数据了,之后我们只需要在前端访问这个路由,创建一个超链接进行下载就行了,接下来就是前端下载了
3、如何将表保存?
我们使用一个按钮,给按钮绑定一个单击事件,
在这里将路由地址进行封装:
export function toExcel(callback) {
return request({
url: "/toExcel", //完整路由地址
method: 'post', //请求类型
procgress: true,
back: callback,
responseType: 'blob' //响应类型,避免乱码
})
}
在事件当中访问这个路由,创建一个下载链接进行下载。
//在使用之前我们需要将刚才封装好的toExcel进行导入到这个vue文件当中。
import {toExcel} from '@/api/ordering' //上面的文件的地址。
//导出excel表
exportExcel() {
toExcel().then((data) => {
this.downloadFile(data)
})
},
/**
* 文件导出
*/
downloadFile(data) {
if (!data) {
return
}
const link = document.createElement('a');
let blob = new Blob([data], {
type: 'application/vnd.ms-excel'
});
link.style.display = 'none';
link.href = URL.createObjectURL(blob);
link.setAttribute('download', 'user info' + '.xls');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
到这里,就实现了在前端获取数据并且下载一个xls文件。但是在这里我们的xls文件也只是一个很简单的表,一点也不美观。那我们又该如何设置这个excel表的样式呢?
4、POI的样式如何设置?
POI给我们提供了一个样式类HSSFCellStyle,使用HSSFWorkbook对象的createCellStyle方法创建一个这样的样式对象。
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFCellStyle setBorder = workbook.createCellStyle();
在这里创建号这个样式对象后,在使用的时候,对要使用的row对象进行使用即可。如下:给第一列加上这个样式,(当然了,在这里这个样式还没有任何值,还没进行设置)
row.getCell(0).setCellStyle(setBorder);
4.1、背景色的设置
在这里使用setFillForegroundColor方法给定颜色,setFillPattern给定填充规则,在这里的IndexedColors的枚举类给出了其颜色的名称。
setBorderTable.setFillForegroundColor(IndexedColors.CORNFLOWER_BLUE.getIndex());
setBorderTable.setFillPattern(FillPatternType.SOLID_FOREGROUND);
单击这个IndexedColors即可进入到这个枚举类当中进行查看。
4.2、字体的设置
在表格当中不可避免的就是字体的设置了,在进行设置字体的时候,POI提供了一个HSSFFont字体类,同样的要使用HSSFWorkbook对象的createFont方法创建一个字体样式对象。
HSSFFont font = workbook.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 12);//设置字体大小
font.setBold(true);//是否加粗
setBorder.setFont(font); //设置字体后应用
在这里对于不同的版本设置也是有点不一致的地方,注意区别。
4.3、设置居中显示
在前面我们以及有了合并单元格的操作了,但是对于合并了的单元格怎么设置其文字的居中显示呢?在这里直接使用对应的两个设置位置的方法即可。
setBorder.setAlignment(HorizontalAlignment.CENTER); // 水平居中
setBorder.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中
4.4、设置文字换行显示
再将单元格进行合并之后,文字为了美观在某些地方还需设置换行显示,这又如何实现呢?在这里POI提供了一个自动换行的方法setWrapText,和上面一样代码如下:
setBorderTableData.setWrapText(true);
但是这里excel也只会根据判断显示是否换行,在这里还提供了转义字符,可以进行强制换行。使用如下:/r/n
即表示强制换行,这样A和B就会进行换行了。
row.createCell(0).setCellValue(A+ "\r\n" + B);
整体的关于excel表的样式大致使用到这些就用导出一个还行的表了。
5、使用EasyPOI进行导出
首先加入easypoi的依赖,这里还是基于springboot微服务进行操作:
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>3.1.0</version>
</dependency>
在使用EasyPOI的时候对导出的数据用实体类进行保存,所以我们只需要对实体类进行相关的配置即可:通过@Excel注解来标识导出的Excel表头对应的列名称。
之后就是导出接口编写了,直接通过queryWrapper查所有数据再塞给workbook,到这里也就完成了导出数据。
@PostMapping("/studentinfo/export")
@ApiOperation(value = "导出学生信息 response", notes = "/studentinfo/export")
public void export(HttpServletResponse response, @RequestBody @ApiParam(name = "studentInfo", value = "studentInfo", required = true) StudentInfo studentInfo) {
try {
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=user.xls");
ExportParams exportParams = new ExportParams();
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, StudentInfo.class, studentInfoMapper.selectList(null));
workbook.write(response.getOutputStream());
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
再往后就是前端访问这个接口进行文件下载。这里使用vue脚手架搭建了一个前端项目,并且引入了axios。通过axios发送请求获取返回的文件流到前端进行文件下载。
doExport() {
this.toInterface("http://localhost:9999/studentinfo/export", this.searchForm)
},
toInterface(url, param, isForm) {
let mergeOptions = {
url: url,
method: "post",
headers: {
Accept: "*",
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8;",
},
data: param,
responseType: "blob",
};
mergeOptions.paramType = "body";
mergeOptions.headers["Content-Type"] =
"application/json; charset=UTF-8";
axios(mergeOptions)
.then(function(response) {
console.log(response)
var blob = new Blob([response.data], {
type: "application/vnd.ms-excel"
});
var href = window.URL.createObjectURL(blob); //创建下载的链接
var link = document.createElement("a");
link.href = href;
link.setAttribute("download", "学生信息.xls");
document.body.appendChild(link);
link.click(); //点击下载
document.body.removeChild(link); //下载完成移除元素
window.URL.revokeObjectURL(href); //释放掉blob对象
})
.catch(function(error) {
console.error(error);
});
},
6、使用EasyPOI进行导入
还是跟导出一样,使用一个配置对应@Excel注解的实体类进行操作,这里我们只需要在前端页面发送请求(后端接口)并且附带上文件,后端接口获取文件之后进行解析入库即可。首先就是前端上传文件操作。直接上代码:
<el-upload
class="upload-demo"
ref="upload"
action="http://localhost:9999/student/grade/import"
:on-preview="handlePreview"
:on-remove="handleRemove"
:file-list="fileList"
:auto-upload="false">
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
<el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button>
<div slot="tip" class="el-upload__tip">只能上传xlxs/xls文件</div>
</el-upload>
这都没啥好说的,直接在elementUI上的导入组件,自需要替换掉地址即可。接下来就是后端这个接口的编写了,
@PostMapping("/student/grade/import")
public String importExcel(@RequestParam("file") MultipartFile file) {
ImportParams importParams = new ImportParams();
importParams.setNeedVerfiy(false);
try {
ExcelImportResult<StudentGrade> result =
ExcelImportUtil.importExcelMore(file.getInputStream(), StudentGrade.class, importParams);
// 当这里就把excel文件的内容根据@Excel注解转换成了对应的实体对象,后续的入库操作这里就省略了
List<StudentGrade> studnetGradeList = result.getList();
// 入库操作 。。。。
log.info("从Excel导入数据一共 {} 行 ", studnetGradeList.size());
} catch (IOException e) {
log.error("导入失败:{}", e.getMessage());
} catch (Exception e1) {
log.error("导入失败:{}", e1.getMessage());
}
return "导入成功";
}