领导最近交给一个任务,要求定时任务,发送邮件。
具体要求为,要求指定数据库库中查出指定数据,并且放到Excel表格中,最后发送到指定邮箱,但是要求文件不落地。
java中能把数据从流转为excel文件的由两种包,一种是apache开发的poi另一种是net.sourceforge.jexcelapi,听说是一个韩国人开发的包。
我尽量两种包都写到。在公司要求中我使用的是poi。我先用poi实现了这个转换。其中没有设置表格的样式内容。
poi设置Excel单元格样式:https://www.cnblogs.com/linkstar/p/5910916.html
具体代码借鉴了这个老哥的思路:https://blog.csdn.net/yixin605691235/article/details/82429156
具体实现如下(poi实现):
数据转Excel工具类:
package com.www.kx;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
public class Data2Excel {
/**
* 对象转Excel文件
*
* @param list
* 传入的数据
* 对应的
* @param sheetSize
* 工作表的大小
* @param request
* 网页传来的请求
* @param response
* 网页传来的相应
* @param fileName
* 文件名
* @param sheetName
* 工作表名字
* @param title
* 表头
* key-为英文表头 表要javabean对应所以要遵循如下规则
* 根据带路径或不带路径的属性名获取属性值,即接受简单属性名,如userName等,又接受带路径的属性名,如student.department.name等
* value-对应中文在excel中产生的表头名
*/
public static void list2ExcelFile(List<T> list, int sheetSize, HttpServletRequest request,
HttpServletResponse response, String fileName, String sheetName, LinkedHashMap<String, String> title) {
OutputStream outputStream = null;
// 首先判断list里面是不是有值
if (list == null || list.size() == 0) {
// 如果空或者大小为0就直接return 或者抛出异常
return;
// throw new Exception("传入的数据为空或者大小为零");
}
try {
// 前台页面显示下载
setResp(request, response, fileName);
outputStream = response.getOutputStream();
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet(sheetName);
fillVeticalSheet(sheet, list, title);
wb.write(outputStream);
if (wb != null) {
wb.close();
}
} catch (Exception e) {
// TODO: handle exception
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 对象转流
*
* @param list
* 传入的数据
* @param sheetSize
* 工作表的大小
* @param request
* 网页传来的请求
* @param response
* 网页传来的相应
* @param fileName
* 文件名
* @param sheetName
* 工作表名字
* @param title
* 表头
* key-为英文表头 表要javabean对应所以要遵循如下规则
* 根据带路径或不带路径的属性名获取属性值,即接受简单属性名,如userName等,又接受带路径的属性名,如student.department.name等
* value-对应中文在excel中产生的表头名
* @return ByteArrayInputStream 字节流对象
* @throws Exception
* 传入数据为空
*/
public static ByteArrayInputStream list2EcelStream(List<T> list, int sheetSize,
/* HttpServletRequest request,HttpServletResponse response, */String fileName, String sheetName,
LinkedHashMap<String, String> title) throws Exception {
byte[] data = null;
ByteArrayOutputStream out = null;
ByteArrayInputStream in = null;
// 首先判断list里面是不是有值
if (list == null || list.size() == 0) {
// 如果空或者大小为0就直接return 或者抛出异常
// return;
throw new Exception("传入的数据为空或者大小为零");
}
try {
// 前台页面显示下载
/*
* setResp(request,response,fileName); outputStream =
* response.getOutputStream();
*/
out = new ByteArrayOutputStream();
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet(sheetName);
fillVeticalSheet(sheet, list, title);
wb.write(out);
data = out.toByteArray();
in = new ByteArrayInputStream(data);
} catch (Exception e) {
// TODO: handle exception
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return in;
}
/**
* 缓存数据转Excel文件
*
* @param list
* 传入的数据 对应的是map类型 key-value 这里的key 要与表头中的key规则一致
* @param sheetSize
* 工作表的大小
* @param request
* 网页传来的请求
* @param response
* 网页传来的相应
* @param fileName
* 文件名
* @param sheetName
* 工作表名字
* @param title
* 表头
* key-为英文表头 表要javabean对应所以要遵循如下规则
* 根据带路径或不带路径的属性名获取属性值,即接受简单属性名,如userName等,又接受带路径的属性名,如student.department.name等
* value-对应中文在excel中产生的表头名
*/
public static void map2ExcelFile(List<Map<String, Object>> list, int sheetSize, HttpServletRequest request,
HttpServletResponse response, String fileName, String sheetName, LinkedHashMap<String, String> title) {
OutputStream outputStream = null;
// 首先判断list里面是不是有值
if (list == null || list.size() == 0) {
// 如果空或者大小为0就直接return 或者抛出异常
return;
// throw new Exception("传入的数据为空或者大小为零");
}
try {
// 前台页面显示下载
setResp(request, response, fileName);
outputStream = response.getOutputStream();
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet(sheetName);
fillVeticalSheet(sheet, list, title);
wb.write(outputStream);
if (wb != null) {
wb.close();
}
} catch (Exception e) {
// TODO: handle exception
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 缓存数据转Excel文件
*
* @param list
* 传入的数据 对应的是map类型 key-value 这里的key 要与表头key的规则一一致
* @param sheetSize
* 工作表的大小
* @param request
* 网页传来的请求
* @param response
* 网页传来的相应
* @param fileName
* 文件名
* @param sheetName
* 工作表名字
* @param title
* 表头
* key-为英文表头 表要javabean对应所以要遵循如下规则
* 根据带路径或不带路径的属性名获取属性值,即接受简单属性名,如userName等,又接受带路径的属性名,如student.department.name等
* value-对应中文在excel中产生的表头名
* @return ByteArrayInputStream
* 字节流对象
* @throws Exception
* 传入数据为空
*/
public static ByteArrayInputStream map2ExcelSteam(List<Map<String, Object>> list, int sheetSize,
/* HttpServletRequest request,HttpServletResponse response, */String fileName, String sheetName,
LinkedHashMap<String, String> title) throws Exception {
byte[] data = null;
ByteArrayOutputStream out = null;
ByteArrayInputStream in = null;
// 首先判断list里面是不是有值
if (list == null || list.size() == 0) {
// 如果空或者大小为0就直接return 或者抛出异常
// return;
throw new Exception("传入的数据为空或者大小为零");
}
try {
// 前台页面显示下载
/*
* setResp(request,response,fileName); outputStream =
* response.getOutputStream();
*/
out = new ByteArrayOutputStream();
Workbook wb = new HSSFWorkbook();
Sheet sheet = wb.createSheet(sheetName);
fillVeticalSheet(sheet, list, title);
wb.write(out);
data = out.toByteArray();
in = new ByteArrayInputStream(data);
} catch (Exception e) {
// TODO: handle exception
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return in;
}
/**
* 下载使用的设置编码格式
*
* @param request
* @param response
* @param fileName
* 文件名
*/
private static void setResp(HttpServletRequest request, HttpServletResponse response, String fileName) {
// 获取用户使用的浏览器,系统 内核版本等信息
final String userAgent = request.getHeader("USER-AGENT");
response.reset();
// response.setContentType("application/vnd.ms-excel");
response.setContentType("application/x-msdownload");
try {
String finalFileName = null;
if (StringUtils.contains(userAgent, "MSIE")) {// IE
finalFileName = URLEncoder.encode(fileName, "UTF8");
} else if (StringUtils.contains(userAgent, "Mozilla")) {// chrome firefox
finalFileName = new String(fileName.getBytes(), "ISO8859-1");
} else {
finalFileName = URLEncoder.encode(fileName, "UTF8");
}
String dataStr = new SimpleDateFormat("yyyMMddHHmmss").format(new Date()).toString();
// 设置浏览器下载提示,不是直接在浏览器中打开文件
response.setHeader("COntent-Disposition", "attachment; filename=" + finalFileName + "_" + dataStr + ".xls");
} catch (UnsupportedEncodingException e) {// 编码异常
e.printStackTrace();
}
}
/**
* 设置表头 数据
*
* @param sheet
* 工作表对象
* @param list
* 存放对象的list
* @param title
* 中英英文对应的表头数据
*/
private static void fillVeticalSheet(Sheet sheet, List<T> list, LinkedHashMap<String, String> title) {
if (null == list || null == title) {
return;
}
// 定义存放英文字段名和中文字段名称的数组
String[] enFields = new String[title.size()];
String[] cnFields = new String[title.size()];
// 填充数组
int count = 0;
for (Entry<String, String> entry : title.entrySet()) {
enFields[count] = entry.getKey();
cnFields[count] = entry.getValue();
count++;
}
// 填充表头
Row row = sheet.createRow(0);
for (int j = 0; j < cnFields.length; j++) {
Cell cell = row.createCell(j);
cell.setCellValue(cnFields[j]);
}
// 填充数据
for (int i = 0; i < list.size(); i++) {
T item = list.get(i);
row = sheet.createRow(i + 1);
for (int j = 0; j < enFields.length; j++) {
Object objValue = getFieldValueByNameSequence(enFields[j], item);
if (objValue instanceof String) {
row.createCell(j).setCellValue((String) objValue);
} else if (objValue instanceof Double) {
row.createCell(j).setCellValue((Double) objValue);
} else if (objValue instanceof RichTextString) {
row.createCell(j).setCellValue((RichTextString) objValue);
} else if (objValue instanceof Date) {
row.createCell(j).setCellValue((Date) objValue);
} else if (objValue instanceof Boolean) {
row.createCell(j).setCellValue((Boolean) objValue);
}
}
}
}
/**
* 设置表头 数据
*
* @param sheet
* 工作表对象
* @param list
* 防止list 中 map的结构为Key为alue的对应字段
* @param title
* 中英英文对应的表头数据
*/
private static void fillVeticalSheet(Sheet sheet, List<Map<String, Object>> list, Map<String, String> title) {
if (null == list) {
return;
}
// 解析list 将 标题和字段解析出来
String[] enFields = new String[title.size()];
String[] cnFields = new String[title.size()];
int count = 0;
for (Entry<String, String> entry : title.entrySet()) {
enFields[count] = entry.getKey();
cnFields[count] = entry.getValue();
count++;
}
// 填充表头
Row row = sheet.createRow(0);
for (int j = 0; j < cnFields.length; j++) {
Cell cell = row.createCell(j);
cell.setCellValue(cnFields[j]);
}
// 填充数据
int i = 1;
for (Map<String, Object> map : list) {
row = sheet.createRow(i + 1);
for (int j = 0; j < cnFields.length; j++) {
Object objValue = map.get(enFields[j]);
if (objValue instanceof String) {
row.createCell(j).setCellValue((String) objValue);
} else if (objValue instanceof Double) {
row.createCell(j).setCellValue((Double) objValue);
} else if (objValue instanceof RichTextString) {
row.createCell(j).setCellValue((RichTextString) objValue);
} else if (objValue instanceof Date) {
row.createCell(j).setCellValue((Date) objValue);
} else if (objValue instanceof Boolean) {
row.createCell(j).setCellValue((Boolean) objValue);
}
}
}
}
/**
* 根据带路径或不带路径的属性名获取属性值,即接受简单属性名,如userName等,又接受带路径的属性名,如student.department.
* name等
*
* @param fieldNameSequence
* 对象名
* @param o
* 对象
* @return 对象的值
*/
private static Object getFieldValueByNameSequence(String fieldNameSequence, Object o) {
Object value = null;
try {
// 将fieldNameSequence进行拆分
String[] attributes = fieldNameSequence.split("\\.");
if (attributes.length == 1) {
value = PropertyUtils.getProperty(o, fieldNameSequence);
} else {
// 根据属性名获取属性对象
Object fieldObj = PropertyUtils.getProperty(o, attributes[0]);
String subFieldNameSequence = fieldNameSequence.substring(fieldNameSequence.indexOf(".") + 1);
value = getFieldValueByNameSequence(subFieldNameSequence, fieldObj);
}
if (value instanceof Date) {
value = new SimpleDateFormat("yyyy-MM-dd").format(value).toString();
}
// if (value.toString().endsWith(".0000")) {
// String txt =value.toString();
// value = txt.substring(0, txt.lastIndexOf("."));
// }
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
}