JAVA匯出資料到excel中大資料量的解決方法——續
阿新 • • 發佈:2019-02-06
之前寫了個大資料匯入excel的方法,將大資料拆分成多個excel檔案,再打包。有人提出能不能放在一個excel檔案分成多個sheet。後來也寫了實現,一直沒貼出來。
首先介面還是那個介面
import java.io.OutputStream; import java.util.Collection; public interface ExportData { public void export(final Collection<String> titles, final OutputStream os, final String sql, final Object... sqlParams); }
只有具體的實現不同
原理:import java.io.IOException; import java.io.OutputStream; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.io.IOUtils; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.core.PreparedStatementCreatorFactory; import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.SqlParameterValue; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.web.util.HtmlUtils; import com.avicinfo.common.base.util.StaticMethod; import com.avicinfo.v2.core.export.ExportData; import com.cnaec.common.Constant; /** * 將資料匯出到excel,多於指定行數後 放在下一個sheet * * @author lisen * */ public class ExportData2ExcelSheetImpl implements ExportData { /** * 每個檔案的最大行數 超過請求按預設算 */ public static final int MAXROWS = 50000; private int maxRow = MAXROWS; /** * 用於資料查詢 */ private JdbcTemplate jdbcTemplate; StringBuffer head = new StringBuffer("<?xml version=\"1.0\"?>").append( "<?mso-application progid=\"Excel.Sheet\"?> ").append( "<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\" ").append( " xmlns:o=\"urn:schemas-microsoft-com:office:office\" ").append( " xmlns:x=\"urn:schemas-microsoft-com:office:excel\" ").append( " xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\" ").append( " xmlns:html=\"http://www.w3.org/TR/REC-html40\">"); StringBuffer foot = new StringBuffer("</Workbook>"); StringBuffer sheetHead = new StringBuffer("<Worksheet ss:Name=\"sheet{0}\">").append("<Table>"); StringBuffer sheetFoot = new StringBuffer("</Table>").append( "<WorksheetOptions xmlns=\"urn:schemas-microsoft-com:office:excel\">").append( "<ProtectObjects>False</ProtectObjects>").append("<ProtectScenarios>False</ProtectScenarios>").append( "</WorksheetOptions>").append("</Worksheet>"); public ExportData2ExcelSheetImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } /** * 獲取單個檔案最大行數 * * @param maxRow * @return */ protected int getMaxRow() { return maxRow < MAXROWS ? maxRow : MAXROWS; } /** * 資料輸出 * * @param data * @param fos * @throws IOException */ protected void writeToOutputStream(String data, OutputStream os) throws IOException { IOUtils.write(data, os, Constant.ENCODING); } /** * 檔案頭的寫入 * * @param fos */ protected void writeHeaderToOutputStream(OutputStream os) throws IOException { writeToOutputStream(head.toString(), os); } /** * 檔案結尾的寫入 * * @param fos */ protected void writeFooterToOutputStream(OutputStream os) throws IOException { writeToOutputStream(foot.toString(), os); } /** * 檔案頭的寫入 * * @param fos */ protected void writeSheetHeaderToOutputStream(OutputStream os, int count) throws IOException { String sh = sheetHead.toString(); String head = java.text.MessageFormat.format(sh, count); writeToOutputStream(head, os); } /** * 檔案結尾的寫入 * * @param fos */ protected void writeSheetFooterToOutputStream(OutputStream os) throws IOException { writeToOutputStream(sheetFoot.toString(), os); } protected void writeTitleToOutputStream(Collection<String> titles, OutputStream os) throws IOException { if (titles != null && titles.size() > 0) { writeToOutputStream("<Row>", os); for (String title : titles) { writeToOutputStream("<Cell><Data ss:Type=\"String\">" + (title == null ? "" : HtmlUtils.htmlEscape(title)) + "</Data></Cell>", os); } writeToOutputStream("</Row>", os); } } protected Object getColumnValue(ResultSet rs, int index) throws SQLException { return JdbcUtils.getResultSetValue(rs, index); } protected void writeOneRowToOutputStream(ResultSet rs, OutputStream os) throws SQLException, IOException { // 獲取metaData; ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); writeToOutputStream("<Row>", os); for (int i = 1; i <= columnCount; i++) { Object obj = getColumnValue(rs, i); writeToOutputStream("<Cell><Data ss:Type=\"String\">" + (obj == null ? "" : HtmlUtils.htmlEscape(obj.toString())) + "</Data></Cell>", os); } writeToOutputStream("</Row>", os); } public void export(final Collection<String> titles, final Collection<String> firstRow, final Collection<String> lastRow, final OutputStream os, String sql, Object... sqlParams) { // 每個檔案最大行數 final int max = getMaxRow(); List<SqlParameterValue> spvList = new ArrayList<SqlParameterValue>(sqlParams.length); for (Object param : sqlParams) { SqlParameterValue spv = new SqlParameterValue(JdbcUtils.TYPE_UNKNOWN, param); spvList.add(spv); } PreparedStatementCreatorFactory factory = new PreparedStatementCreatorFactory(sql, spvList); factory.setResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE); PreparedStatementCreator psc = factory.newPreparedStatementCreator(sqlParams); PreparedStatementSetter pss = factory.newPreparedStatementSetter(sqlParams); jdbcTemplate.query(psc, pss, new ResultSetExtractor() { @Override public Object extractData(ResultSet rs) throws SQLException, DataAccessException { try { writeHeaderToOutputStream(os); // 行數記錄器 int i = 0, j = 0; while (rs.next()) { if (i == 0) { // 寫每個sheet頭 writeSheetHeaderToOutputStream(os, j); if (rs.isFirst() && !StaticMethod.isEmpty(firstRow)) { writeTitleToOutputStream(firstRow, os); } // 資料區標題欄 writeTitleToOutputStream(titles, os); } // 寫一行 i++; writeOneRowToOutputStream(rs, os); if (rs.isLast() && !StaticMethod.isEmpty(lastRow)) { writeTitleToOutputStream(lastRow, os); } if (i == max) { i = 0; j++; // 寫每個sheet尾 writeSheetFooterToOutputStream(os); } else if (rs.isLast()) { writeSheetFooterToOutputStream(os); } } writeFooterToOutputStream(os); } catch (IOException e) { e.printStackTrace(); } return null; } }); } @Override public void export(final Collection<String> titles, final OutputStream os, String sql, Object... sqlParams) { // 每個檔案最大行數 export(titles, null, null, os, sql, sqlParams); } public void setMaxRow(int maxRow) { this.maxRow = maxRow; } }
excel可以識別出xml格式的檔案,之前是拼成html中的table,這次是按照excel的標準格式拼成xml,這個xml格式由excel另存為後可以獲得。
excel的xml格式中,將不同的sheet以“<Worksheet ss:Name='sheet0'></Worksheet>”標識,sheet中的表為table,行為row,列為cell,單元格中的資料為Data,以此拼出xml後由excel開啟。
我在本機測試匯出123456行資料 用時不到7秒,感覺速度還行