poi導出excel

一.怎么定義通用工具?

具體做法如下

    1. 定義注解類
    1. 導出工具類

二.怎么樣實現?

    1. 注解類
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.CellStyle;

/**
 * excel列表注解,包括頭,體,尾
 * 頭,體,尾只有加粗不加粗差別
 * @author lilong
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelColumn {

    /**
     * 表頭列名,必填
     * @return
     */
    public String title() default "";
    
    /**
     * 字段位置 第幾個
     * @return
     */
    public int order() default Integer.MIN_VALUE;
    
    /**
     * 列寬度 默認25
     * @return
     */
    public int width() default 25;
    
    /**
     * 自適應寬度 默認true
     * 注:自適應優先級高于固定寬度
     * @return
     */
    public boolean autoWidth() default true;
    
    /**
     * 內容數據類型,目前只支持String.class和Double.class
     * 注:如果不是double那就是String
     * @return
     */
    public Class<?> dataType() default String.class;
    
    /**
     * 文字樣式 默認居左
     * 其他風格查看CellStyle類
     * @return
     */
    public short align() default CellStyle.ALIGN_LEFT;
    
    /**
     * 單元格是否需要邊框 環繞包圍 默認false
     * @return
     */
    public boolean border() default false;
    
    /**
     * 單元格背景色 默認白色 HSSFColor.WHITE.index
     * 還可以支持WHITE,BLACK,BLUE,RED,YELLOW等,具體可以查看HSSFColor類
     * @return
     */
    public short styleColor() default HSSFColor.WHITE.index;
    
    /**
     * 字體顏色 默認黑色 HSSFColor.BLACK.index
     * 還可以支持BLACK,BLUE,RED,YELLOW等,具體可以查看HSSFColor類
     * @return
     */
    public short fontColor() default HSSFColor.BLACK.index;
    
    /**
     * 字體大小 默認12
     * @return
     */
    public short fontSize() default 12;
    
    /**
     * 字體 默認微軟雅黑
     * @return
     */
    public String fontName() default "微軟雅黑";
    
    /**
     * 頭字體是否加粗及title內容 默認不加粗
     * @return
     */
    public boolean headBold() default true;
    
    /**
     * 體字體是否加粗及數據內容 默認不加粗
     * @return
     */
    public boolean bodyBold() default false;
    
    /**
     * 尾字體是否加粗及最后一行數據 默認不加粗
     * @return
     */
    public boolean tailBold() default false;
    
}
    1. 工具類
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;


/**
 * 導出對象T集合
 * 注:不支持導出屬性來源于父類
 * @author lilong
 *
 * @param <T>
 */
public class ExcelUtil {
    
    /**
     * 通過response導出excel,單個sheet,包含匯總行
     * @param fileName 文件名稱
     * @param sheetName sheet名稱
     * @param data 數據
     * @param response
     */
    public static <T> void exportAndTotal(String fileName,String sheetName,List<T> data,HttpServletResponse response) {
        Map<String, List<T>> datas = new HashMap<>();
        datas.put(sheetName, data);
        exportAndTotal(fileName, datas, response);
    }
    
    /**
     * 通過response導出excel,單個sheet,不包含匯總行
     * @param fileName 文件名稱
     * @param sheetName sheet名稱
     * @param data 數據
     * @param response
     */
    public static <T> void export(String fileName,String sheetName,List<T> data,HttpServletResponse response) {
        Map<String, List<T>> datas = new HashMap<>();
        datas.put(sheetName, data);
        export(fileName, datas, response);
    }
    
    /**
     * 通過response導出excel,多個sheet,包含匯總行
     * @param datas
     * @param response
     */
    public static <T> void exportAndTotal(String fileName,Map<String, List<T>> datas,HttpServletResponse response) {
        try {
            //設置response類型,進行轉碼,使其支持中文文件名
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xls");
            export(datas, true, response.getOutputStream());
        }catch (RuntimeException e) {
            throw e;
        }catch (Exception e) {
            throw new RuntimeException("導出excel異常",e);
        }
    }
    
    /**
     * 通過response導出excel,多個sheet,不包含匯總行
     * @param datas
     * @param response
     */
    public static <T> void export(String fileName,Map<String, List<T>> datas,HttpServletResponse response) {
        try {
            //設置response類型,進行轉碼,使其支持中文文件名
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xls");
            export(datas, false, response.getOutputStream());
        }catch (RuntimeException e) {
            throw e;
        }catch (Exception e) {
            throw new RuntimeException("導出excel異常",e);
        }
    }

