无论是easyExcel或者poi等excel处理框架,在生成excel时都需要同步等待操作。但如果数据量较大,生成excel的时间会比较长,对于用户来说需要等待,影响体验。这时候就需要考虑使用流式生成excel。
下面提供了几种方案实现流失生成,能够在页面上直接开启下载。
- 后端处理
excel2017版本之后,文件本身其实是一个zip文件,里面包含了多个xml文件,这些xml文件描述了excel的内容。所以我们可以通过流式生成xml文件,然后将这些xml文件打包成zip文件,最后返回给浏览器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| public ExcelGenerator(InputStream template, Integer dataIndex, OutputStream outputStream) throws IOException { this.index = dataIndex; zos = new ZipOutputStream(outputStream); try (ZipInputStream zipInputStream = new ZipInputStream(template)) { ZipEntry ze = null; String sheet1 = null; while ((ze = zipInputStream.getNextEntry()) != null) { if (StringUtils.equals(ze.getName(), "xl/worksheets/sheet1.xml")) { sheet1 = IOUtils.toString(zipInputStream, StandardCharsets.UTF_8); } else { ZipEntry zeOut = new ZipEntry(ze.getName()); zeOut.setSize(ze.getSize()); zeOut.setTime(ze.getTime()); zos.putNextEntry(zeOut); IOUtils.copy(zipInputStream, zos); zos.flush(); zos.closeEntry();
} } ZipEntry entry = new ZipEntry("xl/worksheets/sheet1.xml"); zos.putNextEntry(entry); int sheetDataIndex = sheet1.indexOf("</sheetData>"); zos.write(sheet1.substring(0, sheetDataIndex).getBytes(StandardCharsets.UTF_8)); finishStr = sheet1.substring(sheetDataIndex); } }
public void render(List<T> views) throws IOException { for (T t : views) { zos.write(renderRowView(t)); } zos.flush(); }
private byte[] renderRowView(T t) { Map<Integer, String> cells = new TreeMap<>(); ReflectionUtils.doWithFields(t.getClass(), f -> { ColumnMeta meta = f.getAnnotation(ColumnMeta.class); try { Object value = PropertyUtils.getProperty(t, f.getName()); ConvertMeta convert = f.getAnnotation(ConvertMeta.class); if (convert != null) { ConvertInfo convertInfo = new ConvertInfo(); convertInfo.setDefaultValue(convert.defaultVaule()); convertInfo.setTargetType(f.getType()); convertInfo.setFormat(convert.format()); IConvert instance = convert.convert().newInstance(); instance.setConvertInfo(convertInfo); value = instance.convert(value); } cells.put(meta.index(), value.toString()); } catch (Exception e) { } }, f -> null != f.getAnnotation(ColumnMeta.class)); byte[] row = ("<row r=\"" + index + "\">" + cells.entrySet().stream().map(e -> { return "<c r=\"" + getColName(e.getKey()) + index + "\" t=\"inlineStr\"><is><t>" + e.getValue() + "</t></is></c>"; }).collect(Collectors.joining("")) + "</row>").getBytes(StandardCharsets.UTF_8); index++; return row; }
private String getColName(Integer index) { if (index <= 26) { return String.valueOf((char) ('A' + index - 1)); } else { return String.valueOf((char) ('A' + index / 26 - 1)) + String.valueOf((char) ('A' + index % 26 - 1)); } }
public void finish() throws IOException { zos.write(finishStr.getBytes(StandardCharsets.UTF_8)); zos.flush(); zos.closeEntry(); zos.finish(); }
|
- 前端处理
前端可以通过restful接口获取到excel内容,然后通过blob对象生成excel文件,最后通过a标签的download属性下载excel文件。
https://medium.com/@Nopziiemoo/create-excel-files-using-javascript-without-all-the-fuss-2c4aa5377813