首先大数据量的下载,一般的Excel下载操作是不可能完成的,会导致内存溢出

  SXSSFWorkbook 是专门用于大数据了的导出  

  这个参数,会指定一个sheet可读取的row数目,超过该数目的row,会被写入到磁盘文件中,进而不能在通过getRow访问到,通过这种方式,内存使用空间就缩小很多了。 需要注意的是,如果该值指定为-1,说明不限制行数,所有记录都写入内存中;该值不能取0,因为这意味着任何新row都会写入磁盘,进而不能访问,但是新row还没来得及createCell

  当写sheet的时候,临时文件可能远比结果文件要大,所以提供了压缩临时文件的接口,通过压缩,磁盘上的xml的临时文件将会被打包,进而降低磁盘占用空间。


 先上代码再说

  1. 1 /**
  2. 2 * Excel通用下载方法
  3. 3 * @param request
  4. 4 * @param response
  5. 5 * @param fileName
  6. 6 * 文件名
  7. 7 * @param titleNameList
  8. 8 * 标题头部
  9. 9 * @param cellNameList
  10. 10 * 行字段的name,主要用于map的get(),无则为null
  11. 11 * @param dataList
  12. 12 * 内容 支持类型 对象 map集合
  13. 13 */
  14. 14 public static <E> void downLoad(HttpServletRequest request, HttpServletResponse response, String fileName,
  15. 15 List<String> titleNameList,List<String> cellNameList, List<E> dataList) {
  16. 16 OutputStream os = null;
  17. 17 try {
  18. 18 response.setContentType("application/force-download"); // 设置下载类型
  19. 19
  20. 20 response.setHeader("Content-Disposition", "attachment;filename=" + fileName); // 设置文件的名称
  21. 21 os = response.getOutputStream(); // 输出流
  22. 22 SXSSFWorkbook wb = new SXSSFWorkbook(1000);// 内存中保留 1000 条数据,以免内存溢出,其余写入 硬盘
  23. 23 // 获得该工作区的第一个sheet
  24. 24 Sheet sheet1 = wb.createSheet("sheet1");
  25. 25 String mapstring=null;
  26. 26 int excelRow = 0;
  27. 27 List<String> fieldNameList = new ArrayList<>();
  28. 28 // 标题行
  29. 29 Row titleRow = (Row) sheet1.createRow(excelRow++);
  30. 30 int titleSize = titleNameList.size();
  31. 31 for (int i = 0; i < titleSize; i++) {
  32. 32 Cell cell = titleRow.createCell(i);
  33. 33 cell.setCellValue(titleNameList.get(i));
  34. 34 }
  35. 35 int dataSize = dataList.size();
  36. 36 for (int i = 0; i < dataSize; i++) {
  37. 37 // 明细行
  38. 38 Row contentRow = (Row) sheet1.createRow(excelRow++);
  39. 39 Object object = dataList.get(i);
  40. 40 if (object instanceof Map) {
  41. 41 Map<String, Object> objectMap = (Map<String, Object>) object;
  42. 42 for (int j = 0; j < titleSize; j++) {
  43. 43 mapstring=objectMap.get(cellNameList.get(j))+"";
  44. 44 Cell cell = contentRow.createCell(j);
  45. 45 if (!"".equals(mapstring)&&mapstring!=null&&!"null".equals(mapstring)) {
  46. 46 cell.setCellValue(mapstring);
  47. 47 }else {
  48. 48 cell.setCellValue("");
  49. 49 }
  50. 50 }
  51. 51 } else {
  52. 52
  53. 53 if (i == 0) {
  54. 54 if (cellNameList!=null&&cellNameList.size()>0) {
  55. 55 fieldNameList.addAll(cellNameList);
  56. 56 }else {
  57. 57 Field[] fields = object.getClass().getDeclaredFields();
  58. 58 for (Field field : fields) {
  59. 59 field.setAccessible(true);
  60. 60 fieldNameList.add(field.getName());
  61. 61 }
  62. 62 }
  63. 63 }
  64. 64 for (int j = 0; j < titleSize; j++) {
  65. 65 Cell cell = contentRow.createCell(j);
  66. 66 Field field = object.getClass().getDeclaredField(fieldNameList.get(j));
  67. 67 field.setAccessible(true);
  68. 68 Object fieldObj=field.get(object) ;
  69. 69 if (fieldObj != null&&!"null".equals(fieldObj)) {
  70. 70 cell.setCellValue(fieldObj.toString());
  71. 71 } else {
  72. 72 cell.setCellValue("");
  73. 73 }
  74. 74
  75. 75 }
  76. 76
  77. 77
  78. 78 }
  79. 79 }
  80. 80 wb.write(os);
  81. 81 } catch (Exception e) {
  82. 82 e.printStackTrace();
  83. 83 } finally {
  84. 84 try {
  85. 85 if (os != null) {
  86. 86 os.close();
  87. 87 }
  88. 88 } catch (IOException e) {
  89. 89 e.printStackTrace();
  90. 90 } // 关闭输出流
  91. 91 }
  92. 92
  93. 93 }

 

