PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/web/src/main/java/com/sishuok/es/showcase/excel/service/ExcelDataService.java

https://github.com/kissys/es
Java | 799 lines | 565 code | 137 blank | 97 comment | 55 complexity | e4b8f0d86eab02f334334ecc295774b2 MD5 | raw file
Possible License(s): Apache-2.0
  1. /**
  2. * Copyright (c) 2005-2012 https://github.com/zhangkaitao
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. */
  6. package com.sishuok.es.showcase.excel.service;
  7. import com.google.common.collect.Lists;
  8. import com.google.common.collect.Maps;
  9. import com.sishuok.es.common.entity.search.SearchOperator;
  10. import com.sishuok.es.common.entity.search.Searchable;
  11. import com.sishuok.es.common.repository.RepositoryHelper;
  12. import com.sishuok.es.common.service.BaseService;
  13. import com.sishuok.es.common.utils.FileCharset;
  14. import com.sishuok.es.maintain.editor.web.controller.utils.CompressUtils;
  15. import com.sishuok.es.maintain.notification.service.NotificationApi;
  16. import com.sishuok.es.showcase.excel.entity.ExcelData;
  17. import com.sishuok.es.showcase.excel.repository.ExcelDataRepository;
  18. import com.sishuok.es.sys.user.entity.User;
  19. import org.apache.commons.io.FileUtils;
  20. import org.apache.commons.io.FilenameUtils;
  21. import org.apache.commons.io.IOUtils;
  22. import org.apache.commons.io.LineIterator;
  23. import org.apache.commons.lang3.StringUtils;
  24. import org.apache.commons.lang3.time.DateFormatUtils;
  25. import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
  26. import org.apache.poi.hssf.eventusermodel.HSSFRequest;
  27. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  28. import org.apache.poi.openxml4j.opc.OPCPackage;
  29. import org.apache.poi.poifs.filesystem.POIFSFileSystem;
  30. import org.apache.poi.ss.usermodel.Cell;
  31. import org.apache.poi.ss.usermodel.Row;
  32. import org.apache.poi.ss.usermodel.Sheet;
  33. import org.apache.poi.xssf.eventusermodel.XSSFReader;
  34. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
  35. import org.slf4j.Logger;
  36. import org.slf4j.LoggerFactory;
  37. import org.springframework.aop.framework.AopContext;
  38. import org.springframework.beans.factory.annotation.Autowired;
  39. import org.springframework.data.domain.Page;
  40. import org.springframework.scheduling.annotation.Async;
  41. import org.springframework.stereotype.Service;
  42. import org.xml.sax.ContentHandler;
  43. import org.xml.sax.InputSource;
  44. import org.xml.sax.XMLReader;
  45. import org.xml.sax.helpers.XMLReaderFactory;
  46. import java.io.*;
  47. import java.util.*;
  48. /**
  49. * <p>User: Zhang Kaitao
  50. * <p>Date: 13-2-4 下午3:01
  51. * <p>Version: 1.0
  52. */
  53. @Service
  54. public class ExcelDataService extends BaseService<ExcelData, Long> {
  55. private final Logger log = LoggerFactory.getLogger(ExcelDataService.class);
  56. private int batchSize = 1000; //批处理大小
  57. private int pageSize = 1000;//查询时每页大小
  58. /**
  59. * 导出文件的最大大小 超过这个大小会压缩
  60. */
  61. private final int MAX_EXPORT_FILE_SIZE = 10 * 1024 * 1024; //10MB
  62. private ExcelDataRepository getExcelDataRepository() {
  63. return (ExcelDataRepository) baseRepository;
  64. }
  65. @Autowired
  66. private NotificationApi notificationApi;
  67. private final String storePath = "upload/excel";
  68. private final String EXPORT_FILENAME_PREFIX = "excel";
  69. public void setNotificationApi(final NotificationApi notificationApi) {
  70. this.notificationApi = notificationApi;
  71. }
  72. @Async
  73. public void initOneMillionData(final User user) {
  74. ExcelDataService proxy = (ExcelDataService) AopContext.currentProxy();
  75. long beginTime = System.currentTimeMillis();
  76. getExcelDataRepository().truncate();
  77. final int ONE_MILLION = 1000000; //100w
  78. for(int i = batchSize; i <= ONE_MILLION; i += batchSize) {
  79. //不能使用AopContext.currentProxy() 因为task:annotation-driven没有暴露proxy。。
  80. proxy.doBatchSave(i - batchSize);
  81. }
  82. long endTime = System.currentTimeMillis();
  83. Map<String, Object> context = Maps.newHashMap();
  84. context.put("seconds", (endTime - beginTime) / 1000);
  85. notificationApi.notify(user.getId(), "excelInitDataSuccess", context);
  86. }
  87. public void doBatchSave(final int fromId) {
  88. for(int i = 1; i <= batchSize; i++) {
  89. Long id = Long.valueOf(fromId + i);
  90. String content = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
  91. getExcelDataRepository().save(id, content);
  92. }
  93. }
  94. /**
  95. * 如果主键冲突 覆盖,否则新增
  96. * @param dataList
  97. */
  98. public void doBatchSave(final List<ExcelData> dataList) {
  99. for(ExcelData data : dataList) {
  100. ExcelData dbData = findOne(data.getId());
  101. if(dbData == null) {
  102. getExcelDataRepository().save(data.getId(), data.getContent());
  103. } else {
  104. dbData.setContent(data.getContent());
  105. update(dbData);
  106. }
  107. }
  108. }
  109. /**
  110. * csv格式
  111. * @param user
  112. * @param is
  113. */
  114. @Async
  115. public void importCvs(final User user, final InputStream is) {
  116. ExcelDataService proxy = ((ExcelDataService)AopContext.currentProxy());
  117. BufferedInputStream bis = null;
  118. try {
  119. long beginTime = System.currentTimeMillis();
  120. bis = new BufferedInputStream(is);
  121. String encoding = FileCharset.getCharset(bis);
  122. LineIterator iterator = IOUtils.lineIterator(bis, encoding);
  123. String separator = ",";
  124. int totalSize = 0; //总大小
  125. final List<ExcelData> dataList = Lists.newArrayList();
  126. if (iterator.hasNext()) {
  127. iterator.nextLine();//跳过第一行标题
  128. }
  129. while (iterator.hasNext()) {
  130. totalSize++;
  131. String line = iterator.nextLine();
  132. String[] dataArray = StringUtils.split(line, separator);
  133. ExcelData data = new ExcelData();
  134. data.setId(Long.valueOf(dataArray[0]));
  135. data.setContent(dataArray[1]);
  136. dataList.add(data);
  137. if (totalSize % batchSize == 0) {
  138. try {
  139. proxy.doBatchSave(dataList);
  140. } catch (Exception e) {
  141. Long fromId = dataList.get(0).getId();
  142. Long endId = dataList.get(dataList.size() - 1).getId();
  143. log.error("from " + fromId + " to " + endId + ", error", e);
  144. }
  145. dataList.clear();
  146. }
  147. }
  148. if(dataList.size() > 0) {
  149. proxy.doBatchSave(dataList);
  150. }
  151. long endTime = System.currentTimeMillis();
  152. Map<String, Object> context = Maps.newHashMap();
  153. context.put("seconds", (endTime - beginTime) / 1000);
  154. notificationApi.notify(user.getId(), "excelImportSuccess", context);
  155. } catch (Exception e) {
  156. log.error("excel import error", e);
  157. Map<String, Object> context = Maps.newHashMap();
  158. context.put("error", e.getMessage());
  159. notificationApi.notify(user.getId(), "excelImportError", context);
  160. } finally {
  161. IOUtils.closeQuietly(bis);
  162. }
  163. }
  164. /**
  165. * 导入 excel 2003 biff格式
  166. * 如果是xml格式的 可以使用SAX(未测试)
  167. * @param user
  168. * @param is
  169. */
  170. @Async
  171. public void importExcel2003(final User user, final InputStream is) {
  172. ExcelDataService proxy = ((ExcelDataService)AopContext.currentProxy());
  173. BufferedInputStream bis = null;
  174. InputStream dis = null;
  175. try {
  176. long beginTime = System.currentTimeMillis();
  177. List<ExcelData> dataList = Lists.newArrayList();
  178. //输入流
  179. bis = new BufferedInputStream(is);
  180. // 创建 org.apache.poi.poifs.filesystem.Filesystem
  181. POIFSFileSystem poifs = new POIFSFileSystem(bis);
  182. // 从输入流 得到 Workbook(excel 部分)流
  183. dis = poifs.createDocumentInputStream("Workbook");
  184. // 构造 HSSFRequest
  185. HSSFRequest req = new HSSFRequest();
  186. // 添加监听器
  187. req.addListenerForAllRecords(new Excel2003ImportListener(proxy, dataList, batchSize));
  188. // 创建事件工厂
  189. HSSFEventFactory factory = new HSSFEventFactory();
  190. // 根据文档输入流处理事件
  191. factory.processEvents(req, dis);
  192. //把最后剩下的不足batchSize大小
  193. if (dataList.size() > 0) {
  194. proxy.doBatchSave(dataList);
  195. }
  196. long endTime = System.currentTimeMillis();
  197. Map<String, Object> context = Maps.newHashMap();
  198. context.put("seconds", (endTime - beginTime) / 1000);
  199. notificationApi.notify(user.getId(), "excelImportSuccess", context);
  200. } catch (Exception e) {
  201. log.error("excel import error", e);
  202. Map<String, Object> context = Maps.newHashMap();
  203. context.put("error", e.getMessage());
  204. notificationApi.notify(user.getId(), "excelImportError", context);
  205. } finally {
  206. // 关闭输入流
  207. IOUtils.closeQuietly(bis);
  208. // 关闭文档流
  209. IOUtils.closeQuietly(dis);
  210. }
  211. }
  212. @Async
  213. public void importExcel2007(final User user, final InputStream is) {
  214. ExcelDataService proxy = ((ExcelDataService)AopContext.currentProxy());
  215. BufferedInputStream bis = null;
  216. try {
  217. long beginTime = System.currentTimeMillis();
  218. List<ExcelData> dataList = Lists.newArrayList();
  219. bis = new BufferedInputStream(is);
  220. OPCPackage pkg = OPCPackage.open(bis);
  221. XSSFReader r = new XSSFReader(pkg);
  222. XMLReader parser =
  223. XMLReaderFactory.createXMLReader();
  224. ContentHandler handler = new Excel2007ImportSheetHandler(proxy, dataList, batchSize);
  225. parser.setContentHandler(handler);
  226. Iterator<InputStream> sheets = r.getSheetsData();
  227. while (sheets.hasNext()) {
  228. InputStream sheet = null;
  229. try {
  230. sheet = sheets.next();
  231. InputSource sheetSource = new InputSource(sheet);
  232. parser.parse(sheetSource);
  233. } catch (Exception e) {
  234. throw e;
  235. } finally {
  236. IOUtils.closeQuietly(sheet);
  237. }
  238. }
  239. //把最后剩下的不足batchSize大小
  240. if (dataList.size() > 0) {
  241. proxy.doBatchSave(dataList);
  242. }
  243. long endTime = System.currentTimeMillis();
  244. Map<String, Object> context = Maps.newHashMap();
  245. context.put("seconds", (endTime - beginTime) / 1000);
  246. notificationApi.notify(user.getId(), "excelImportSuccess", context);
  247. } catch (Exception e) {
  248. log.error("excel import error", e);
  249. Map<String, Object> context = Maps.newHashMap();
  250. context.put("error", e.getMessage());
  251. notificationApi.notify(user.getId(), "excelImportError", context);
  252. } finally {
  253. IOUtils.closeQuietly(bis);
  254. }
  255. }
  256. @Async
  257. public void exportCvs(final User user, final String contextRootPath, final Searchable searchable) {
  258. String encoding = "gbk";
  259. int perSheetRows = 60000; //每个sheet 6w条
  260. int totalRows = 0;
  261. String separator = ",";
  262. Long maxId = 0L;
  263. String fileName = generateFilename(user, contextRootPath, "csv");
  264. File file = new File(fileName);
  265. BufferedOutputStream out = null;
  266. try {
  267. long beginTime = System.currentTimeMillis();
  268. out = new BufferedOutputStream(new FileOutputStream(file));
  269. out.write("编号,内容\n".getBytes(encoding));
  270. while (true) {
  271. totalRows = 0;
  272. Page<ExcelData> page = null;
  273. do {
  274. searchable.setPage(0, pageSize);
  275. //优化分页性能
  276. if(!searchable.containsSearchKey("id_in")) {
  277. searchable.addSearchFilter("id", SearchOperator.gt, maxId);
  278. }
  279. page = findAll(searchable);
  280. for (ExcelData data : page.getContent()) {
  281. out.write(String.valueOf(data.getId()).getBytes(encoding));
  282. out.write(separator.getBytes(encoding));
  283. out.write((data.getContent()).replace(separator, ";").getBytes(encoding));
  284. out.write("\n".getBytes(encoding));
  285. maxId = Math.max(maxId, data.getId());
  286. totalRows++;
  287. }
  288. //clear entity manager
  289. RepositoryHelper.clear();
  290. } while (page.hasNextPage() && totalRows <= perSheetRows);
  291. if (!page.hasNextPage()) {
  292. break;
  293. }
  294. }
  295. IOUtils.closeQuietly(out);
  296. if(needCompress(file)) {
  297. fileName = compressAndDeleteOriginal(fileName);
  298. }
  299. long endTime = System.currentTimeMillis();
  300. Map<String, Object> context = Maps.newHashMap();
  301. context.put("seconds", (endTime - beginTime) / 1000);
  302. context.put("url", fileName.replace(contextRootPath, ""));
  303. notificationApi.notify(user.getId(), "excelExportSuccess", context);
  304. } catch (Exception e) {
  305. IOUtils.closeQuietly(out);
  306. log.error("excel export error", e);
  307. Map<String, Object> context = Maps.newHashMap();
  308. context.put("error", e.getMessage());
  309. notificationApi.notify(user.getId(), "excelExportError", context);
  310. }
  311. }
  312. /**
  313. * 写多个workbook
  314. * 1、给用户一个vbs 脚本合并
  315. * 2、给用户写一个c#程序合并
  316. * 不想这么麻烦 需要时再写吧,还不如直接让用户装office 2007 更简单。
  317. * @param user
  318. * @param contextRootPath
  319. * @param searchable
  320. */
  321. @Async
  322. public void exportExcel2003WithOneSheetPerWorkBook(final User user, final String contextRootPath, final Searchable searchable) {
  323. int workbookCount = 0;
  324. List<String> workbookFileNames = new ArrayList<String>();
  325. int perSheetRows = 60000; //每个sheet 6w条
  326. int totalRows = 0;
  327. String extension = "xls";
  328. int pageSize = 1000;
  329. Long maxId = 0L;
  330. BufferedOutputStream out = null;
  331. try {
  332. long beginTime = System.currentTimeMillis();
  333. while (true) {
  334. workbookCount++;
  335. String fileName = generateFilename(user, contextRootPath, workbookCount, extension);
  336. workbookFileNames.add(fileName);
  337. File file = new File(fileName);
  338. HSSFWorkbook wb = new HSSFWorkbook();
  339. Sheet sheet = wb.createSheet();
  340. Row headerRow = sheet.createRow(0);
  341. Cell idHeaderCell = headerRow.createCell(0);
  342. idHeaderCell.setCellValue("编号");
  343. Cell contentHeaderCell = headerRow.createCell(1);
  344. contentHeaderCell.setCellValue("内容");
  345. totalRows = 1;
  346. Page<ExcelData> page = null;
  347. do {
  348. searchable.setPage(0, pageSize);
  349. //优化分页性能
  350. if(!searchable.containsSearchKey("id_in")) {
  351. searchable.addSearchFilter("id", SearchOperator.gt, maxId);
  352. }
  353. page = findAll(searchable);
  354. for (ExcelData data : page.getContent()) {
  355. Row row = sheet.createRow(totalRows);
  356. Cell idCell = row.createCell(0);
  357. idCell.setCellValue(data.getId());
  358. Cell contentCell = row.createCell(1);
  359. contentCell.setCellValue(data.getContent());
  360. maxId = Math.max(maxId, data.getId());
  361. totalRows++;
  362. }
  363. //clear entity manager
  364. RepositoryHelper.clear();
  365. } while (page.hasNextPage() && totalRows <= perSheetRows);
  366. out = new BufferedOutputStream(new FileOutputStream(file));
  367. wb.write(out);
  368. IOUtils.closeQuietly(out);
  369. if (!page.hasNextPage()) {
  370. break;
  371. }
  372. }
  373. String fileName = workbookFileNames.get(0);
  374. if (workbookCount > 1 || needCompress(new File(fileName))) {
  375. fileName = fileName.substring(0, fileName.lastIndexOf("_")) + ".zip";
  376. //去掉索引
  377. compressAndDeleteOriginal(fileName, workbookFileNames.toArray(new String[0]));
  378. } else {
  379. String newFileName = fileName.substring(0, fileName.lastIndexOf("_")) + "." + extension;
  380. FileUtils.moveFile(new File(fileName), new File(newFileName));
  381. fileName = newFileName;
  382. }
  383. long endTime = System.currentTimeMillis();
  384. Map<String, Object> context = Maps.newHashMap();
  385. context.put("seconds", (endTime - beginTime) / 1000);
  386. context.put("url", fileName.replace(contextRootPath, ""));
  387. notificationApi.notify(user.getId(), "excelExportSuccess", context);
  388. } catch (Exception e) {
  389. e.printStackTrace();
  390. //以防万一
  391. IOUtils.closeQuietly(out);
  392. log.error("excel export error", e);
  393. Map<String, Object> context = Maps.newHashMap();
  394. context.put("error", e.getMessage());
  395. notificationApi.notify(user.getId(), "excelExportError", context);
  396. }
  397. }
  398. /**
  399. * excel 2003
  400. * 1、生成模板,另存为:XML表格
  401. * 2、需要在导出之前,先做好模板(即先到excel中定义好格式,不支持复杂格式,如图片等),然后输出数)
  402. * 3、复杂格式请考虑testExportExcel2003_3
  403. * <p/>
  404. * 生成的文件巨大。。
  405. * <p/>
  406. * 还一种是写html(缺点不支持多sheet)
  407. * @param user
  408. * @param contextRootPath
  409. * @param searchable
  410. */
  411. @Async
  412. public void exportExcel2003WithXml(final User user, final String contextRootPath, final Searchable searchable) {
  413. int perSheetRows = 60000; //每个sheet 6w条
  414. int totalSheets = 0;
  415. int totalRows = 0;
  416. Long maxId = 0L;
  417. String templateEncoding = "utf-8";
  418. String fileName = generateFilename(user, contextRootPath, "xls");
  419. File file = new File(fileName);
  420. BufferedOutputStream out = null;
  421. try {
  422. long beginTime = System.currentTimeMillis();
  423. String workBookHeader = IOUtils.toString(ExcelDataService.class.getResourceAsStream("excel_2003_xml_workbook_header.txt"));
  424. String workBookFooter = IOUtils.toString(ExcelDataService.class.getResourceAsStream("excel_2003_xml_workbook_footer.txt"));
  425. String sheetHeader = IOUtils.toString(ExcelDataService.class.getResourceAsStream("excel_2003_xml_sheet_header.txt"));
  426. String sheetFooter = IOUtils.toString(ExcelDataService.class.getResourceAsStream("excel_2003_xml_sheet_footer.txt"));
  427. String rowTemplate = IOUtils.toString(ExcelDataService.class.getResourceAsStream("excel_2003_xml_row.txt"));
  428. out = new BufferedOutputStream(new FileOutputStream(file));
  429. out.write(workBookHeader.getBytes(templateEncoding));
  430. while (true) {
  431. totalSheets++;
  432. out.write(sheetHeader.replace("{sheetName}", "Sheet" + totalSheets).getBytes(templateEncoding));
  433. Page<ExcelData> page = null;
  434. totalRows = 1;
  435. do {
  436. searchable.setPage(0, pageSize);
  437. //优化分页性能
  438. if(!searchable.containsSearchKey("id_in")) {
  439. searchable.addSearchFilter("id", SearchOperator.gt, maxId);
  440. }
  441. page = findAll(searchable);
  442. for (ExcelData data : page.getContent()) {
  443. out.write(rowTemplate.replace("{id}", String.valueOf(data.getId())).replace("{content}", data.getContent()).getBytes(templateEncoding));
  444. maxId = Math.max(maxId, data.getId());
  445. totalRows++;
  446. }
  447. //clear entity manager
  448. RepositoryHelper.clear();
  449. } while (page.hasNextPage() && totalRows <= perSheetRows);
  450. out.write(sheetFooter.getBytes(templateEncoding));
  451. if (!page.hasNextPage()) {
  452. break;
  453. }
  454. }
  455. out.write(workBookFooter.getBytes(templateEncoding));
  456. IOUtils.closeQuietly(out);
  457. if (needCompress(file)) {
  458. fileName = compressAndDeleteOriginal(fileName);
  459. }
  460. long endTime = System.currentTimeMillis();
  461. Map<String, Object> context = Maps.newHashMap();
  462. context.put("seconds", (endTime - beginTime) / 1000);
  463. context.put("url", fileName.replace(contextRootPath, ""));
  464. notificationApi.notify(user.getId(), "excelExportSuccess", context);
  465. } catch (Exception e) {
  466. e.printStackTrace();
  467. IOUtils.closeQuietly(out);
  468. log.error("excel export error", e);
  469. Map<String, Object> context = Maps.newHashMap();
  470. context.put("error", e.getMessage());
  471. notificationApi.notify(user.getId(), "excelExportError", context);
  472. }
  473. }
  474. /**
  475. * excel 2003
  476. * 不支持大数据量
  477. * 每个sheet最多65536行(因为是usermodel模型,数据先写到内存 最后flush出去 不支持大数据量导出)
  478. * @param user
  479. * @param contextRootPath
  480. * @param searchable
  481. */
  482. @Async
  483. public void exportExcel2003WithUsermodel(final User user, final String contextRootPath, final Searchable searchable) {
  484. int perSheetRows = 60000; //每个sheet 6w条
  485. int totalRows = 0;
  486. Long maxId = 0L;
  487. String fileName = generateFilename(user, contextRootPath, "xls");
  488. File file = new File(fileName);
  489. BufferedOutputStream out = null;
  490. try {
  491. long beginTime = System.currentTimeMillis();
  492. HSSFWorkbook wb = new HSSFWorkbook();
  493. while (true) {
  494. Sheet sheet = wb.createSheet();
  495. Row headerRow = sheet.createRow(0);
  496. Cell idHeaderCell = headerRow.createCell(0);
  497. idHeaderCell.setCellValue("编号");
  498. Cell contentHeaderCell = headerRow.createCell(1);
  499. contentHeaderCell.setCellValue("内容");
  500. totalRows = 1;
  501. Page<ExcelData> page = null;
  502. do {
  503. searchable.setPage(0, pageSize);
  504. //优化分页性能
  505. if(!searchable.containsSearchKey("id_in")) {
  506. searchable.addSearchFilter("id", SearchOperator.gt, maxId);
  507. }
  508. page = findAll(searchable);
  509. for (ExcelData data : page.getContent()) {
  510. Row row = sheet.createRow(totalRows);
  511. Cell idCell = row.createCell(0);
  512. idCell.setCellValue(data.getId());
  513. Cell contentCell = row.createCell(1);
  514. contentCell.setCellValue(data.getContent());
  515. maxId = Math.max(maxId, data.getId());
  516. totalRows++;
  517. }
  518. //clear entity manager
  519. RepositoryHelper.clear();
  520. } while (page.hasNextPage() && totalRows <= perSheetRows);
  521. if (!page.hasNextPage()) {
  522. break;
  523. }
  524. }
  525. out = new BufferedOutputStream(new FileOutputStream(file));
  526. wb.write(out);
  527. IOUtils.closeQuietly(out);
  528. if(needCompress(file)) {
  529. fileName = compressAndDeleteOriginal(fileName);
  530. }
  531. long endTime = System.currentTimeMillis();
  532. Map<String, Object> context = Maps.newHashMap();
  533. context.put("seconds", (endTime - beginTime) / 1000);
  534. context.put("url", fileName.replace(contextRootPath, ""));
  535. notificationApi.notify(user.getId(), "excelExportSuccess", context);
  536. } catch (Exception e) {
  537. IOUtils.closeQuietly(out);
  538. log.error("excel export error", e);
  539. Map<String, Object> context = Maps.newHashMap();
  540. context.put("error", e.getMessage());
  541. notificationApi.notify(user.getId(), "excelExportError", context);
  542. }
  543. }
  544. /**
  545. * 支持大数据量导出
  546. * excel 2007 每个sheet最多1048576行
  547. * @param user
  548. * @param contextRootPath
  549. * @param searchable
  550. */
  551. @Async
  552. public void exportExcel2007(final User user, final String contextRootPath, final Searchable searchable) {
  553. int rowAccessWindowSize = 1000; //内存中保留的行数,超出后会写到磁盘
  554. int perSheetRows = 100000; //每个sheet 10w条
  555. int totalRows = 0; //统计总行数
  556. Long maxId = 0L;//当前查询的数据中最大的id 优化分页的
  557. String fileName = generateFilename(user, contextRootPath, "xlsx");
  558. File file = new File(fileName);
  559. BufferedOutputStream out = null;
  560. SXSSFWorkbook wb = null;
  561. try {
  562. long beginTime = System.currentTimeMillis();
  563. wb = new SXSSFWorkbook(rowAccessWindowSize);
  564. wb.setCompressTempFiles(true);//生成的临时文件将进行gzip压缩
  565. while (true) {
  566. Sheet sheet = wb.createSheet();
  567. Row headerRow = sheet.createRow(0);
  568. Cell idHeaderCell = headerRow.createCell(0);
  569. idHeaderCell.setCellValue("编号");
  570. Cell contentHeaderCell = headerRow.createCell(1);
  571. contentHeaderCell.setCellValue("内容");
  572. totalRows = 1;
  573. Page<ExcelData> page = null;
  574. do {
  575. searchable.setPage(0, pageSize);
  576. //优化分页性能
  577. if(!searchable.containsSearchKey("id_in")) {
  578. searchable.addSearchFilter("id", SearchOperator.gt, maxId);
  579. }
  580. page = findAll(searchable);
  581. for (ExcelData data : page.getContent()) {
  582. Row row = sheet.createRow(totalRows);
  583. Cell idCell = row.createCell(0);
  584. idCell.setCellValue(data.getId());
  585. Cell contentCell = row.createCell(1);
  586. contentCell.setCellValue(data.getContent());
  587. maxId = Math.max(maxId, data.getId());
  588. totalRows++;
  589. }
  590. //clear entity manager
  591. RepositoryHelper.clear();
  592. } while (page.hasNextPage() && totalRows <= perSheetRows);
  593. if (!page.hasNextPage()) {
  594. break;
  595. }
  596. }
  597. out = new BufferedOutputStream(new FileOutputStream(file));
  598. wb.write(out);
  599. IOUtils.closeQuietly(out);
  600. if (needCompress(file)) {
  601. fileName = compressAndDeleteOriginal(fileName);
  602. }
  603. long endTime = System.currentTimeMillis();
  604. Map<String, Object> context = Maps.newHashMap();
  605. context.put("seconds", (endTime - beginTime) / 1000);
  606. context.put("url", fileName.replace(contextRootPath, ""));
  607. notificationApi.notify(user.getId(), "excelExportSuccess", context);
  608. } catch (Exception e) {
  609. IOUtils.closeQuietly(out);
  610. log.error("excel export error", e);
  611. Map<String, Object> context = Maps.newHashMap();
  612. context.put("error", e.getMessage());
  613. notificationApi.notify(user.getId(), "excelExportError", context);
  614. } finally {
  615. // 清除本工作簿备份在磁盘上的临时文件
  616. wb.dispose();
  617. }
  618. }
  619. private String compressAndDeleteOriginal(final String filename) {
  620. String newFileName = FilenameUtils.removeExtension(filename) + ".zip";
  621. compressAndDeleteOriginal(newFileName, filename);
  622. return newFileName;
  623. }
  624. private void compressAndDeleteOriginal(final String newFileName, final String... needCompressFilenames) {
  625. CompressUtils.zip(newFileName, needCompressFilenames);
  626. for(String needCompressFilename : needCompressFilenames) {
  627. FileUtils.deleteQuietly(new File(needCompressFilename));
  628. }
  629. }
  630. private boolean needCompress(final File file) {
  631. return file.length() > MAX_EXPORT_FILE_SIZE;
  632. }
  633. /**
  634. * 生成要导出的文件名
  635. * @param user
  636. * @param contextRootPath
  637. * @param extension
  638. * @return
  639. */
  640. private String generateFilename(final User user, final String contextRootPath, final String extension) {
  641. return generateFilename(user, contextRootPath, null, extension);
  642. }
  643. private String generateFilename(final User user, final String contextRootPath, Integer index, final String extension) {
  644. String path = FilenameUtils.concat(contextRootPath, storePath);
  645. path = FilenameUtils.concat(path, user.getUsername());
  646. path = FilenameUtils.concat(
  647. path,
  648. EXPORT_FILENAME_PREFIX + "_" +
  649. DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") +
  650. (index != null ? ("_" + index) : "") +
  651. "." + extension);
  652. File file = new File(path);
  653. if(!file.exists()) {
  654. File parentFile = file.getParentFile();
  655. if(!parentFile.exists()) {
  656. parentFile.mkdirs();
  657. }
  658. return path;
  659. }
  660. return generateFilename(user, contextRootPath, extension);
  661. }
  662. }