一、EasyExcel 介绍
Java 解析、生成 Excel 比较有名的框架有 Apache POI、jxl。但它们都存在一个严重的问题就是非常的耗内存。POI 有一套 SAX 模式的 API 可以一定程度的解决一些内存溢出的问题,但 POI 还是有一些缺陷,比如 07 版 Excel 解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。EasyExcel 重写了 POI 对 07 版 Excel 的解析,能够原本一个 3M 的 Excel 用 POI SAX 依然需要 100M 左右内存降低到几 M,并且再大的excel 不会出现内存溢出,03 版依赖 POI 的 sax 模式。在上层做了模型转换的封装,让使用者更加简单方便。
EasyExcel 是一个基于 Java 的简单、省内存的读写 Excel 的开源项目。在尽可能节约内存的情况下支持读写百 M 的 Excel。
文档地址:https://alibaba-easyexcel.github.io/index.html
github地址:https://github.com/alibaba/easyexcel
导出示例链接:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write
二、EasyExcel 集成
首先,在pom.xml中添加依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
</dependencies>
三、实现excel导出操作
首先,封装excel导出条件;
@Data
public class UserInfoExportForm {
@ApiModelProperty("用户账户")
private String userName;
@ApiModelProperty("手机号码")
private String phone;
@ApiModelProperty("用户状态")
private String status;
@ApiModelProperty("用户类型")
private String type;
@ApiModelProperty(value = "excel表头")
private List<String> headers;
@ApiModelProperty(value = "excel的key值")
private List<String> values;
}
UserInfoController:
@RestController
@RequestMapping("/userInfo")
@Api(value = "userInfo", tags = "用户信息")
@Slf4j
public class UserInfoController {
@Resource
private IUserInfoService userInfoService;
@ApiOperation(value = "导出调度员账户信息", notes = "导出调度员账户信息,支持Excel格式")
@ApiImplicitParam(name = "form", value = "导出调度员账户信息参数", required = true, dataType = "UserInfoExportForm")
@PostMapping(value = "/export")
public Result excelExport(@RequestBody UserInfoExportForm form) {
return userInfoService.excelExport(form);
}
}
Service:
public interface IUserInfoService extends IService<UserInfo>{
Result excelExport(UserInfoExportForm form);
}
ServiceImpl:
对象集合要写封装成List<List>结构。
这是一个声明了一个List类型的 head
变量,其中每个元素也是一个 List
,每个内部列表的元素都是字符串类型 (String
)。
@Service
@Slf4j
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
implements IUserInfoService {
@Resource
private UserInfoMapper userInfoMapper;
@Resource
private ExcelUploadUtils excelUploadUtils;
@Override
public Result excelExport(UserInfoExportForm form) {
try {
if (form.getHeaders() == null || form.getHeaders().size() == 0) {
return Result.fail("没有传入excel表头,无法导出!");
}
Result<PageResult> pageResultResult = query(form);
List<UserInfoVO> records = (List<UserInfoVO>) pageResultResult.getData().getRecords();
//封装表头
List<List<String>> head = new ArrayList<>();
for (String s : form.getHeaders()) {
List<String> list = new ArrayList<>();
list.add(s);
head.add(list);
}
//封装表数据
List<List<String>> data = records.stream().map(userInfo -> {
List<String> rowData = new ArrayList<>();
rowData.add(userInfo.getUserCode());
rowData.add(userInfo.getUserName());
rowData.add(userInfo.getNickName());
rowData.add(userInfo.getType().equals("00") ?"系统用户":(userInfo.getType().equals("01") ?"调度员":"生产商用户"));
rowData.add(userInfo.getIdCard());
rowData.add(userInfo.getSex().equals("0")?"男":"女");
rowData.add(userInfo.getPhone()); rowData.add(userInfo.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh-mm-ss")));
rowData.add(userInfo.getUpdateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh-mm-ss")));
rowData.add(userInfo.getRemarks());
return rowData;
}).collect(Collectors.toList());
// 导出Excel文件
//创建ByteArrayOutputStream对象,用于存储写入的数据
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//设置写入对象
EasyExcel.write(outputStream)
//注册写入处理器,自动调整宽度
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
//设置表头
.head(head)
//设置写入的excel表名
.sheet("用户信息表")
//写入data数据到excel表中
.doWrite(data);
//调用文件上传接口,传入字节输出流和文件名,返回文件下载链接
return excelUploadUtils.uploadExcelFile(outputStream,"用户信息表");
} catch (Exception e) {
return Result.fail("导出用户信息表失败");
}
}
public Result<PageResult> query(UserInfoExportForm form) {
LambdaQueryWrapper<UserInfo> lqw = getQueryWrapper(form.getUserName(), form.getPhone(), form.getStatus(), form.getType());
List<UserInfo> records = userInfoMapper.selectList(lqw);
List<UserInfoVO> userInfoVOList = new ArrayList<>();
for (UserInfo record : records) {
UserInfoVO userInfoVO = new UserInfoVO();
BeanUtils.copyProperties(record,userInfoVO);
userInfoVOList.add(userInfoVO);
}
return Result.success(userInfoVOList);
}
private LambdaQueryWrapper<Dispatcher> getQueryWrapper(String userName, String phone, String status, String type) {
LambdaQueryWrapper<UserInfo> lqw = Wrappers.<UserInfo>lambdaQuery()
.eq(null != userName,UserInfo::getUserName, userName)
.eq(null != phone,UserInfo::getPhone, phone)
.eq(null != status,UserInfo::getStatus, status)
.eq(null != type,UserInfo::getType, type);
return lqw;
}
}
文件上传工具类:
@Component
@Slf4j
public class ExcelUploadUtils {
@Resource
public FileStoreClient storeClient;
public Result<String> uploadExcelFile(ByteArrayOutputStream outputStream, String fileName) {
try {
if (outputStream != null) {
byte[] bytes = outputStream.toByteArray();
// 上传到服务器
String uploadType = "1";
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_hh-mm-ss"));
FileStoreHelper helper = new FileStoreHelper();
helper.setBytes(bytes);
helper.setRemoteFile(time + fileName + ".xlsx");
helper.setUploadType(uploadType);
Result<String> result = storeClient.ossFileStore(helper);
outputStream.flush();
outputStream.close();
return result;
}
} catch (IOException e) {
log.error("Exception");
}
return Result.success();
}
}
导出Excel文件,使用 new ByteArrayOutputStream()
创建一个ByteArrayOutputStream 对象,用于存储写入的数据。通过 EasyExcel.write(outputStream)
创建 EasyExcel 写入器,并将写入目标设置为 outputStream 对象。
接下来,使用 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
方法注册一个 LongestMatchColumnWidthStyleStrategy
写入处理器,用于在写入过程中自动调整列宽;使用 .head(head)
方法设置表头,其中 head 是一个包含表头信息的 List 对象;使用 .sheet("调度员账户信息表")
方法设置要写入的工作表的名称,使用 .doWrite(data)
方法将数据(data)写入到工作表中。