寫在前面
此為總結(jié)篇礼预,很多都是參考網(wǎng)友總結(jié)的方法超营,然后在自己的項(xiàng)目中實(shí)踐鸳玩,感覺很實(shí)用,又不想存粹的拿來主義演闭,現(xiàn)將原文鏈接附上以作記錄和參考:
1不跟、http://blog.csdn.net/houxuehan/article/details/50960259
2、http://www.cnblogs.com/wqsbk/p/5417180.html
3米碰、http://www.anyrt.com/blog/list/poiexcel.html
4窝革、http://poi.apache.org/spreadsheet/quick-guide.html 「官方」
本文中我們將看到什么
- Maven配置
- 導(dǎo)出數(shù)據(jù)列表到Excel的公共方法
- 工具類調(diào)用接口
- Controller書寫樣例
- 生成帶有文檔內(nèi)部鏈接的Excel文檔
- 多任務(wù)通用模式下載任務(wù)解決方案,采用阻塞隊(duì)列和線程池
Maven
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.16</version>
</dependency>
導(dǎo)出數(shù)據(jù)列表到Excel的公共方法
/**
* 導(dǎo)出excel公共方法吕座,適合列表類文件導(dǎo)出虐译。
* 導(dǎo)出文件中的數(shù)值列支持選擇計算統(tǒng)計。
* 此方法為內(nèi)存型導(dǎo)出吴趴,請注意數(shù)據(jù)量過大的問題
* @param title
* @param headMap
* @param jsonArray
* @param datePattern
* @param colWidth
* @param out
*/
public static void exportExcelX(String title, Map<String, String> headMap, JSONArray jsonArray,
String datePattern, int colWidth, OutputStream out) {
if (datePattern == null) datePattern = DEFAULT_DATE_PATTERN;
// 聲明一個工作薄
SXSSFWorkbook workbook = new SXSSFWorkbook(1000);//緩存
workbook.setCompressTempFiles(true);
//表頭樣式
CellStyle titleStyle = workbook.createCellStyle();
titleStyle.setAlignment(HorizontalAlignment.CENTER);
Font titleFont = workbook.createFont();
titleFont.setFontHeightInPoints((short) 20);
titleFont.setBoldweight((short) 700);
titleStyle.setFont(titleFont);
// 列頭樣式
CellStyle headerStyle = workbook.createCellStyle();
//headerStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
headerStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
headerStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
headerStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
headerStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
headerStyle.setAlignment(HorizontalAlignment.CENTER);
Font headerFont = workbook.createFont();
headerFont.setFontHeightInPoints((short) 12);
headerFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
headerStyle.setFont(headerFont);
// 單元格樣式
CellStyle cellStyle = workbook.createCellStyle();
//cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
cellStyle.setAlignment(HorizontalAlignment.CENTER);
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
Font cellFont = workbook.createFont();
cellFont.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
cellStyle.setFont(cellFont);
// 生成一個(帶標(biāo)題)表格
SXSSFSheet sheet = workbook.createSheet();
//設(shè)置列寬
int minBytes = colWidth < DEFAULT_COLOUMN_WIDTH ? DEFAULT_COLOUMN_WIDTH : colWidth;//至少字節(jié)數(shù)
int[] arrColWidth = new int[headMap.size()];
// 產(chǎn)生表格標(biāo)題行,以及設(shè)置列寬
String[] properties = new String[headMap.size()];
String[] headers = new String[headMap.size()];
int ii = 0;
for (Iterator<String> iter = headMap.keySet().iterator(); iter.hasNext(); ) {
String fieldName = iter.next();
properties[ii] = fieldName;
headers[ii] = headMap.get(fieldName);
int bytes = fieldName.getBytes().length;
arrColWidth[ii] = bytes < minBytes ? minBytes : bytes;
sheet.setColumnWidth(ii, arrColWidth[ii] * 256);
ii++;
}
// 遍歷集合數(shù)據(jù)漆诽,產(chǎn)生數(shù)據(jù)行
int rowIndex = 0;
for (Object obj : jsonArray) {
if (rowIndex == 65535 || rowIndex == 0) {
if (rowIndex != 0) sheet = workbook.createSheet();//如果數(shù)據(jù)超過了,則在第二頁顯示
SXSSFRow titleRow = sheet.createRow(0);//表頭 rowIndex=0
titleRow.createCell(0).setCellValue(title);
titleRow.getCell(0).setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, headMap.size() - 1));
SXSSFRow headerRow = sheet.createRow(1); //列頭 rowIndex =1
for (int i = 0; i < headers.length; i++) {
headerRow.createCell(i).setCellValue(headers[i]);
headerRow.getCell(i).setCellStyle(headerStyle);
}
rowIndex = 2;//數(shù)據(jù)內(nèi)容從 rowIndex=2開始
}
JSONObject jo = (JSONObject) JSONObject.toJSON(obj);
SXSSFRow dataRow = sheet.createRow(rowIndex);
for (int i = 0; i < properties.length; i++) {
SXSSFCell newCell = dataRow.createCell(i);
Object o = jo.get(properties[i]);
String cellValue = "";
if (o == null) cellValue = "";
else if (o instanceof Date) cellValue = new SimpleDateFormat(datePattern).format(o);
else if (o instanceof Float || o instanceof Double) {
cellValue = new BigDecimal(o.toString()).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
}
else cellValue = o.toString();
if (o instanceof Float || o instanceof Double || o instanceof BigDecimal) {
newCell.setCellValue(Double.parseDouble(cellValue));
}else if(o instanceof Integer) {
newCell.setCellValue(Integer.parseInt(cellValue));
}else{
newCell.setCellValue(cellValue);
}
newCell.setCellStyle(cellStyle);
}
rowIndex++;
}
try {
workbook.write(out);
} catch (IOException e) {
log.error("導(dǎo)出Excel操作時組裝Excel文件出現(xiàn)異常", e);
} finally {
if(workbook != null) {
try {
workbook.close();
workbook.dispose();
} catch (IOException e) {
log.error("關(guān)閉流時出現(xiàn)異常", e);
}
}
}
}
以上方法適用于導(dǎo)出數(shù)據(jù)列表锣枝,提供文件大標(biāo)題厢拭、列標(biāo)題等,而且對數(shù)值列進(jìn)行了專門的格式化處理撇叁,以方便需要后期選中統(tǒng)計處理供鸠。這種通用方法是內(nèi)存型處理方式,需要注意查詢的列表內(nèi)容不能過大陨闹,可能上十萬還能hold住回季,主要還是看各位的機(jī)器性能和內(nèi)存容量家制,否則可能出現(xiàn)OOM,通常對于大文件的處理泡一,建議分批查詢再寫入到excel中。excel文檔可能存在sheet頁行數(shù)不能超過65535的問題觅廓,對于這樣的解決方案以上方法也已給出鼻忠,就是擴(kuò)展到另外一張sheet頁。
針對以上方法杈绸,我們給出一個使用樣例帖蔓,供大家參考使用。
工具類調(diào)用接口
/**
* 導(dǎo)出文件到本地指定目錄
* @param title
* @param fileName
* @param headMap
* @param ja
*/
public static void downloadExcelFile(String title, String fileName, Map<String, String> headMap, JSONArray ja) {
try {
FileOutputStream fileOut = new FileOutputStream(fileName);
ExcelUtil.exportExcelX(title, headMap, ja, null, 0, fileOut);
} catch (Exception e) {
log.error("處理導(dǎo)出文件時遇到異常", e);
}
}
/**
* 通過web調(diào)用方式直接導(dǎo)出Excel文件
* @param title
* @param headMap
* @param ja
* @param response
*/
public static void downloadExcelFile(String title, Map<String, String> headMap, JSONArray ja, HttpServletResponse response) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ExcelUtil.exportExcelX(title, headMap, ja, null, 0, os);
byte[] content = os.toByteArray();
InputStream is = new ByteArrayInputStream(content);
// 設(shè)置response參數(shù)瞳脓,可以打開下載頁面
response.reset();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + new String((title + ".xlsx").getBytes(), "iso-8859-1"));
response.setContentLength(content.length);
ServletOutputStream outputStream = response.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(is);
BufferedOutputStream bos = new BufferedOutputStream(outputStream);
byte[] buff = new byte[8192];
int bytesRead;
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
// 自行在finally中處理流的關(guān)閉
bis.close();
bos.close();
outputStream.flush();
outputStream.close();
} catch (Exception e) {
log.error("導(dǎo)出Excel操作時操作下載請求出現(xiàn)異常", e);
}
}
基于SpringBoot的Controller
@RequestMapping("export")
@ResponseBody
public void exportOrderListData(HttpServletRequest request, HttpServletResponse response) {
// 查詢條件 【略】
PageQuery pageQuery = new PageQuery();
// 查詢數(shù)據(jù)
pageQuery.setPageSize(Integer.MAX_VALUE);
orderInfoService.queryOrderLogInfoList(pageQuery);
// 數(shù)據(jù)集合塑娇,將其轉(zhuǎn)換成json串
String jsonString = JSON.toJSONString(pageQuery.getList());
JSONArray jsonArray = JSON.parseArray(jsonString);
// 表頭,key是獲取數(shù)據(jù)時將要調(diào)用的get方法劫侧,value是顯示的列標(biāo)題
Map<String, String> headMap = new LinkedHashMap<>();
headMap.put("operationTime", "日期");
headMap.put("buyerPhoneNum", "手機(jī)號");
headMap.put("tradingNum", "交易ID");
headMap.put("areaInfo", "區(qū)域信息");
headMap.put("deviceNo", "設(shè)備編號");
headMap.put("deviceAddr", "設(shè)備地址");
headMap.put("cardSlotName", "貨道名稱");
headMap.put("commId", "商品編號");
headMap.put("commodityName", "商品名稱");
headMap.put("tradingPrice", "成本價");
headMap.put("tradingPrice", "零售價");
headMap.put("buyerPayPrice", "實(shí)際售價");
headMap.put("empty", "代金券");
headMap.put("empty", "大轉(zhuǎn)盤獎勵商品");
headMap.put("empty", "隨機(jī)立減金額");
// 導(dǎo)出表格名稱
String title = "訂單列表";
// 執(zhí)行導(dǎo)出
ExcelUtil.downloadExcelFile(title, headMap, jsonArray, response);
}
在controller中直接調(diào)用導(dǎo)出文件到瀏覽器的接口埋酬,用戶的瀏覽器將自動阻塞并等待文件下載,如果調(diào)用生成文件到本地的方法烧栋,則沒有返回写妥,直接在本地磁盤生成文件。需要注意的是审姓,如果待導(dǎo)出文件很大珍特,將會花費(fèi)很長的計算和生成excel文檔的時間,這可能會導(dǎo)致用戶瀏覽器請求出現(xiàn)超時魔吐,影響用戶體驗(yàn)扎筒,到時具體問題具體處理,你可以自己想辦法規(guī)避這種情況酬姆,比如采用異步執(zhí)行任務(wù)的方式嗜桌,用戶點(diǎn)擊導(dǎo)出之后開啟異步線程進(jìn)行處理,然后直接返回提示信息轴踱,稍后將下載文件的鏈接給到用戶即可症脂。(下面有涉及到)
Tips:如果想通過web調(diào)用直接導(dǎo)出Excel文件,那么在前端頁面需要使用提交表單跳轉(zhuǎn)的方式來請求下載淫僻,不能使用Ajax調(diào)用的方式诱篷,這點(diǎn)相信大家應(yīng)該明了。
比如雳灵,你可以寫出這種通用的調(diào)用方式:
function export(action){
var form = $('#form');
form.attr('action', action);
form.attr('method', 'post');
form.attr('target', '_self');
form.submit();
}
到這里棕所,相信你已經(jīng)具備初步的導(dǎo)出Excel功能了,但是很多地方還需要根據(jù)自身情況進(jìn)行優(yōu)化悯辙,再有琳省,以上編碼難免有錯誤迎吵,請自行更正,相信都比較簡單针贬,而且估計導(dǎo)出Excel方法中很多方法都已過時击费,可以通過查看源碼的方式,找到更新更優(yōu)的方法桦他。
下面蔫巩,我們給出一個針對復(fù)雜一點(diǎn)兒的業(yè)務(wù)(或者說場景)給出的一種文檔方式。
帶有內(nèi)部鏈接的Excel
估計你也有遇到這種情況快压,就是導(dǎo)出的列表數(shù)據(jù)中圆仔,可能需要點(diǎn)擊某個值,再鏈接到它關(guān)聯(lián)的詳情信息頁面蔫劣,這在excel中也是可以實(shí)現(xiàn)的坪郭,具體就是,通過點(diǎn)擊某個單元格脉幢,然后鏈接到某個sheet頁以顯示更加詳細(xì)的內(nèi)容歪沃,同時,在詳情頁面上鸵隧,額外增加一個「回到主頁」的超鏈绸罗,這樣整個文檔就和在頁面中點(diǎn)擊差不多效果了。
比如下面這種效果:
基于SpringBoot的Controller中的實(shí)現(xiàn)方法
@RequestMapping("exportFile")
@ResponseBody
public void exportExcelWithDetail(HttpServletRequest request, HttpServletResponse response) {
PageQuery pageQuery = new PageQuery<>();
// 查詢條件 【略】
log.info("開始執(zhí)行導(dǎo)出商品銷售數(shù)據(jù)信息Excel文件【START】");
FileOutputStream fileOut = null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
// 流式下載
ServletOutputStream outputStream = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try{
XSSFWorkbook wb = new XSSFWorkbook();
CreationHelper createHelper = wb.getCreationHelper();
// 創(chuàng)建主頁
XSSFSheet sheet = wb.createSheet("Home");
// 創(chuàng)建頁頭
XSSFRow row = sheet.createRow(0);
XSSFCell cell = row.createCell((short) 0);
cell.setCellValue("商品銷售數(shù)據(jù)信息");
sheet.addMergedRegion(new CellRangeAddress(
0, //first row (0-based)
0, //last row (0-based)
0, //first column (0-based)
4 //last column (0-based)
));
// 創(chuàng)建頁頭樣式
// -- 字體
XSSFFont font = wb.createFont();
font.setFontHeightInPoints((short)24);
font.setFontName("Microsoft YaHei UI");
font.setBold(true);
// -- 頁頭樣式
XSSFCellStyle style = wb.createCellStyle();
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setBorderTop(BorderStyle.THIN);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
// -- 賦予頁頭樣式
cell.setCellStyle(style);
// 創(chuàng)建標(biāo)題欄
XSSFRow titleRow = sheet.createRow(1);
Cell title_cell_0 = titleRow.createCell((short) 0);
title_cell_0.setCellValue("日期");
Cell title_cell_1 = titleRow.createCell((short) 1);
title_cell_1.setCellValue("區(qū)域信息");
Cell title_cell_2 = titleRow.createCell((short) 2);
title_cell_2.setCellValue("設(shè)備編號");
Cell title_cell_3 = titleRow.createCell((short) 3);
title_cell_3.setCellValue("商品銷售總量");
Cell title_cell_4 = titleRow.createCell((short) 4);
title_cell_4.setCellValue("商品銷售詳情");
// 設(shè)置單元格寬度
int width = "商品銷售詳情".getBytes().length * 256;
sheet.setColumnWidth(0, width);
sheet.setColumnWidth(1, width);
sheet.setColumnWidth(2, width);
sheet.setColumnWidth(3, width);
sheet.setColumnWidth(4, width);
// -- 字體
XSSFFont titleFont = wb.createFont();
titleFont.setFontHeightInPoints((short)12);
titleFont.setFontName("Microsoft YaHei UI");
titleFont.setColor(new XSSFColor(new java.awt.Color(255, 255, 255)));
// -- 標(biāo)題樣式
XSSFCellStyle titleStyle = wb.createCellStyle();
titleStyle.setFont(titleFont);
titleStyle.setAlignment(HorizontalAlignment.LEFT);
titleStyle.setFillBackgroundColor(new XSSFColor(new java.awt.Color(75, 102, 128)));
titleStyle.setFillPattern(FillPatternType.FINE_DOTS);
titleStyle.setBorderTop(BorderStyle.THIN);
titleStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
titleStyle.setBorderRight(BorderStyle.THIN);
titleStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
titleStyle.setBorderBottom(BorderStyle.THIN);
titleStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
titleStyle.setBorderLeft(BorderStyle.THIN);
titleStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
// 賦予標(biāo)題欄樣式
title_cell_0.setCellStyle(titleStyle);
title_cell_1.setCellStyle(titleStyle);
title_cell_2.setCellStyle(titleStyle);
title_cell_3.setCellStyle(titleStyle);
title_cell_4.setCellStyle(titleStyle);
// 超鏈接樣式
XSSFCellStyle hlink_style = wb.createCellStyle();
Font hlink_font = wb.createFont();
hlink_font.setUnderline(Font.U_SINGLE);
hlink_font.setColor(IndexedColors.BLUE.getIndex());
hlink_style.setFont(hlink_font);
hlink_style.setAlignment(HorizontalAlignment.CENTER);
hlink_style.setBorderTop(BorderStyle.THIN);
hlink_style.setTopBorderColor(IndexedColors.BLACK.getIndex());
hlink_style.setBorderRight(BorderStyle.THIN);
hlink_style.setRightBorderColor(IndexedColors.BLACK.getIndex());
hlink_style.setBorderBottom(BorderStyle.THIN);
hlink_style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
hlink_style.setBorderLeft(BorderStyle.THIN);
hlink_style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
// 內(nèi)容樣式-居左
XSSFCellStyle contentStyle_left = wb.createCellStyle();
contentStyle_left.setBorderTop(BorderStyle.THIN);
contentStyle_left.setTopBorderColor(IndexedColors.BLACK.getIndex());
contentStyle_left.setBorderRight(BorderStyle.THIN);
contentStyle_left.setRightBorderColor(IndexedColors.BLACK.getIndex());
contentStyle_left.setBorderBottom(BorderStyle.THIN);
contentStyle_left.setBottomBorderColor(IndexedColors.BLACK.getIndex());
contentStyle_left.setBorderLeft(BorderStyle.THIN);
contentStyle_left.setLeftBorderColor(IndexedColors.BLACK.getIndex());
// 內(nèi)容樣式-居中
XSSFCellStyle contentStyle_center = wb.createCellStyle();
contentStyle_center.setAlignment(HorizontalAlignment.CENTER);
contentStyle_center.setBorderTop(BorderStyle.THIN);
contentStyle_center.setTopBorderColor(IndexedColors.BLACK.getIndex());
contentStyle_center.setBorderRight(BorderStyle.THIN);
contentStyle_center.setRightBorderColor(IndexedColors.BLACK.getIndex());
contentStyle_center.setBorderBottom(BorderStyle.THIN);
contentStyle_center.setBottomBorderColor(IndexedColors.BLACK.getIndex());
contentStyle_center.setBorderLeft(BorderStyle.THIN);
contentStyle_center.setLeftBorderColor(IndexedColors.BLACK.getIndex());
// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// 計數(shù)器
long totalPage = 0l;
long pageNum = 1;
long count = Integer.MAX_VALUE;
// 處理循環(huán)
log.info("開始循環(huán)插入列表基本數(shù)據(jù)豆瘫,并且開始查詢銷售數(shù)據(jù)詳情信息珊蟀。");
do{
// 分頁查詢數(shù)據(jù)
PageQuery<TbDeviceInfo> pageQuery = new PageQuery();
pageQuery.setPageNumber(pageNum++);
pageQuery.setPageSize(10);
exportQuery(request, pageQuery);
totalPage = pageQuery.getTotalPage();
// 保證某些功能只執(zhí)行一次
if(count > totalPage) {
count = totalPage;
}
// 搭建數(shù)據(jù)
List<TbDeviceInfo> tbDeviceInfoList = pageQuery.getList();
if(pageQuery.getTotalRow() > 0 && null != tbDeviceInfoList && !tbDeviceInfoList.isEmpty()) {
log.info("本次需要導(dǎo)出的記錄數(shù)[同時需要執(zhí)行該次數(shù)DB查詢]:{}", pageQuery.getTotalRow());
for(int i=0, len=tbDeviceInfoList.size(); i < len; i++) {
TbDeviceInfo tbDeviceInfo = tbDeviceInfoList.get(i);
// 創(chuàng)建詳情sheet頁
String prefix = String.valueOf((totalPage - count) * pageQuery.getPageSize() + (i + 1)).concat("_");
XSSFSheet sheetDetail = wb.createSheet(prefix + tbDeviceInfo.getDeviceId()); // sheet頁名稱為設(shè)備ID
XSSFRow detailHeadRow = sheetDetail.createRow(0);
XSSFCell detailHeadCell = detailHeadRow.createCell((short)0);
detailHeadCell.setCellValue("商品銷售數(shù)據(jù)詳情");
detailHeadCell.setCellStyle(style);
sheetDetail.addMergedRegion(new CellRangeAddress(
0, //first row (0-based)
0, //last row (0-based)
0, //first column (0-based)
4 //last column (0-based)
));
sheetDetail.setColumnWidth(0, width);
sheetDetail.setColumnWidth(1, width);
sheetDetail.setColumnWidth(2, width);
sheetDetail.setColumnWidth(3, width);
sheetDetail.setColumnWidth(4, width);
sheetDetail.setColumnWidth(5, width);
// 創(chuàng)建回到首頁鏈接
XSSFCell toHomeCell = detailHeadRow.createCell((short)5);
toHomeCell.setCellValue("點(diǎn)擊回到首頁");
Hyperlink link2 = createHelper.createHyperlink(HyperlinkType.DOCUMENT);
link2.setAddress("'Home'!A1");
toHomeCell.setHyperlink(link2);
toHomeCell.setCellStyle(hlink_style);
// 創(chuàng)建詳情公共信息
// 日期
XSSFRow dateRow = sheetDetail.createRow(1);
dateRow.createCell((short)0).setCellValue("日期");
dateRow.createCell((short)1).setCellValue(tbDeviceInfo.getOrderDate());
dateRow.getCell(0).setCellStyle(contentStyle_left);
dateRow.getCell(1).setCellStyle(contentStyle_left);
dateRow.createCell(2).setCellStyle(contentStyle_left);
dateRow.createCell(3).setCellStyle(contentStyle_left);
dateRow.createCell(4).setCellStyle(contentStyle_left);
sheetDetail.addMergedRegion(new CellRangeAddress(
1, //first row (0-based)
1, //last row (0-based)
1, //first column (0-based)
4 //last column (0-based)
));
// 區(qū)域信息
XSSFRow areaRow = sheetDetail.createRow(2);
areaRow.createCell((short)0).setCellValue("區(qū)域信息");
areaRow.createCell((short)1).setCellValue(tbDeviceInfo.getAreaInfo());
areaRow.getCell(0).setCellStyle(contentStyle_left);
areaRow.getCell(1).setCellStyle(contentStyle_left);
areaRow.createCell(2).setCellStyle(contentStyle_left);
areaRow.createCell(3).setCellStyle(contentStyle_left);
areaRow.createCell(4).setCellStyle(contentStyle_left);
sheetDetail.addMergedRegion(new CellRangeAddress(
2, //first row (0-based)
2, //last row (0-based)
1, //first column (0-based)
4 //last column (0-based)
));
// 設(shè)備編號
XSSFRow deviceNoRow = sheetDetail.createRow(3);
deviceNoRow.createCell((short)0).setCellValue("設(shè)備編號");
deviceNoRow.createCell((short)1).setCellValue(tbDeviceInfo.getDeviceId());
deviceNoRow.getCell(0).setCellStyle(contentStyle_left);
deviceNoRow.getCell(1).setCellStyle(contentStyle_left);
deviceNoRow.createCell(2).setCellStyle(contentStyle_left);
deviceNoRow.createCell(3).setCellStyle(contentStyle_left);
deviceNoRow.createCell(4).setCellStyle(contentStyle_left);
sheetDetail.addMergedRegion(new CellRangeAddress(
3, //first row (0-based)
3, //last row (0-based)
1, //first column (0-based)
4 //last column (0-based)
));
// 設(shè)備地址
XSSFRow deviceAddrRow = sheetDetail.createRow(4);
deviceAddrRow.createCell((short)0).setCellValue("設(shè)備地址");
deviceAddrRow.createCell((short)1).setCellValue(tbDeviceInfo.getDeviceAddr());
deviceAddrRow.getCell(0).setCellStyle(contentStyle_left);
deviceAddrRow.getCell(1).setCellStyle(contentStyle_left);
deviceAddrRow.createCell(2).setCellStyle(contentStyle_left);
deviceAddrRow.createCell(3).setCellStyle(contentStyle_left);
deviceAddrRow.createCell(4).setCellStyle(contentStyle_left);
sheetDetail.addMergedRegion(new CellRangeAddress(
4, //first row (0-based)
4, //last row (0-based)
1, //first column (0-based)
4 //last column (0-based)
));
// 銷售詳情列表標(biāo)題
XSSFRow detailTitleRow = sheetDetail.createRow(5);
XSSFCell detailTitle_cell_0 = detailTitleRow.createCell((short)0);
detailTitle_cell_0.setCellValue("序號");
XSSFCell detailTitle_cell_1 = detailTitleRow.createCell((short)1);
detailTitle_cell_1.setCellValue("商品名稱");
XSSFCell detailTitle_cell_2 = detailTitleRow.createCell((short)2);
detailTitle_cell_2.setCellValue("貨道總?cè)萘?);
XSSFCell detailTitle_cell_3 = detailTitleRow.createCell((short)3);
detailTitle_cell_3.setCellValue("單價");
XSSFCell detailTitle_cell_4 = detailTitleRow.createCell((short)4);
detailTitle_cell_4.setCellValue("銷售數(shù)量");
// 設(shè)備標(biāo)題樣式
detailTitle_cell_0.setCellStyle(titleStyle);
detailTitle_cell_1.setCellStyle(titleStyle);
detailTitle_cell_2.setCellStyle(titleStyle);
detailTitle_cell_3.setCellStyle(titleStyle);
detailTitle_cell_4.setCellStyle(titleStyle);
// 查詢數(shù)據(jù)庫,補(bǔ)充詳情數(shù)據(jù)
Integer deviceId = tbDeviceInfo.getId();
String orderDate = tbDeviceInfo.getOrderDate();
List<GoodsSaleDetailInfo> goodsSaleDetailInfoList = goodsSaleService.queryGoodsSaleDetails(deviceId, orderDate);
if(null != goodsSaleDetailInfoList && !goodsSaleDetailInfoList.isEmpty()) {
for(int j=0, len2=goodsSaleDetailInfoList.size(); j < len2; j++) {
GoodsSaleDetailInfo goodsSaleDetailInfo = goodsSaleDetailInfoList.get(j);
XSSFRow dataRow = sheetDetail.createRow(6 + j);
XSSFCell data_cell_0 = dataRow.createCell((short)0);
data_cell_0.setCellValue(j + 1);
XSSFCell data_cell_1 = dataRow.createCell((short)1);
data_cell_1.setCellValue(goodsSaleDetailInfo.getCommodityName());
XSSFCell data_cell_2 = dataRow.createCell((short)2);
data_cell_2.setCellValue(goodsSaleDetailInfo.getSlotCount());
XSSFCell data_cell_3 = dataRow.createCell((short)3);
data_cell_3.setCellValue(goodsSaleDetailInfo.getCommodityPrice() + "");
XSSFCell data_cell_4 = dataRow.createCell((short)4);
data_cell_4.setCellValue(goodsSaleDetailInfo.getSalesCount());
// 設(shè)置內(nèi)容單元格樣式
data_cell_0.setCellStyle(contentStyle_center);
data_cell_1.setCellStyle(contentStyle_left);
data_cell_2.setCellStyle(contentStyle_center);
data_cell_3.setCellStyle(contentStyle_center);
data_cell_4.setCellStyle(contentStyle_center);
}
}
// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// 創(chuàng)建主頁內(nèi)容欄
String index = String.valueOf(2 + (totalPage - count) * pageQuery.getPageSize() + i);
XSSFRow contentRow = sheet.createRow(Integer.valueOf(index));
XSSFCell content_cell_0 = contentRow.createCell((short) 0);
content_cell_0.setCellValue(tbDeviceInfo.getOrderDate()); // 日期
XSSFCell content_cell_1 = contentRow.createCell((short) 1);
content_cell_1.setCellValue(tbDeviceInfo.getAreaInfo()); // 區(qū)域信息
XSSFCell content_cell_2 = contentRow.createCell((short) 2);
content_cell_2.setCellValue(tbDeviceInfo.getDeviceId()); // 設(shè)備編號
XSSFCell content_cell_3 = contentRow.createCell((short) 3);
content_cell_3.setCellValue(tbDeviceInfo.getSalesCount()); // 銷售數(shù)量
// 文件內(nèi)鏈接內(nèi)容
XSSFCell content_cell_4 = contentRow.createCell((short) 4);
content_cell_4.setCellValue("點(diǎn)擊查看");
Hyperlink link2Detail = createHelper.createHyperlink(HyperlinkType.DOCUMENT);
link2Detail.setAddress("'" + prefix + tbDeviceInfo.getDeviceId() + "'!A1");
content_cell_4.setHyperlink(link2Detail);
content_cell_4.setCellStyle(hlink_style);
// 賦予內(nèi)容單元格樣式
content_cell_0.setCellStyle(contentStyle_center);
content_cell_1.setCellStyle(contentStyle_left);
content_cell_2.setCellStyle(contentStyle_left);
content_cell_3.setCellStyle(contentStyle_center);
}
}else {
log.info("本次查詢沒有匹配到符合條件的記錄外驱,無記錄導(dǎo)出育灸。");
// 創(chuàng)建無數(shù)據(jù)內(nèi)容的提示語
XSSFRow emptyRow = sheet.createRow(2);
XSSFCell emptyCell = emptyRow.createCell((short) 0);
emptyCell.setCellValue("沒有查詢到符合條件的記錄");
sheet.addMergedRegion(new CellRangeAddress(
2, //first row (0-based)
2, //last row (0-based)
0, //first column (0-based)
4 //last column (0-based)
));
XSSFCellStyle emptyStyle = wb.createCellStyle();
emptyStyle.setAlignment(HorizontalAlignment.CENTER);
emptyCell.setCellStyle(emptyStyle);
}
}while(--count > 0);
// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
// 輸出
*//*String filename = DateUtil.getFormatDate(new Date(), "yyyyMMddHHmmssSSS") + "-workbook.xlsx";
fileOut = new FileOutputStream(filename);
wb.write(fileOut);*//*
wb.write(os);
wb.close();
// 執(zhí)行流下載
byte[] content = os.toByteArray();
InputStream is = new ByteArrayInputStream(content);
// 設(shè)置response參數(shù),可以打開下載頁面
response.reset();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename="+ new String(("商品銷售數(shù)據(jù)信息列表.xlsx").getBytes(), "iso-8859-1"));
response.setContentLength(content.length);
outputStream = response.getOutputStream();
bis = new BufferedInputStream(is);
bos = new BufferedOutputStream(outputStream);
byte[] buff = new byte[8192];
int bytesRead;
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
outputStream.flush();
}catch (Exception e) {
log.error("export excel error", e);
}finally {
// 流的關(guān)閉 【略】
}
}
上面的代碼篇幅有點(diǎn)兒大昵宇,方法沒有經(jīng)過抽取和優(yōu)化磅崭,后續(xù)可以自行處理,詳細(xì)邏輯還是很清晰的瓦哎,加之以注釋說明砸喻,很容易看懂。
接下來蒋譬,我們將針對項(xiàng)目中存在多個相同方式的下載任務(wù)割岛,提供一種解決方案。
將導(dǎo)出任務(wù)添加到隊(duì)列中犯助,并定時用線程清理隊(duì)列中的任務(wù)癣漆,達(dá)到對服務(wù)器端減壓的效果;同時剂买,針對導(dǎo)出后的任務(wù)惠爽,以通知的形式告知用戶導(dǎo)出任務(wù)已結(jié)束癌蓖,可以到下載界面進(jìn)行下載。
這種解決方案的代碼實(shí)現(xiàn)有很多種婚肆,下面將使用阻塞隊(duì)列以及線程池的方式來處理租副,僅作參考。
以下是基于SpringBoot的實(shí)現(xiàn)方式旬痹,代碼中可能涉及到其他的技術(shù)標(biāo)簽附井,可忽略
目前只能給出核心代碼,實(shí)現(xiàn)思路已經(jīng)很明確两残,很多實(shí)現(xiàn)細(xì)節(jié)可自行處理,也還有很多可優(yōu)化之處
Bean配置
@Slf4j
@Configuration
public class ExportMethodConfiguration {
@Bean(initMethod = "execute", destroyMethod = "shutdown")
public ExportBlockingQueueService ExportBlockingQueueService(){
// 創(chuàng)建保存文件的路徑
String filePath = "." + File.separator + "excel";
File dir = new File(filePath);
if(!dir.exists()){
dir.mkdir();
log.info("保存Excel文件的目錄暫不存在把跨,現(xiàn)已創(chuàng)建成功人弓。{}", filePath);
}
return new ExportBlockingQueueServiceImpl();
}
}
阻塞隊(duì)列接口和實(shí)現(xiàn)
/** 接口 */
public interface ExportBlockingQueueService {
void addTask(ExportExcelWrapper exportExcelService);
void execute();
void shutdown();
}
/** 實(shí)現(xiàn) */
@Slf4j
public class ExportBlockingQueueServiceImpl implements ExportBlockingQueueService {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
Queue<ExportExcelWrapper> linkedBlockingQueue = new ConcurrentLinkedQueue<>();
@Override
public void addTask(ExportExcelWrapper exportExcelService) {
try {
log.info("導(dǎo)出Excel任務(wù)已加入到執(zhí)行隊(duì)列中");
linkedBlockingQueue.add(exportExcelService);
} catch (Exception e) {
log.error("將導(dǎo)出excel文件任務(wù)加入到隊(duì)列時出現(xiàn)異常。", e);
}
}
@Override
public void shutdown() {
scheduledExecutorService.shutdown();
}
public void execute() {
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
log.info("-------- loop");
ExportExcelWrapper exportExcelWrapper = linkedBlockingQueue.poll();
if (null != exportExcelWrapper) {
log.info("開始執(zhí)行下載任務(wù)");
exportExcelWrapper.getExportExcelService().executeQueryAndExport(exportExcelWrapper.getPageQuery(),
exportExcelWrapper.getOperateUser(), exportExcelWrapper.getDownloadFilePath());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, 3, 30, TimeUnit.SECONDS);
}
}
導(dǎo)出任務(wù)包裝類
@Data
public class ExportExcelWrapper {
// 導(dǎo)出文件存放的相對路徑
private String downloadFilePath = "." + File.separator + "excel";
// 登錄用戶
private LoginUser operateUser;
// 查詢條件集合
private PageQuery pageQuery;
// 執(zhí)行導(dǎo)出任務(wù)的實(shí)現(xiàn)方法
private ExportExcelService exportExcelService;
}
導(dǎo)出Excel任務(wù)的公共接口與實(shí)現(xiàn)舉例
/** 接口聲明 */
public interface ExportExcelService {
void executeQueryAndExport(PageQuery pageQuery, LoginUser operateUser, String downloadFilePath);
}
/** 實(shí)現(xiàn)舉例 */
@Service
@Slf4j
public class UserInfoExportExcelServiceImpl extends ExportExcelHelper implements ExportExcelService {
@Autowired
private OPUserService opUserService;
@Override
public void executeQueryAndExport(PageQuery pageQuery, LoginUser operateUser, String downloadFilePath) {
// 查詢數(shù)據(jù)
pageQuery.setPageSize(Integer.MAX_VALUE);
opUserService.queryUserPage(pageQuery);
// 數(shù)據(jù)集合
String jsonString = JSON.toJSONString(pageQuery.getList());
JSONArray jsonArray = JSON.parseArray(jsonString);
// 表頭
Map<String, String> headMap = new LinkedHashMap<>();
headMap.put("loginName", "登錄名稱");
headMap.put("userName", "用戶名稱");
headMap.put("roleName", "用戶角色");
headMap.put("email", "Email");
headMap.put("mobile", "聯(lián)系電話");
headMap.put("accessAuth", "授權(quán)準(zhǔn)入");
headMap.put("giveTime", "加入時間");
// 導(dǎo)出表格名稱
String title = "用戶信息列表";
String fileName = DateUtil.getFormatDate(new Date(), "yyyyMMddHHmmssSSS") + title + ".xlsx";
String filePath = downloadFilePath + File.separator + fileName;
// 執(zhí)行導(dǎo)出
ExcelUtil.downloadExcelFile(title, filePath, headMap, jsonArray);
log.info("導(dǎo)出Excel文件【{}】成功", fileName);
// 保存下載記錄
createExportFileRecod(fileName, filePath, operateUser.getUserId());
log.info("保存導(dǎo)出記錄成功着逐。");
// 執(zhí)行郵件發(fā)送通知
sendEmail(operateUser, fileName, filePath);
}
}
在Controller中調(diào)用
@RequestMapping("exportFile")
@ResponseBody
public void queryUserExportDataList(HttpServletRequest request, HttpServletResponse response) {
// 查詢條件 【略】
PageQuery pageQuery = new PageQuery<>();
LoginUser loginUser = (LoginUser) redisExService.getModel(CacheKey.USER_LOGIN.getKey().concat(request.getSession().getId()));
ExportExcelWrapper exportExcelWrapper =new ExportExcelWrapper();
exportExcelWrapper.setExportExcelService(exportExcelService);
exportExcelWrapper.setPageQuery(pageQuery);
exportExcelWrapper.setOperateUser(loginUser);
exportBlockingQueueService.addTask(exportExcelWrapper);
// 可以在這里給用戶回復(fù)提示語
responseMsg(response);
}
好了崔赌,到目前為止,你基本上已經(jīng)掌握了如何使用Java導(dǎo)出Excel文件了耸别,當(dāng)然健芭,這里只是導(dǎo)出文件的基本方式,可能實(shí)際當(dāng)中有各種不同各種奇怪的需求秀姐,這就需要自己根據(jù)實(shí)際場景去處理了慈迈。
希望上面的知識對你的項(xiàng)目有所幫助。