excel导出工具包

excel导出工具类

package com.ljyq.central.common.util;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;


public final class Export {

public static final boolean excel07 = true;
public static final boolean excel03 = ! excel07;
private static final String DEFAULT_SHEET_NAME = "工作表1";

/**
* 导出文件! 在 Controller 中调用!
*
* @param type 类型: csv 或 xls xlsx 三种, 传 null 则默认是 csv
* @param name 导出的文件名, 若导出成 excel, sheet 名也是这个
* @param titleMap 标题(key 为英文, value 为标题内容)
* @param dataList 导出的数据(数组中的每个 object 都是一行, object 中的属性名与标题中的 key 相对)
*/
public static void export ( Type type , String name , LinkedHashMap< String, String > titleMap ,
List< ? > dataList , HttpServletResponse response ) throws IOException {
if ( type == null ) type = Type.CSV;

// 导出文件加上当前时间
String fileName = encodeName( name ) + "." + type.getValue().toLowerCase();
if ( type.isExcel() ) {
LinkedHashMap linkedHashMap = new LinkedHashMap( Collections.singletonMap( name , dataList ) );
exportXls( response , fileName , titleMap , linkedHashMap );
} /*else if ("pdf".equalsIgnoreCase(type)) {
// ...
}*/ else {
exportCsv( response , fileName , titleMap , dataList );
}
}

public static String encodeName ( String name ) {
String fileName = name + "-" + ( DateUtils.currentTimeString() );
String userAgent = RequestUtils.getUserAgentHeader();
if ( StringUtils.isNotBlank( userAgent ) && userAgent.contains( "Mozilla" ) ) {
// Chrome, Firefox, Safari etc...
fileName = new String( fileName.getBytes() , StandardCharsets.ISO_8859_1 );
} else {
try {
fileName = URLEncoder.encode( fileName , StandardCharsets.UTF_8.name() );
} catch ( UnsupportedEncodingException e ) {
// ignore
}
}
return fileName;
}

private static void setResponse ( HttpServletResponse response , String type , String fileName ) {
response.setContentType( "text/" + type );
response.setContentType( "application/octet-stream; charset=utf-8" );
response.setHeader( "Content-Disposition" , "attachment;filename=" + fileName );
}

public static void exportDoc ( HttpServletResponse response , String fileName , String html ) throws IOException {
// 将 html 导出成 doc 文档
setResponse( response , "doc" , fileName );

POIFSFileSystem fs = new POIFSFileSystem();
fs.createDocument( new ByteArrayInputStream( html.getBytes( StandardCharsets.UTF_8 ) ) , "WordDocument" );
fs.writeFilesystem( response.getOutputStream() );
}


public static void exportXls ( HttpServletResponse response , String fileName ,
LinkedHashMap< String, String > titleMap ,
LinkedHashMap< String, List< ? > > dataMap ) throws IOException {
// 导出 excel
setResponse( response , "xls" , fileName );
exportXls( response.getOutputStream() , Type.is07Suffix( fileName ) , titleMap , dataMap );
}

public static void exportXls ( OutputStream outputStream ,
boolean isExcel07 ,
String sheetName ,
LinkedHashMap< String, String > titleMap ,
List< ? > dataList ) throws IOException {
exportXls( outputStream ,
isExcel07 ,
titleMap ,
new LinkedHashMap<>( Collections.singletonMap( sheetName , dataList ) ) );
}

public static void exportXls ( OutputStream outputStream ,
LinkedHashMap< String, String > titleMap ,
List< ? > dataList ) throws IOException {
exportXls( outputStream ,
excel03 ,
titleMap ,
new LinkedHashMap<>( Collections.singletonMap( DEFAULT_SHEET_NAME , dataList ) )
);
}


public static void exportXls ( OutputStream outputStream ,
boolean isExcel07 ,
LinkedHashMap< String, String > titleMap ,
List< ? > dataList ) throws IOException {
exportXls( outputStream ,
isExcel07 ,
titleMap ,
new LinkedHashMap<>( Collections.singletonMap( DEFAULT_SHEET_NAME , dataList ) ) );
}

/**
* 导出Excel
*
* @param outputStream : 输出流
* @param isExcel07 : 是否microsoft excel 2007(xlsx)
* @param titleMap : 属性名为 key, 对应的标题为 value
* @param dataMap : ( key : sheetname,value : sheet数据 ) , 如果你只要一个表,那么可以这样:new LinkedHashMap<>( Collections.singletonMap( sheetName , dataMap ) ) )
* @throws IOException
*/
public static void exportXls ( OutputStream outputStream , boolean isExcel07 ,
LinkedHashMap< String, String > titleMap ,
LinkedHashMap< String, List< ? > > dataMap ) throws IOException {
exportXlsToWorkbook( isExcel07 , titleMap , dataMap ).write( outputStream );
}

public static Workbook exportXlsToWorkbook ( boolean isExcel07 ,
LinkedHashMap< String, String > titleMap ,
List< ? > dataList ) throws IOException {
return ExportExcel.handle( isExcel07 ,
titleMap ,
new LinkedHashMap<>( Collections.singletonMap( DEFAULT_SHEET_NAME , dataList ) ) );
}

public static Workbook exportXlsToWorkbook ( boolean isExcel07 ,
LinkedHashMap< String, String > titleMap ,
LinkedHashMap< String, List< ? > > dataMap ) throws IOException {
return ExportExcel.handle( isExcel07 , titleMap , dataMap );
}


public static void exportTxt ( HttpServletResponse response , String fileName , String content ) throws
IOException {
// 导出 txt
setResponse( response , "plain" , fileName );
response.getOutputStream().write( content.getBytes( StandardCharsets.UTF_8 ) );
}

public static void exportCsv ( HttpServletResponse response , String fileName ,
LinkedHashMap< String, String > titleMap , List< ? > dataList ) throws IOException {
String content = convertCsv( titleMap , dataList );

// 导出 csv
setResponse( response , "csv" , fileName );
response.getOutputStream().write( content.getBytes( StandardCharsets.UTF_8 ) );

POIFSFileSystem poifsFileSystem = new POIFSFileSystem();
poifsFileSystem.createDocument( new ByteArrayInputStream( content.getBytes( "GBK" ) ) , "WordDocument" );
poifsFileSystem.writeFilesystem( response.getOutputStream() );
}

private static String convertCsv ( LinkedHashMap< String, String > titleMap , List< ? > dataList ) {

// 没有数据或没有标题, 返回一个内容为空的文件
if ( MapUtils.isEmpty( titleMap ) || CollectionUtils.isEmpty( dataList ) ) return StringUtils.EMPTY;

// 用英文逗号(,)隔开列, 用换行(\n)隔开行, 内容中包含了逗号的需要用双引号包裹, 若内容中包含了双引号则需要用两个双引号表示.
StringBuilder sbd = new StringBuilder();
int i = 0;
for ( String title : titleMap.values() ) {
sbd.append( handleCsvContent( title ) );
i++;
if ( i != titleMap.size() ) sbd.append( "," );
}
for ( Object data : dataList ) {
sbd.append( "\n" );
i = 0;
for ( String title : titleMap.keySet() ) {
sbd.append( handleCsvContent(
ReflectionPlusUtils.invokeFieldGettersMethodToCustomizeString( data , title ) )
);
i++;
if ( i != titleMap.size() ) sbd.append( "," );
}
}
return sbd.toString();
}

private static String handleCsvContent ( String content ) {
return "\"" + content.replace( "\"" , "\"\"" ) + "\"";
}

public static String addXlsSuffix ( String fileName ) {
return StringPrivateUtils.getString( fileName ) + ".xls";
}

public static String addXlsxSuffix ( String fileName ) {
return StringPrivateUtils.getString( fileName ) + ".xlsx";
}

public enum Type {
MS03( "xls" ), MS07( "xlsx" ), CSV( "csv" );

private String value;

Type ( String value ) {
this.value = value;
}

public static boolean is07Suffix ( String fileName ) {
return fileName.toLowerCase().endsWith( Type.MS07.getValue().toLowerCase() );
}

public static boolean is03Suffix ( String fileName ) {
return fileName.toLowerCase().endsWith( Type.MS03.getValue().toLowerCase() );
}

public String getValue () {
return value;
}

public boolean isExcel () {
return Arrays.asList( MS03 , MS07 ).contains( this );
}
}

private static final class ExportExcel {
/** excel 2003 的最大列数是 256 列, 2007 及以上版本是 16384 列 */
private static final int CELL_TOTAL = 256;
/** excel 2003 的最大行数是 65536 行, 等同于 (2 << 15) - 1, 2007 开始的版本是 1048576 行 */
private static final int ROW_TOTAL = 65536;

/** 标题行的字体大小 */
private static final short HEAD_FONT_SIZE = 11;
/** 其他内容的字体大小 */
private static final short FONT_SIZE = 10;
/** 行高. 要比上面的字体大一点! */
private static final short ROW_HEIGHT = 15;

private static boolean must07 ( int cellSize ) {
return cellSize > CELL_TOTAL;
}

/**
* 返回一个 excel 工作簿
*
* @param excel07 是否返回 microsoft excel 2007 的版本
* @param titleMap 属性名为 key, 对应的标题为 value, 为了处理显示时的顺序, 因此使用 linkedHashMap
* @param dataMap 以「sheet 名」为 key, 对应的数据为 value(每一行的数据为一个 Object)
*/
private static Workbook handle ( boolean excel07 , LinkedHashMap< String, String > titleMap ,
LinkedHashMap< String, List< ? > > dataMap ) {
// 如果要导出的不是 07 的版本, 但是导出的列却大于 256 列, 则只能导出 07 版本
if ( ! excel07 ) {
excel07 = must07( titleMap.size() );
}

// 声明一个工作薄. HSSFWorkbook 是 Office 2003 的版本, XSSFWorkbook 是 2007
Workbook workbook = excel07 ? new XSSFWorkbook() : new HSSFWorkbook();
// 没有数据, 或者没有标题, 都直接返回
if ( MapUtils.isEmpty( dataMap ) || MapUtils.isEmpty( titleMap ) ) return workbook;

// 头样式
CellStyle headStyle = createHeadStyle( workbook );
// 内容样式
CellStyle contentStyle = createContentStyle( workbook );

// sheet
Sheet sheet;
//
Row row;
//
Cell cell;
// 表格数 行索引 列索引 数据起始索引 数据结束索引
int sheetCount, rowIndex, cellIndex, fromIndex, toIndex;
// 大小 数据
int size;
List< ? > excelList;

for ( Map.Entry< String, List< ? > > entry : dataMap.entrySet() ) {
// 当前 sheet 的数据
excelList = entry.getValue();
if ( CollectionUtils.isNotEmpty( excelList ) ) {
// 一个 sheet 数据过多 excel 处理会出错, 分多个 sheet
size = excelList.size();
sheetCount = ( ( size % ROW_TOTAL == 0 ) ? ( size / ROW_TOTAL ) : ( size / ROW_TOTAL + 1 ) );
for ( int i = 0 ; i < sheetCount ; i++ ) {
// 构建 sheet, 带名字
sheet = workbook.createSheet( entry.getKey() + ( sheetCount > 1
? ( "-" + ( i + 1 ) )
: StringUtils.EMPTY ) );

// 每个 sheet 的标题行
rowIndex = 0;
cellIndex = 0;
row = sheet.createRow( rowIndex );
row.setHeightInPoints( ROW_HEIGHT );
// 每个 sheet 的标题行
for ( String header : titleMap.values() ) {
// 宽度自适应
sheet.autoSizeColumn( cellIndex );
cell = row.createCell( cellIndex );
cell.setCellStyle( headStyle );
cell.setCellValue( header );
cellIndex++;
}
// 冻结第一行
sheet.createFreezePane( 0 , 1 , 0 , 1 );

// 每个 sheet 除标题行以外的数据
fromIndex = ROW_TOTAL * i;
toIndex = ( i + 1 == sheetCount ) ? size : ROW_TOTAL;
for ( Object data : excelList.subList( fromIndex , toIndex ) ) {
if ( data != null ) {
rowIndex++;
// 每行
row = sheet.createRow( rowIndex );
row.setHeightInPoints( ROW_HEIGHT );
cellIndex = 0;
for ( String value : titleMap.keySet() ) {
String cellValue;
if ( data instanceof Map ) {
final Object obj = ( ( Map ) data ).get( value );
cellValue = Objects.isNull( obj ) ? StringUtils.EMPTY : obj.toString();
} else {
cellValue =
ReflectionPlusUtils.invokeFieldGettersMethodToCustomizeString( data ,
value );
}
// 每列
cell = row.createCell( cellIndex );
cell.setCellStyle( contentStyle );
cell.setCellValue( cellValue );
cellIndex++;
}
}
}
}
}
}
return workbook;
}

/** 头样式 */
private static CellStyle createHeadStyle ( Workbook workbook ) {
CellStyle style = workbook.createCellStyle();

style.setAlignment( HorizontalAlignment.LEFT ); // 水平居左
style.setVerticalAlignment( VerticalAlignment.CENTER ); // 垂直居中
// style.setWrapText(true); // 自动换行

Font font = workbook.createFont();
font.setBold( true ); // 粗体
font.setFontHeightInPoints( HEAD_FONT_SIZE );
style.setFont( font );
return style;
}

/** 内容样式 */
private static CellStyle createContentStyle ( Workbook workbook ) {
CellStyle style = workbook.createCellStyle();

style.setAlignment( HorizontalAlignment.LEFT );
style.setVerticalAlignment( VerticalAlignment.CENTER );

Font font = workbook.createFont();
font.setFontHeightInPoints( FONT_SIZE );
style.setFont( font );
return style;
}
}
}
View Code

 

posted @ 2019-09-11 19:07  刷鱼不刷牙  阅读(272)  评论(0编辑  收藏  举报