阿里巴巴的组件easyExcel
之前没有接触过导出报表大数据量的相关技术,上班后,涉及到需要导出大数据量的功能,因为接触了阿里的组件easyExcel。
easyExcel相比之前的excelpoi 占用内存很小,我们知道excelpoi在处理特别多的数据的时候,速度慢且经常有异常发生,因为用阿里研发的easyExcel较为靠谱些。
easyExcel主要功能:
1.支持Excel导入与导出,同时支持xls和xlsm,即07版本和03版本(官方建议03版本不要超过2000行)的Excel文件格式。
2.支持pojo注释时,映射成为java实体模型。
3.支持多个sheet,同时一个sheet支持多张表。
4.支持自定义Excel样式:字体,加粗,表头颜色,数据内容颜色等。
5.可以设置是否需要写表头。
使用easyExcel需要导入依赖(案例中使用的是2.1.6版本)
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</dependency>
该案例是基于若依管理系统进行了改造(用户管理,导出功能)
easyExcel可以分为有Java模型和无Java模型,有Java模型需要继承BaseRowModel
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')")
@GetMapping("/export")
public AjaxResult export(SysUser user, HttpServletResponse response, HttpServletRequest request)
{
AjaxResult ajaxResult = null;
try {
List<SysUser> exportList = new ArrayList<>();
List<SysUser> list = userService.selectUserList(user);
//组装导出数据
list.stream().forEach(x -> {
SysUser sysUser = new SysUser();
sysUser.buildSysUser(sysUser,x);
exportList.add(sysUser);
});
ajaxResult = ExportExcelUtil.generateExcel(new ArrayList<>(), exportList, response, request);
}catch (Exception e){
e.printStackTrace();
log.error("daochu error", e);
}
return ajaxResult;
}
SysUser (BaseEntity里面继承了BaseRowModel)
package com.ruoyi.project.system.domain;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.project.system.converter.SexConverter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType;
import com.ruoyi.framework.aspectj.lang.annotation.Excel.Type;
import com.ruoyi.framework.aspectj.lang.annotation.Excels;
import com.ruoyi.framework.web.domain.BaseEntity;
/**
* 用户对象 sys_user
*
* @author ruoyi
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SysUser extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 用户ID */
@ExcelProperty(value = "用户序号",index = 0)
private Long userId;
/** 部门ID */
private Long deptId;
/** 用户账号 */
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
@ExcelProperty(value = "登录名称",index = 1)
private String userName;
/** 用户昵称 */
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
@ExcelProperty(value = "用户名称",index = 2)
private String nickName;
/** 用户邮箱 */
@Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
@ExcelProperty(value = "用户邮箱",index = 3)
private String email;
/** 手机号码 */
@Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
@ExcelProperty(value = "手机号码",index = 4)
private String phonenumber;
/** 用户性别 */
@ExcelProperty(value = "用户性别",index = 5)
private String sex;
/** 用户头像 */
private String avatar;
/** 密码 */
@JsonProperty
private String password;
/** 盐加密 */
private String salt;
/** 帐号状态(0正常 1停用) */
@ExcelProperty(value = "帐号状态",index = 6)
private String status;
/** 删除标志(0代表存在 2代表删除) */
private String delFlag;
/** 最后登陆IP */
@ExcelProperty(value = "最后登陆IP",index = 7)
private String loginIp;
/** 最后登陆时间 */
@ExcelProperty(value = "最后登陆时间",index = 8)
private Date loginDate;
/** 部门名称 */
@ExcelProperty(value = "部门名称",index = 9)
private String deptName;
/** 负责人 */
@ExcelProperty(value = "部门负责人", index = 10)
private String leader;
/** 部门对象 */
private SysDept dept;
/** 角色对象 */
private List<SysRole> roles;
/** 角色组 */
private Long[] roleIds;
/** 岗位组 */
private Long[] postIds;
public SysUser(Long userId)
{
this.userId = userId;
}
public boolean isAdmin()
{
return isAdmin(this.userId);
}
public static boolean isAdmin(Long userId)
{
return userId != null && 1L == userId;
}
public void buildSysUser(SysUser sysUser, SysUser x) {
sysUser.setUserId(x.getUserId());
sysUser.setUserName(x.getUserName());
sysUser.setNickName(x.getNickName());
sysUser.setEmail(x.getEmail());
sysUser.setPhonenumber(x.getPhonenumber());
sysUser.setSex(x.getSex());
sysUser.setStatus(x.getStatus());
sysUser.setLoginIp(x.getLoginIp());
sysUser.setLoginDate(x.getLoginDate());
sysUser.setDeptName(x.getDept().getDeptName());
sysUser.setLeader(x.getDept().getLeader());
}
}
ExportExcelUtil类
package com.ruoyi.common.utils.poi;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.ruoyi.common.exception.BaseException;
import com.ruoyi.framework.config.RuoYiConfig;
import com.ruoyi.framework.web.domain.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.UUID;
@Slf4j
public class ExportExcelUtil {
/**
* 生成带数据的excel
* @param templateDetails
* @param dataList
* @param
*/
public static AjaxResult generateExcel(List<Object> templateDetails, List<? extends BaseRowModel> dataList, HttpServletResponse response, HttpServletRequest request) throws Exception{
if (CollectionUtils.isEmpty(dataList)){
throw new BaseException("导出数据不能为空");
}
String sheetName = "用户数据";
String filename = encodingFilename(sheetName);
//初始化数据
generateDatasExcel(templateDetails, dataList,filename);
return AjaxResult.success(filename);
}
/**
* 初始化数据
* @param templateDetails
* @param dataList
* @param path
*/
private static void generateDatasExcel(List<Object> templateDetails, List<? extends BaseRowModel> dataList,String path) throws Exception{
try(OutputStream out = new FileOutputStream(getAbsoluteFile(path))){
ExcelWriter excelWriter = EasyExcelFactory.getWriterWithTempAndHandler(null, out,ExcelTypeEnum.XLSX, true,null);
Class<? extends BaseRowModel> a = dataList.get(0).getClass();
Sheet sheet1 = new Sheet(1,1,a);
sheet1.setSheetName("用户数据");
excelWriter.write(dataList,sheet1);
excelWriter.finish();
}catch (IOException e){
log.error("导出生成文件失败", e);
}
}
/**
* 获取下载路径
*
* @param filename 文件名称
*/
public static String getAbsoluteFile(String filename)
{
String downloadPath = RuoYiConfig.getDownloadPath() + filename;
File desc = new File(downloadPath);
if (!desc.getParentFile().exists())
{
desc.getParentFile().mkdirs();
}
return downloadPath;
}
/**
* 编码文件名
*/
public static String encodingFilename(String filename)
{
filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";
return filename;
}
}
fileDownload方法(打断点走到了这个方法里面对文件进行下载,不知道在哪里调用的?有知道的小伙伴可以在评论里留言)
package com.ruoyi.project.common;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.framework.config.RuoYiConfig;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.framework.web.domain.AjaxResult;
/**
* 通用请求处理
*
* @author ruoyi
*/
@RestController
public class CommonController
{
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
@Autowired
private ServerConfig serverConfig;
/**
* 通用下载请求
*
* @param fileName 文件名称
* @param delete 是否删除
*/
@GetMapping("common/download")
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
{
try
{
if (!FileUtils.isValidFilename(fileName))
{
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
}
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
String filePath = RuoYiConfig.getDownloadPath() + fileName;
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition",
"attachment;fileName=" + FileUtils.setFileDownloadHeader(request, realFileName));
FileUtils.writeBytes(filePath, response.getOutputStream());
if (delete)
{
FileUtils.deleteFile(filePath);
}
}
catch (Exception e)
{
log.error("下载文件失败", e);
}
}
/**
* 通用上传请求
*/
@PostMapping("/common/upload")
public AjaxResult uploadFile(MultipartFile file) throws Exception
{
try
{
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
// 上传并返回新文件名称
String fileName = FileUploadUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
AjaxResult ajax = AjaxResult.success();
ajax.put("fileName", fileName);
ajax.put("url", url);
return ajax;
}
catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
}
/**
* 本地资源通用下载
*/
@GetMapping("/common/download/resource")
public void resourceDownload(String name, HttpServletRequest request, HttpServletResponse response) throws Exception
{
// 本地资源路径
String localPath = RuoYiConfig.getProfile();
// 数据库资源地址
String downloadPath = localPath + StringUtils.substringAfter(name, Constants.RESOURCE_PREFIX);
// 下载名称
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition",
"attachment;fileName=" + FileUtils.setFileDownloadHeader(request, downloadName));
FileUtils.writeBytes(downloadPath, response.getOutputStream());
}
}