我们直接上代码:
首先是service层,首先定义一个方法,并在实现类中写文件导出的逻辑:
void export(ExportErrorPartDataReq exportReq, ServletOutputStream out);
serviceImpl:
@Override
public void export(ExportErrorPartDataReq exportReq, ServletOutputStream out) {
SptBrandTempExample example = new SptBrandTempExample();
SptBrandTempExample.Criteria criteria = example.createCriteria();
criteria.andUuidEqualTo(exportReq.getUuid());
criteria.andIsErrorEqualTo(0);
List<SptBrandTemp> sptBrandTempList = sptBrandTempMapper.selectByExample(example);
String [] title = new String[16];
title[0] = "aaa";
title[1] = "bbb";
title[2] = "ccc";
title[3] = "ddd";
title[4] = "eee";
title[5] = "fff";
title[6] = "ggg";
title[7] = "hhh";
title[8] = "iii";
title[9] = "jjj";
title[10] = "kkk";
title[11] = "lll";
title[12] = "mmm";
if(DefaultEnum.SPT_ENUM_BRNAD.getKey() == exportReq.getPartType()){
title[13] = "nnn";
title[14] = "ooo";
title[15] = "ppp";
}
List<List<String>> rows = null;
try {
rows = new LinkedList<>();
for (SptBrandTemp sptBrandTemps : sptBrandTempList) {
List<String> row = new LinkedList<>();
// 配件类型
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getCarBrandName()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getFactoryName()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getOeNum()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getOeNumFormat()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getOeNumExt()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getOeName()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getCatPartName()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getCatPartCode()));
row.add(DefaultEnum.getEnum(exportReq.getPartType()).getName());
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getPartRemark()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getNewOeNumber()));
row.add("adaptModelJY");
row.add("adaptModel");
if(DefaultEnum.SPT_ENUM_BRNAD.getKey() == exportReq.getPartType()){
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getPartFormat()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getPartBrandName()));
row.add(NotEmptyUtil.notEmptyString(sptBrandTemps.getFactoryCode()));
}
logger.info("sptBrandTemps---> {}" + sptBrandTemps.toString());
logger.info(row.size() + "个元素");
rows.add(row);
}
} catch (Exception e) {
e.printStackTrace();
}
try {
HSSFWorkbook wk = ExcelUtil.writeListToExcel("信息", title, rows);
wk.write(out);
// out流不是自己创建的,不用关闭
out.flush();
} catch (IOException e) {
logger.error("写excel出错 {}", e);
}
return;
}
为了预防空指针异常,我们自定义一个工具类,添加判空处理,这个大家可以根据自己的业务需要自己定义相应的工具类,目的呢,第一就是进行数据判空或校验或者其他处理,其次调用工具类可以使代码更加整洁美观,可读性方面也会好一些:
public class NotEmptyUtil {
public static String notEmptyString (String in){
if(StringUtil.isNotEmptyOrNull(in)){
return in;
}else{
return "-";
}
}
public static Integer notzeroInt(Integer in){
if(!in.equals(0)){
return in;
}else{
return 0;
}
}
然后是调用相应的excel工具类:
public class ExcelUtil {
/**
* 导出数据
*
* @param sheetName sheet
* @param heads 标题
* @param dataList 数据
* @return
*/
public static HSSFWorkbook writeListToExcel(String sheetName, String[] heads, List<List<String>> dataList) {
if (heads == null || dataList == null || dataList.isEmpty()) {
return null;
}
// 创建工作簿对象
HSSFWorkbook workbook = new HSSFWorkbook();
// 创建工作表
HSSFSheet sheet = workbook.createSheet(sheetName);
/** 写标题 (第一行) */
HSSFRow headRow = sheet.createRow(0);
for (int i = 0, len = heads.length; i < len; i++) {
// 创建单元格
HSSFCell cell = headRow.createCell(i);
// 设置单元格的值
cell.setCellValue(heads[i]);
}
/** 写数据 */
int count = 0;
for (List<String> list : dataList) {
// 行从第一行开始,第0行是标题
count++;
// 创建行
HSSFRow row = sheet.createRow(count);
int i = 0;
for (String str : list) {
// 创建单元格
HSSFCell cell = row.createCell(i);
// 设置单元格的值
cell.setCellValue(str);
i++;
}
}
return workbook;
}
public static List<String> writeExcel(List<List<String>> sheetData, String fileUrl, long fileLimit) {
//在内存中保持100条, 超过的数据放到硬盘中
SXSSFWorkbook wb = new SXSSFWorkbook(100);
wb.setCompressTempFiles(true); //将磁盘中的临时文件压缩优化,否则会很大
Sheet sh = wb.createSheet("Sheet1");
printRowValue(sheetData, sh, 0, sheetData.size());
List<String> res = null;
try {
res = writeFileOutputStream(sheetData, fileUrl, wb, fileLimit);
} catch (IOException e) {
e.printStackTrace();
}finally {
wb.dispose();//删除临时文件
}
return res;
}
private static List<String> writeFileOutputStream(List<List<String>> sheetData, String fileUrl, SXSSFWorkbook wb, long fileLimit) throws IOException {
File file=new File(fileUrl);
//如果父目录不存在,则创建
if (!file.getParentFile().exists()) {
if(!file.getParentFile().mkdirs()){
return null;
}
}
FileOutputStream out = new FileOutputStream(fileUrl);
wb.write(out);
out.close();
if(new File(fileUrl).length()>=20*1020*1024){
return null;
}
List<String> fileUrls = new ArrayList<>();
if(fileLimit != 0){//按照文件大小限制分片
int burstSize = new Long(new File(fileUrl).length()/fileLimit+1).intValue();
int increment = sheetData.size()/burstSize+1;
String preUrl = file.getParent()+File.separator+"数据", sufUrl = ".xlsx", resUrl = "";
for(int i=0; i<burstSize; i++){
resUrl = preUrl + (i+1) + sufUrl;
fileUrls.add(resUrl);
writeExcelSplit(sheetData, resUrl,i*increment, increment);
}
file.delete();
}else{
fileUrls.add(fileUrl);
}
return fileUrls;
}
/**
*
* @param sheetData 需要导出的数据
* @param fileUrl 导出的文件url
* @param offset 数据开始的偏移量
* @param limit 数据限制条数
*/
public static void writeExcelSplit(List<List<String>> sheetData, String fileUrl, int offset, int limit) {
//在内存中保持100条, 超过的数据放到硬盘中
SXSSFWorkbook wb = new SXSSFWorkbook(100);
wb.setCompressTempFiles(true); //将磁盘中的临时文件压缩优化,否则会很大
Sheet sh = wb.createSheet("Sheet1");
printRowValue(sheetData, sh, offset, limit);
FileOutputStream out = null;
try {
out = new FileOutputStream(fileUrl);
wb.write(out);
out.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
wb.dispose();//删除临时文件
}
}
private static void printRowValue(List<List<String>> sheetData, Sheet sh, int offset, int limit) {
int size = Math.min(offset+limit, sheetData.size());
for (int rownum = offset; rownum < size; rownum++) {
Row row = sh.createRow(rownum-offset);
List<String> rowValue = sheetData.get(rownum);
for (int cellnum = 0; cellnum < rowValue.size(); cellnum++) {
Cell cell = row.createCell(cellnum);
cell.setCellValue(rowValue.get(cellnum));
}
}
}
}
最后面试controller层,调用service方法,一般使用GET方式请求接口:
/**
* 导出数据
*/
@RequestMapping(value = "import/errorlist", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public DataResult export(HttpServletRequest request, @Valid ExportErrorPartDataReq exportReq, BindingResult bindingResult, HttpServletResponse response) {
DataResult res = new DataResult();
logger.info("request params : {}" , JSONObject.toJSON(exportReq));
try {
String fileName = "导出-" + DateUtil.getCurrentDateString();
response.reset();
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xls");
response.setContentType("application/vnd.ms-excel; charset=utf-8");
ServletOutputStream out = response.getOutputStream();
carAllPartStatInfoService.export(exportReq,out);
} catch (IOException e) {
e.printStackTrace();
}
logger.info("result params : {}" , JSONObject.toJSON(res));
return res;
}
到这里一个数据导出到excel文件,并下载excel文件接口就这样完成了,很简单,大家拿来直接按照自己的数据导出模板套着用就可以了,这里源码就不提供了,因为设计到公司的一些数据,相信大家应该看的懂把。这种方式导出的excel文件,是不会再服务端生成一个excel文件,在通过输出流下载文件的。在日常的生产环境中,也是不允许在服务端生成文件的,这一点大家要注意一下。