读取大数据的Excel文件

 

直接上代码 

  1. public class SheetHandler extends DefaultHandler {
  2. /**
  3. * 单元格中的数据可能的数据类型
  4. */
  5. enum CellDataType {
  6. BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER, DATE, NULL
  7. }
  8. /**
  9. * 共享字符串表
  10. */
  11. private SharedStringsTable sst;
  12. /**
  13. * 上一次的索引值
  14. */
  15. private String lastIndex;
  16. /**
  17. * 文件的绝对路径
  18. */
  19. private String filePath = "";
  20. /**
  21. * 工作表索引
  22. */
  23. private int sheetIndex = 0;
  24. /**
  25. * sheet名
  26. */
  27. private String sheetName = "";
  28. /**
  29. * 总行数
  30. */
  31. private int totalRows=0;
  32. /**
  33. * 一行内cell集合
  34. */
  35. private List<String> cellList = new ArrayList<String>();
  36. /**
  37. * 判断整行是否为空行的标记
  38. */
  39. private boolean flag = false;
  40. /**
  41. * 当前行
  42. */
  43. private int curRow = 1;
  44. /**
  45. * 当前列
  46. */
  47. private int curCol = 0;
  48. /**
  49. * T元素标识
  50. */
  51. private boolean isTElement;
  52. /**
  53. * 异常信息,如果为空则表示没有异常
  54. */
  55. private String exceptionMessage;
  56. /**
  57. * 单元格数据类型,默认为字符串类型
  58. */
  59. private CellDataType nextDataType = CellDataType.SSTINDEX;
  60. private final DataFormatter formatter = new DataFormatter();
  61. /**
  62. * 单元格日期格式的索引
  63. */
  64. private short formatIndex;
  65. /**
  66. * 日期格式字符串
  67. */
  68. private String formatString;
  69. //定义前一个元素和当前元素的位置,用来计算其中空的单元格数量,如A6和A8等
  70. private String preRef = null, ref = null;
  71. //定义该文档一行最大的单元格数,用来补全一行最后可能缺失的单元格
  72. private String maxRef = null;
  73. private int maxCol=0;
  74. private int nowcol;
  75. /**
  76. * 单元格
  77. */
  78. private StylesTable stylesTable;
  79. /**
  80. * 日期格式化 yyyy-MM-dd
  81. */
  82. private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
  83. /**
  84. * 日期格式化
  85. */
  86. private SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  87. /**
  88. * 现在的时间
  89. */
  90. private String dateTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
  91. /**
  92. * 一个表格的所有数据
  93. */
  94. private List<List<String>> sheetList=new ArrayList<>();
  95. /**
  96. * 操作人员
  97. */
  98. private String userName;
  99. private static int startElements=0;
  100. private static int endElement=0;
  101. /* public static void main(String[] args) {
  102. SheetHandler sheetHandler=new SheetHandler();
  103. File file=new File("C:\\Users\\Administrator\\Desktop\\jichu.xlsx");
  104. try {
  105. sheetHandler.process(new FileInputStream(file), "周光林");
  106. } catch (FileNotFoundException e) {
  107. // TODO Auto-generated catch block
  108. e.printStackTrace();
  109. } catch (Exception e) {
  110. // TODO Auto-generated catch block
  111. e.printStackTrace();
  112. }
  113. }*/
  114. /**
  115. * 遍历工作簿中所有的电子表格
  116. * 并缓存在mySheetList中
  117. *
  118. * @param filename
  119. * @throws Exception
  120. */
  121. public List<List<String>> process(InputStream in,String userName) throws Exception {
  122. this.userName=userName;
  123. OPCPackage pkg = OPCPackage.open(in);
  124. XSSFReader xssfReader = new XSSFReader(pkg);
  125. stylesTable = xssfReader.getStylesTable();
  126. SharedStringsTable sst = xssfReader.getSharedStringsTable();
  127. XMLReader parser = XMLReaderFactory.createXMLReader();
  128. this.sst = sst;
  129. parser.setContentHandler(this);
  130. XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
  131. while (sheets.hasNext()) { //遍历sheet
  132. curRow = 1; //标记初始行为第一行
  133. sheetIndex++;
  134. InputStream sheet = sheets.next(); //sheets.next()和sheets.getSheetName()不能换位置,否则sheetName报错
  135. sheetName = sheets.getSheetName();
  136. // System.err.println(new BufferedReader(new InputStreamReader(sheet)).readLine());
  137. InputSource sheetSource = new InputSource(sheet);
  138. parser.parse(sheetSource); //解析excel的每条记录,在这个过程中startElement()、characters()、endElement()这三个函数会依次执行
  139. sheet.close();
  140. }
  141. if (in!=null) {
  142. in.close();
  143. }
  144. return sheetList; //返回该excel文件的总行数,不包括首列和空行
  145. }
  146. /**
  147. * 第一个执行
  148. *
  149. * @param uri
  150. * @param localName
  151. * @param name
  152. * @param attributes
  153. * @throws SAXException
  154. */
  155. @Override
  156. public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
  157. //c => 单元格
  158. if ("c".equals(name)) {
  159. //当前单元格的位置
  160. ref = attributes.getValue("r");
  161. //设定单元格类型
  162. this.setNextDataType(attributes);
  163. }
  164. //当元素为t时
  165. if ("t".equals(name)) {
  166. isTElement = true;
  167. } else {
  168. isTElement = false;
  169. }
  170. //置空
  171. lastIndex = "";
  172. }
  173. /**
  174. * 第二个执行
  175. * 得到单元格对应的索引值或是内容值
  176. * 如果单元格类型是字符串、INLINESTR、数字、日期,lastIndex则是索引值
  177. * 如果单元格类型是布尔值、错误、公式,lastIndex则是内容值
  178. * @param ch
  179. * @param start
  180. * @param length
  181. * @throws SAXException
  182. */
  183. @Override
  184. public void characters(char[] ch, int start, int length) throws SAXException {
  185. lastIndex += new String(ch, start, length);
  186. }
  187. /**
  188. * 第三个执行
  189. *
  190. * @param uri
  191. * @param localName
  192. * @param name
  193. * @throws SAXException
  194. */
  195. @Override
  196. public void endElement(String uri, String localName, String name) throws SAXException {
  197. //t元素也包含字符串
  198. if (isTElement) {//这个程序没经过
  199. //将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
  200. String value = lastIndex.trim();
  201. cellList.add(curCol, value);
  202. curCol++;
  203. isTElement = false;
  204. //如果里面某个单元格含有值,则标识该行不为空行
  205. if (value != null && !"".equals(value)) {
  206. flag = true;
  207. }
  208. } else if ("v".equals(name)) {
  209. //v => 单元格的值,如果单元格是字符串,则v标签的值为该字符串在SST中的索引
  210. String value = this.getDataValue(lastIndex.trim(), "");//根据索引值获取对应的单元格值
  211. //补全单元格之间的空单元格
  212. if (preRef!=null) {
  213. if (!ref.equals(preRef)) {
  214. int len = countNullCell(ref, preRef);
  215. for (int i = 0; i < len; i++) {
  216. cellList.add(curCol, "");
  217. curCol++;
  218. }
  219. }
  220. }
  221. preRef=ref;
  222. cellList.add(curCol, value);
  223. curCol++;
  224. //如果里面某个单元格含有值,则标识该行不为空行
  225. if (value != null && !"".equals(value)) {
  226. flag = true;
  227. }
  228. } else {
  229. //如果标签名称为row,这说明已到行尾,调用optRows()方法
  230. if ("row".equals(name)) {
  231. //默认第一行为表头,以该行单元格数目为最大数目
  232. if (curRow == 1) {
  233. maxRef = ref;
  234. maxCol=curCol;
  235. }
  236. //补全一行尾部可能缺失的单元格
  237. if (maxRef != null) {
  238. int len = countNullCell(maxRef, ref);
  239. for (int i = 0; i <= len; i++) {
  240. cellList.add(curCol, "");
  241. curCol++;
  242. }
  243. }
  244. if (flag&&curRow!=1){ //该行不为空行且该行不是第一行,则发送(第一行为列名,不需要)
  245. nowcol=cellList.size();
  246. if (cellList.size()<maxCol) {
  247. for (int i = 0; i <maxCol-nowcol; i++) {
  248. cellList.add("");
  249. }
  250. }
  251. if (nowcol>maxCol) {
  252. for (int i = nowcol-1; i >maxCol-1; i--) {
  253. cellList.remove(i);
  254. }
  255. }
  256. cellList.add(userName);
  257. cellList.add(dateTime);
  258. sheetList.add(new ArrayList<>(cellList));
  259. totalRows++;
  260. }
  261. cellList.clear();
  262. curRow++;
  263. curCol = 0;
  264. preRef = null;
  265. ref = null;
  266. flag=false;
  267. }
  268. }
  269. }
  270. /**
  271. * 处理数据类型
  272. *
  273. * @param attributes
  274. */
  275. public void setNextDataType(Attributes attributes) {
  276. nextDataType = CellDataType.NUMBER; //cellType为空,则表示该单元格类型为数字
  277. formatIndex = -1;
  278. formatString = null;
  279. String cellType = attributes.getValue("t"); //单元格类型
  280. String cellStyleStr = attributes.getValue("s"); //
  281. String columnData = attributes.getValue("r"); //获取单元格的位置,如A1,B1
  282.  
  283. if ("b".equals(cellType)) { //处理布尔值
  284. nextDataType = CellDataType.BOOL;
  285. } else if ("e".equals(cellType)) { //处理错误
  286. nextDataType = CellDataType.ERROR;
  287. } else if ("inlineStr".equals(cellType)) {
  288. nextDataType = CellDataType.INLINESTR;
  289. } else if ("s".equals(cellType)) { //处理字符串
  290. nextDataType = CellDataType.SSTINDEX;
  291. } else if ("str".equals(cellType)) {
  292. nextDataType = CellDataType.FORMULA;
  293. }
  294. if (cellStyleStr != null) { //处理日期
  295. int styleIndex = Integer.parseInt(cellStyleStr);
  296. XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
  297. formatIndex = style.getDataFormat();
  298. formatString = style.getDataFormatString();
  299. if (formatString == null) {
  300. nextDataType = CellDataType.NULL;
  301. formatString = BuiltinFormats.getBuiltinFormat(formatIndex);
  302. }else if (formatString.contains("m/d/yy") || formatString.contains("yyyy/mm/dd")|| formatString.contains("yyyy/m/d")) {
  303. nextDataType = CellDataType.DATE;
  304. formatString = "yyyy-MM-dd";
  305. }
  306. }
  307. }
  308. /**
  309. * 对解析出来的数据进行类型处理
  310. * @param value 单元格的值,
  311. * value代表解析:BOOL的为0或1, ERROR的为内容值,FORMULA的为内容值,INLINESTR的为索引值需转换为内容值,
  312. * SSTINDEX的为索引值需转换为内容值, NUMBER为内容值,DATE为内容值
  313. * @param thisStr 一个空字符串
  314. * @return
  315. */
  316. @SuppressWarnings("deprecation")
  317. public String getDataValue(String value, String thisStr) {
  318. switch (nextDataType) {
  319. // 这几个的顺序不能随便交换,交换了很可能会导致数据错误
  320. case BOOL: //布尔值
  321. char first = value.charAt(0);
  322. thisStr = first == \'0\' ? "FALSE" : "TRUE";
  323. break;
  324. case ERROR: //错误
  325. thisStr = value.toString();
  326. break;
  327. case FORMULA: //公式
  328. thisStr = \'"\' + value.toString() + \'"\';
  329. break;
  330. case INLINESTR:
  331. XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());
  332. thisStr = rtsi.toString();
  333. rtsi = null;
  334. break;
  335. case SSTINDEX: //字符串
  336. String sstIndex = value.toString();
  337. try {
  338. int idx = Integer.parseInt(sstIndex);
  339. XSSFRichTextString rtss = new XSSFRichTextString(sst.getEntryAt(idx));//根据idx索引值获取内容值
  340. thisStr = rtss.toString();
  341. rtss = null;
  342. } catch (NumberFormatException ex) {
  343. thisStr = value.toString();
  344. }
  345. break;
  346. case NUMBER: //数字
  347. if (formatString != null) {
  348. thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString).trim();
  349. } else {
  350. thisStr = value;
  351. }
  352. thisStr = thisStr.replace("_", "").trim();
  353. break;
  354. case DATE: //日期
  355. try {
  356. thisStr = formatter.formatRawCellContents(Double.parseDouble(value), formatIndex, formatString);
  357. }catch (Exception e) {
  358. thisStr="0000-00-00";
  359. }
  360. // 对日期字符串作特殊处理,去掉T
  361. thisStr = thisStr.replace("T", " ");
  362. break;
  363. default:
  364. if (Integer.parseInt(value)>20000) {
  365. Date date = DateUtil.getJavaDate(Double.parseDouble(value));
  366. thisStr =sdf.format(date);
  367. }else {
  368. thisStr="11111";
  369. }
  370. break;
  371. }
  372. return thisStr;
  373. }
  374. public int countNullCell(String ref, String preRef) {
  375. //excel2007最大行数是1048576,最大列数是16384,最后一列列名是XFD
  376. String xfd = ref.replaceAll("\\d+", "");
  377. String xfd_1 = preRef.replaceAll("\\d+", "");
  378. xfd = fillChar(xfd, 3, \'@\', true);
  379. xfd_1 = fillChar(xfd_1, 3, \'@\', true);
  380. char[] letter = xfd.toCharArray();
  381. char[] letter_1 = xfd_1.toCharArray();
  382. int res = (letter[0] - letter_1[0]) * 26 * 26 + (letter[1] - letter_1[1]) * 26 + (letter[2] - letter_1[2]);
  383. return res - 1;
  384. }
  385. public String fillChar(String str, int len, char let, boolean isPre) {
  386. int len_1 = str.length();
  387. if (len_1 < len) {
  388. if (isPre) {
  389. for (int i = 0; i < (len - len_1); i++) {
  390. str = let + str;
  391. }
  392. } else {
  393. for (int i = 0; i < (len - len_1); i++) {
  394. str = str + let;
  395. }
  396. }
  397. }
  398. return str;
  399. }
  400. /**
  401. * @return the exceptionMessage
  402. */
  403. public String getExceptionMessage() {
  404. return exceptionMessage;
  405. }
  406. }
  1. SheetHandler sheetHandler=new SheetHandler();
  2. List<List<String>> lists=sheetHandler.process(in,userName);

 

版权声明:本文为zhouguanglin原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zhouguanglin/p/9959344.html