    /**
     * 導出excel
     * @param datas key:sheet名稱,value:每頁對應的數據
     * @param isTotal 是否包含匯總行
     * @param os 輸出流
     */
     static <T> void export(Map<String, List<T>> datas,boolean isTotal,OutputStream os) {
        //有數據才導出
        if(datas != null && datas.size() > 0) {
            HSSFWorkbook wk = new HSSFWorkbook();
            for(Map.Entry<String, List<T>> data : datas.entrySet()) {
                HSSFSheet sheet = wk.createSheet(data.getKey());
                
                List<T> lists = data.getValue();
                if(lists.size() > 0) {
                    //獲取元素解析字段
                    Map<Integer, Column> map = parse(lists.get(0).getClass());
                    
                    //創建頭樣式和列樣式,目前頭和數據樣式一樣,頭樣式字體必須加粗
                    Map<Integer,HSSFCellStyle> heads = new HashMap<>();
                    Map<Integer,HSSFCellStyle> bodys = new HashMap<>();
                    Map<Integer,HSSFCellStyle> tails = new HashMap<>();
                    for(int i = 0;i < map.size();i ++) {
                        Column column = map.get(i);
                        HSSFCellStyle headCellStyle = createCellStyle(wk, column, column.isHeadBold());
                        heads.put(i, headCellStyle);
                        
                        HSSFCellStyle bodyCellStyle = createCellStyle(wk, column, column.isBodyBold());
                        bodys.put(i, bodyCellStyle);
                        
                        HSSFCellStyle tailCellStyle = createCellStyle(wk, column, column.isTailBold());
                        tails.put(i, tailCellStyle);
                    }
                    
                    //創建head,body,tail
                    createHead(sheet, heads, map);
                    if(isTotal) {
                        createBody(sheet, bodys, map, lists,true);
                        createTail(sheet, tails, map, lists);
                    }else {
                        createBody(sheet, bodys, map, lists,false);
                    }
                    
                    //設置列寬度
                    setColumnWidth(sheet, map);
                }
            }
            
            try {
                wk.write(os);
            }catch (Exception e) {
                throw new RuntimeException("導出excel異常",e);
            }
        }
    }
     
     /**
      * 創建頭
      * @param sheet
      * @param heads 頭樣式
      * @param map 頭標題
      */
     static void createHead(HSSFSheet sheet,Map<Integer,HSSFCellStyle> heads,Map<Integer, Column> map) {
           HSSFRow headRow = sheet.createRow(0);
           for(int i = 0;i < map.size();i ++) {
                Column column = map.get(i);
                
                HSSFCell cell = headRow.createCell(i);
                cell.setCellStyle(heads.get(i));
                cell.setCellValue(new HSSFRichTextString(column.getTitle()));
                setWidth(column, cell.getStringCellValue());
           }
     }
     
     /**
      * 創建體
      * @param sheet
      * @param bodys 體樣式
      * @param map 獲取列值
      * @param lists 導出數據
      * @param isTotal 是否包含匯總行,包含匯總行不會輸出匯總行
      */
     static <T> void createBody(HSSFSheet sheet,Map<Integer,HSSFCellStyle> bodys,
             Map<Integer, Column> map,List<T> lists,boolean isTotal) {
         for(int i = 1;i < lists.size();i ++) {
            write(sheet, map, bodys, i, lists.get(i - 1));
         }
         
         if(!isTotal) {
             write(sheet, map, bodys, lists.size(), lists.get(lists.size() - 1));
         }
     }
     
     /**
      * 創建體
      * @param sheet
      * @param bodys 體樣式
      * @param map 獲取列值
      * @param lists 導出數據
      */
     static <T> void createTail(HSSFSheet sheet,Map<Integer,HSSFCellStyle> tails,Map<Integer, Column> map,List<T> lists) {
         write(sheet, map, tails, lists.size(), lists.get(lists.size() - 1));
     }
     
     /**
      * 設置列寬度
      * @param sheet
      * @param map
      */
     static void setColumnWidth(HSSFSheet sheet,Map<Integer, Column> map) {
         for(int i = 0;i < map.size();i ++) {
            Column column = map.get(i);
            sheet.setColumnWidth(i, column.getWidth() * 256);
         }
     }
     
    /**
     * 
     * @param sheet excel sheet
     * @param map 導出對象
     * @param bodys 數據樣式
     * @param row 行號
     * @param t 對象
     */
    static <T> void write(HSSFSheet sheet,Map<Integer, Column> map,Map<Integer,HSSFCellStyle> bodys,int row,T t) {
        HSSFRow line = sheet.createRow(row);
        
        //創建body
        for(int i = 0;i < map.size();i ++) {
            HSSFCell cell = line.createCell(i);
            Column column = map.get(i);
            
            //設置樣式和屬性值
            cell.setCellStyle(bodys.get(i));
            setCellValue(cell, t, column);
        }
    }
    
