zoukankan      html  css  js  c++  java
  • 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;
    
    // 用英文逗号(,)隔开列, 用换行(
    )隔开行, 内容中包含了逗号的需要用双引号包裹, 若内容中包含了双引号则需要用两个双引号表示.
    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( "
    " );
    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
  • 相关阅读:
    JavaScript模态对话框类
    事件模块的演变(1)
    html5中可通过document.head获取head元素
    How to search for just a specific file type in Visual Studio code?
    What do 'lazy' and 'greedy' mean in the context of regular expressions?
    正则非获取匹配 Lookahead and Lookbehind ZeroLength Assertions
    regex length 正则长度问题
    Inversion of Control vs Dependency Injection
    How to return View with QueryString in ASP.NET MVC 2?
    今天才发现Google Reader
  • 原文地址:https://www.cnblogs.com/dayuss/p/11508125.html
Copyright © 2011-2022 走看看