源碼地址:https://github.com/alibaba/easyexcel
升級版本 升級版本 升級版本
依賴
<!--阿里easyExcel工具包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.0.2</version>
</dependency>
對應工具類
/**
* @author yy
* @version 2.0
* @date 2019/7/30 16:27
* @deprecated 版本升級爲對應的2.0.0以上
* 性能更加高效 導出數據更加穩定
* 支持 64M內存1分鐘內讀取75M(46W行25列)
**/
@Component
public class ExcelUtil {
/**
* 導出 Excel :一個 sheet,帶表頭.
*
* @param response HttpServletResponse
* @param list 數據 list,每個元素爲一個 BaseRowModel
* @param fileName 導出的文件名
* @param sheetName 導入文件的 sheet 名
* @param model 映射實體類,Excel 模型
* @throws Exception 異常
*/
public void writeExcel(HttpServletResponse response, List<? extends Object> data,
String fileName, String sheetName, Class model) throws Exception {
// 頭的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
//設置表頭居中對齊
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
// 內容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
//設置內容靠左對齊
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
// 這個策略是 頭是頭的樣式 內容是內容的樣式 其他的策略可以自己實現
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
// 這裏 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字爲模板 然後文件流會自動關閉
EasyExcel.write(getOutputStream(fileName, response), model)
.excelType(ExcelTypeEnum.XLSX)
.sheet(sheetName)
.registerWriteHandler(horizontalCellStyleStrategy)
//最大長度自適應 目前沒有對應算法優化 建議註釋掉不用 會出bug
// .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.doWrite(data);
}
/**
* 導出文件時爲Writer生成OutputStream.
*
* @param fileName 文件名
* @param response response
* @return ""
*/
private OutputStream getOutputStream(String fileName,
HttpServletResponse response) throws Exception {
try {
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
response.setHeader("Pragma", "public");
response.setHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "max-age=0");
return response.getOutputStream();
} catch (IOException e) {
throw new Exception("導出excel表格失敗!", e);
}
}
/**
* 簡單的讀
* @param fileName
* @param clazz
* @return
*/
public List<?> simpleRead(String fileName, Class clazz) {
// 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然後裏面用到spring可以構造方法傳進去
// 這裏 需要指定讀用哪個class去讀,然後讀取第一個sheet 文件流會自動關閉
EasyExcel.read(fileName, clazz, new DataListener()).sheet().doRead();
return new DataListener().saveData();
}
***由於本身listener類沒有歸spring管理 所以無法獲取容器內部的bean實例***
class DataListener extends AnalysisEventListener<User> {
/**
* 每隔5條存儲數據庫,實際使用中可以3000條,然後清理list ,方便內存回收
*/
private final int BATCH_COUNT = 10;
private List<User> list = new ArrayList<>();
public List<User> getList() {
return list;
}
/**
* 這個每一條數據解析都會來調用
*
* @param data
* one row value. It is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(User data, AnalysisContext context) {
LOGGER.info("解析到一條數據:{}", JSON.toJSONString(data));
list.add(data);
// 達到BATCH_COUNT了,就需要對list進行清空,防止數據幾萬條數據在內存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存儲完成清理 list
list.clear();
}
}
/**
* 所有數據解析完成了 都會來調用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 這裏也要保存數據,確保最後遺留的數據也存儲到數據庫
// saveData();
LOGGER.info("所有數據解析完成!");
}
/**
* 加上存儲數據庫
*/
private List<?> saveData() {
LOGGER.info("{}條數據,開始存儲數據庫!", list.size());
return list;
}
}
實體映射Excelproperty 對應導出Excel表頭
@Data
@ColumnWidth(22)
@ContentRowHeight(15) //由於新版本並沒有對單元格設置默認值
所以需要手動對單元格進行賦值
public class User extends BaseRowModel implements Serializable {
/**
* value: 單表頭如下 如果多表頭 則 @ExcelProperty(value = {"姓名1","姓名2","姓名3"}, index = 0)
* index: 列的號, 0表示第一列
* @ExcelIgnore 生成Excel忽略那個字段
*/
/**
* 姓名
*/
@ExcelProperty(value = "姓名", index = 0)
private String name;
/**
* 年齡
*/
@ExcelProperty(value = "年齡", index = 1)
private String age;
/**
* 性別
*/
@ExcelProperty(value = "性別", index = 2)
private String sex;
}
測試
@Autowired
private ExcelUtil excelUtil;
public void testWright() {
List<User> list = new HashList<>();
for (int i = 0; i < 10000; i++) {
User user = new User();
user.setName(UUID.randomUUID().toString());
user.setAge("10");
user.setSex("男");
list.add(user);
}
excelUtil.writeExcel(response, list, "test", "testsheet", user.getClass());
}
@Autowired
private ExcelUtil excelUtil;
public void testread(){
User user = new User();
List<User> list = excelUtil.simpleRead("報表路徑",user.getClass());
//打印每一個讀取的對象
list.forEach(System.out::println);
}
目前讀取報表的監聽器需要每次在讀取報表時重新new 所以不可以給spring容器管理 只能使用構造方法自己實例化 導致了無法獲取容器裏面的bean實例