    /**
     * 根據column定義設置cell樣式
     * @param wk excel對象
     * @param cell 列對象
     * @param column 樣式結構體
     * @param isBold 是否加粗,由于頭,體,尾都不一樣所有需要傳值更方便
     */
    static <T> HSSFCellStyle createCellStyle(HSSFWorkbook wk,Column column,boolean isBold) {
        short align = column.getAlign();
        boolean border = column.isBorder();
        short fontColor = column.getFontColor();
        short fontSize = column.getFontSize();
        String fontName = column.getFontName();
        
        //設置樣式
        HSSFCellStyle style = wk.createCellStyle();
        style.setAlignment(align);
        style.setFillForegroundColor(fontColor);
        if(border) {
            style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
            style.setBorderTop(HSSFCellStyle.BORDER_THIN);
            style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
            style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
            style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        }
        
        //設置字體
        HSSFFont font = wk.createFont();
        font.setFontName(fontName);
        font.setColor(fontColor);
        //設置字體大小
        font.setFontHeightInPoints(fontSize);
        
        //設置字體是否加粗
        if(isBold) {
            font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        }
        
        //把字體應用到樣式
        style.setFont(font);
        
        return style;
    }
    
    /**
     * 通過字段名稱從t中獲取屬性值設置cell值,通過修改tmpWidth
     * @param cell 類對象
     * @param t 對象信息
     * @param fieldName 字段信息
     * 注:只讀取當前類中的屬性值
     */
    static <T> void setCellValue(HSSFCell cell,T t,Column column) {
        try {
            Field field = t.getClass().getDeclaredField(column.getFieldName());
            field.setAccessible(true);
            //轉換任意類型為String
            String value = ConvertUtils.convert(field.get(t));
            setWidth(column, value);
            
            if(column.getDataType().equals(Double.class)) {
                try {
                    cell.setCellValue(Double.parseDouble(value));
                }catch (Exception e) {
                    HSSFRichTextString text = new HSSFRichTextString(value);
                    cell.setCellValue(text);
                }
            }else {
                HSSFRichTextString text = new HSSFRichTextString(value);
                cell.setCellValue(text);
            }
            
        }catch (Exception e) {
            throw new RuntimeException("屬性獲取或者操作錯誤",e);
        }
    }
    
    /**
     * 自動生成寬度則修改width
     * @param column
     * @param value
     */
    static <T> void setWidth(Column column,String value){
        if(column.isAutoWidth() && value != null && value.length() > 0) {
            Double length = 0.0;
            for(int i = 0;i < value.length();i ++) {
                char c = value.charAt(i);
                if(isChinese(c)) {
                    length += 2.8;
                }else {
                    length += 1.4;
                }
            }
            
            if(length.intValue() > column.getWidth()) {
                column.setWidth(length.intValue());
            }
        }
    }
    
    static boolean isChinese(char c) {   
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);  
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS  
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS  
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A  
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION  
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION  
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {  
            return true;
        }
        return false;  
    }
    
    /**
     * 把類中需要生成excel的字段生成order和屬性的映射
     * @param clazz
     * @return
     */
    static <T> Map<Integer, Column> parse(Class<T> clazz){
        List<Column> columns = new ArrayList<>();
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields) {
            if(field.isAnnotationPresent(ExcelColumn.class)) {
                ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
                Column column = new Column();
                column.setFieldName(field.getName());
                column.setTitle(annotation.title());
                column.setOrder(annotation.order());
                column.setAutoWidth(annotation.autoWidth());
                column.setDataType(annotation.dataType());
                column.setWidth(annotation.autoWidth()?0:annotation.width());
                column.setAlign(annotation.align());
                column.setBorder(annotation.border());
                column.setFontColor(annotation.fontColor());
                column.setFontSize(annotation.fontSize());
                column.setFontName(annotation.fontName());
                column.setHeadBold(annotation.headBold());
                column.setBodyBold(annotation.bodyBold());
                column.setTailBold(annotation.tailBold());
                
                columns.add(column);
            }
        }
        
        //由于order可以亂填,需要把order處理成一個從0開始的連續的數字,然后生成一個order和字段注解屬性的關系
        Collections.sort(columns, new Comparator<Column>() {

            @Override
            public int compare(Column c1, Column c2) {
                return c1.getOrder() - c2.getOrder();
            }
        });
        
        for(int i = 0;i < columns.size();i ++) {
            columns.get(i).setOrder(i);
        }
        
        //根據order生成一個映射關系
        Map<Integer, Column> map = new HashMap<>();
        for(Column column : columns) {
            map.put(column.getOrder(), column);
        }
        
        return map;
    }
    
    static class Column {
        
        private String fieldName; //屬性名稱
        private String title; //標題
        private int order; //排序
        private int width; //寬度
        private boolean autoWidth; //自適應寬度
        private Class<?> dataType;//文本框類型
        private short align; //位置
        private boolean border; //邊框
        private short fontColor; //字體顏色
        private short fontSize; //字體大小
        private String fontName; //字體名稱
        private boolean headBold; //頭字體是否加粗
        private boolean bodyBold; //體字體是否加粗
        private boolean tailBold; //尾字體是否加粗
        
        public String getFieldName() {
            return fieldName;
        }
        public void setFieldName(String fieldName) {
            this.fieldName = fieldName;
        }
        public String getTitle() {
            return title;
        }
        public void setTitle(String title) {
            this.title = title;
        }
        public int getOrder() {
            return order;
        }
        public void setOrder(int order) {
            this.order = order;
        }
        public int getWidth() {
            return width;
        }
        public void setWidth(int width) {
            this.width = width;
        }
        public boolean isAutoWidth() {
            return autoWidth;
        }
        public void setAutoWidth(boolean autoWidth) {
            this.autoWidth = autoWidth;
        }
        public Class<?> getDataType() {
            return dataType;
        }
        public void setDataType(Class<?> dataType) {
            this.dataType = dataType;
        }
        public short getAlign() {
            return align;
        }
        public void setAlign(short align) {
            this.align = align;
        }
        public boolean isBorder() {
            return border;
        }
        public void setBorder(boolean border) {
            this.border = border;
        }
        public short getFontColor() {
            return fontColor;
        }
        public void setFontColor(short fontColor) {
            this.fontColor = fontColor;
        }
        public short getFontSize() {
            return fontSize;
        }
        public void setFontSize(short fontSize) {
            this.fontSize = fontSize;
        }
        public String getFontName() {
            return fontName;
        }
        public void setFontName(String fontName) {
            this.fontName = fontName;
        }
        public boolean isHeadBold() {
            return headBold;
        }
        public void setHeadBold(boolean headBold) {
            this.headBold = headBold;
        }
        public boolean isBodyBold() {
            return bodyBold;
        }
        public void setBodyBold(boolean bodyBold) {
            this.bodyBold = bodyBold;
        }
        public boolean isTailBold() {
            return tailBold;
        }
        public void setTailBold(boolean tailBold) {
            this.tailBold = tailBold;
        }
    }
    
}

注:代碼使用了lombok注解

三.怎么樣使用?

  • 1.定義導出類
import java.math.BigDecimal;

import org.apache.poi.ss.usermodel.CellStyle;

import lombok.Data;

/**
 * test excel
 * @author lilong
 */
@Data
public class TestExcelVO {
  
    @ExcelColumn(title = "編號", order = -1)
    protected String num;
    @ExcelColumn(title = "名稱", order = 0)
    protected String name;
    @ExcelColumn(title = "年齡", order = 1, dataType = Double.class)
    protected Integer age;
    @ExcelColumn(title = "性別", order = 2)
    protected String sex;
    @ExcelColumn(title = "零花錢", order = 3,align=CellStyle.ALIGN_RIGHT)
    protected BigDecimal amount;

}
  • 2.調用工具類
@RequestMapping(value="/test")
public void test(HttpServletResponse response){
    List<TestExcelVO> lt = Lists.newArrayList();
        //導出十條數據
        BigDecimal total = BigDecimal.ZERO;
        for(int i = 0;i < 10;i ++) {
            TestExcelVO vo = new TestExcelVO();
            vo.setNum(i+"");
            vo.setAge(10);
            vo.setSex(i/2==0?"男":"女");
            vo.setName("張三"+i);
                
            BigDecimal amount = new BigDecimal(i/2);
            total = total.add(amount);
            vo.setAmount(amount);
                
            lt.add(vo);
        }
            
        //匯總行以數據形式添加
        TestExcelVO vo = new TestExcelVO();
        vo.setNum("匯總");
        vo.setAmount(total);
        lt.add(vo);
        
        ExcelUtil.exportAndTotal("測試excel", "測試excel", lt, response);
  }
  • 3.學習其他接口
    注解類和接口其他方法請自行理解使用
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,558評論 25 708
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,975評論 2 59
  • 使用Apache POI導出Excel小結 關于使用Apache POI導出Excel我大概會分三篇文章去寫 使用...
    JianweiZhou閱讀 9,055評論 3 14
  • 工作需要,花了一個星期搭建了gitlab:1、關閉了gitlab的注冊功能2、修改了默認端口3、漢化并添加了401...
    cws閱讀 5,009評論 1 1
  • 1.電影院的列表排序:收藏的影院 距離你當前位置近的影院 問題:只能修改城市,不能修改城市中的具體位置,只能定位...
    Petra閱讀 793評論 0 1