1. 程式人生 > >java操作excel文件通用工具類

java操作excel文件通用工具類

1.自定義註解類

 

package cn.com.test.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * 自定義註解
 * @author:   Rodge
 * @time:     2017年12月26日 下午8:32:10
 * @version:  V1.0.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CellField {

	/**
     * 檔案列名
     */
    public String name() default "";
    
}

 

2.excel工具類

 

package cn.com.javatest.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.FileMagic;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import cn.com.javatest.annotation.CellField;


/**
 * Excel工具類
 * 
 * @author:   Rodge
 * @time:     2017年12月26日 下午8:54:10
 * @version:  V1.0.0
 */
public final class ExcelUtils {
	
	 /** 日誌物件 **/  
    private static final Logger LOGGER = LoggerFactory.getLogger(ExcelUtils.class);  
      
    /** 私有無參構造方法 **/  
    private ExcelUtils() { }  
     
    /** 
     * 生成excel2007文件 
     *   
     * @param filePath 檔案儲存路徑 ,例如:D:/excel/test.xlsx 
     * @param beans 實體物件 
     */  
    public static <T> void createExcel(String filePath, List<T> beans) {  
        String fileName = filePath.substring(filePath.lastIndexOf('/') + 1);  
        Workbook wb = createWorkbook(beans, fileName);  
        File file = createFile(filePath);  
        try (OutputStream os = new FileOutputStream(file)) {  
            wb.write(os);  
        } catch (IOException e) {  
            LOGGER.error("生成excel2007檔案失敗", e);                                   
        }  
    }  
      
    /** 
     * 匯出excel2007檔案 
     *   
     * @param response 響應物件 
     * @param beans 實體物件集合 
     */  
    public static <T> void exportExcel(HttpServletResponse response, List<T> beans) {  
        // 以當前時間作為檔名  
        String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());  
        // 建立Workbook物件  
        Workbook wb = createWorkbook(beans, fileName);  
        // 匯出excel  
        try (OutputStream os = response.getOutputStream()) {  
        	response.setContentType("application/octet-stream;charset=UTF-8");
    		response.setHeader("Access-Control-Allow-Origin", "*");
    		response.setHeader("Access-Control-Expose-Headers", "*");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            wb.write(os);  
        } catch (IOException e) {  
            LOGGER.error("匯出excel2007檔案失敗", e);  
        }  
    }  
    
    /** 
     * 匯出excel2007檔案 
     *   
     * @param response 響應物件 
     * @param beans 實體物件集合 
     * @param fileName 檔名稱 ,如:測試名稱
     */  
    public static <T> void exportExcel(HttpServletResponse response, List<T> beans, String fileName) {  
    	// 建立Workbook物件  
    	Workbook wb = createWorkbook(beans, fileName);  
    	// 匯出excel  
    	try (OutputStream os = response.getOutputStream()) {  
    		response.setContentType("application/octet-stream;charset=UTF-8");
    		response.setHeader("Access-Control-Allow-Origin", "*");
    		response.setHeader("Access-Control-Expose-Headers", "*");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
    		wb.write(os); 
    	} catch (IOException e) {  
    		LOGGER.error("匯出excel2007檔案失敗", e);  
    	}  
    }  
    
    /** 
     * 匯出excel2007模板
     *   
     * @param response 響應物件 
     * @param Class<T> 類型別 
     */  
    public static <T> void exportExcelTemplate(HttpServletResponse response, Class<T> clazz) {  
    	// 以當前時間作為檔名  
    	String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());  
    	exportExcelTemplate(response, clazz, fileName);
    }  
    
    /** 
     * 匯出excel2007模板
     *   
     * @param response 響應物件 
     * @param Class<T> 類型別 
     * @param fileName 檔名稱
     */  
    public static <T> void exportExcelTemplate(HttpServletResponse response, Class<T> clazz, String fileName) {  
    	// 1.建立2007版工作簿   
    	Workbook wb = new XSSFWorkbook();     
    	// 2.建立工作表  
    	Sheet sheet = wb.createSheet(fileName);  
    	// 3.獲取實體類標有@CellField註解的Field物件  
    	List<Field> fields = new ArrayList<Field>();
    	Field[] declaredFields = clazz.getDeclaredFields();    
    	for (Field field : declaredFields) {  
    		CellField anno = field.getAnnotation(CellField.class);   
    		if (anno != null) {   
    			fields.add(field);    
    		}  
    	}
    	// 4.設定列寬  
    	for (int i = 0; i < fields.size(); i++) {  
    		sheet.setColumnWidth((short) i, (short) (20.7 * 150));  
    	}  
    	// 5.寫入標題  
    	Row row = sheet.createRow(0);  
    	writeTitles(fields, row);
    	// 6.匯出excel模板  
    	try (OutputStream os = response.getOutputStream()) {  
    		response.setContentType("application/octet-stream;charset=UTF-8");
    		response.setHeader("Access-Control-Allow-Origin", "*");
    		response.setHeader("Access-Control-Expose-Headers", "*");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");  
            wb.write(os);  
    	} catch (IOException e) {  
    		LOGGER.error("匯出excel2007模板失敗", e);  
    	} finally {
    		if (wb != null) {
    			try {
    				wb.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	} 
    }  
    
    /** 
     * 解析Excel文件,轉成物件集合返回 
     *   
     * @param filePath 檔案存放路徑,例如:D:/excel/test.xlsx 
     * @param clazz 泛型類型別 
     * @return List<T> 物件集合 
     */  
    public static <T> List<T> parseExcel(String filePath, Class<T> clazz) {  
        List<T> list = new ArrayList<>();  
        InputStream input = null;  
        try {  
            input = new FileInputStream(new File(filePath));  
            if (input != null && !input.markSupported()) {  
                input = new PushbackInputStream(input, 8);  
            }  
            Workbook wb = getWorkbook(input);  
            if (wb != null) {  
                list = getBeanList(wb, clazz);  
            }  
        } catch (Exception e) {  
            LOGGER.error("解析檔案失敗", e);  
        } finally {  
            if (input != null) {  
                try {  
                    input.close();  
                } catch (IOException e) {  
                    LOGGER.error("關閉流失敗", e);  
                }  
            }  
        }  
        return list;  
    }   
      
    /** 
     * 刪除該目錄下所有檔案 
     * @param filePath 檔案目錄路徑,如:d:/test/ 
     * @return boolean 
     */  
    public static boolean deleteFiles(String filePath) {  
        File file = new File(filePath);  
        if (file.exists()) {  
            File[] files = file.listFiles();  
            if (files != null && files.length > 0) {  
                for (File f : files) {  
                    if (f.isFile() && f.delete()) {  
                        LOGGER.info("刪除" + f.getName() + "檔案成功");  
                    }  
                }  
                return true;  
            }  
        }  
        return false;  
    }  
  
    /** 
     * 刪除指定檔案 
     * @param filePath 檔案目錄路徑,如:d:/test/ 
     * @param fileName 檔名稱,如:test.xlsx 
     * @return boolean 
     */  
    public static boolean deleteFile(String filePath, String fileName) {  
        File file = new File(filePath);  
        if (file.exists()) {  
            File[] files = file.listFiles();  
            if (files != null && files.length > 0) {  
                for (File f : files) {  
                    if (f.isFile() && f.getName().equals(fileName)) {  
                        return f.delete();  
                    }  
                }  
            }  
        }  
        return false;  
    } 
    
    /** 
     * 獲取Workbook物件 
     *   
     * @param input 輸入流 
     * @return Workbook 
     */  
    private static Workbook getWorkbook(InputStream input) {  
        try {  
            if (POIFSFileSystem.hasPOIFSHeader(input)) {  
                return new HSSFWorkbook(input); // 得到2003工作簿  
            } else {
            	try (OPCPackage op = OPCPackage.open(input)) {  
            		return new XSSFWorkbook(op); // 得到2007工作簿  
            	} catch (Exception e) {  
            		LOGGER.error("獲取Workbook物件失敗", e);  
            	}   
			} 
        } catch (IOException e) {  
            LOGGER.error("獲取Workbook物件失敗", e);  
        }  
        return null;  
    } 
    
    /** 
     * 通過泛型轉換為物件集合 
     *   
     * @param wb 工作簿物件 
     * @param clazz 實體物件類型別 
     * @param <T> 泛型型別 
     * @return <T> 泛型實體物件 
     */  
    private static <T> List<T> getBeanList(Workbook wb, Class<T> clazz) {  
        List<T> list = new ArrayList<T>();  
        // 註解名稱與欄位屬性的對應關係  
        Map<String, Field> annoMap = getFields(clazz);  
        // 列所引與標題名稱對應關係  
        Map<Integer, String> titleMap = new HashMap<Integer, String>();  
        Sheet sheet = wb.getSheetAt(0); // 獲取第一張工作表  
        Iterator<Row> rows = sheet.iterator(); // 利用迭代器,取出每一個行  
        int index = 0;  
        while (rows.hasNext()) {  
            Row row = rows.next(); // 每一行  
            Iterator<Cell> cells = row.iterator(); // 利用迭代器,取出每一個格  
            if (index == 0) {   
                // 獲取第一行標題  
                while (cells.hasNext()) {  
                    Cell cell = cells.next(); // 每一格  
                    titleMap.put(cell.getColumnIndex(), cell.getStringCellValue().trim());                         
                }   
            } else {  
                // 建立實體物件,把值設定進去  
                if (!annoMap.isEmpty() && !titleMap.isEmpty()) {  
                    list.add(buildBean(annoMap, titleMap, clazz, cells));  
                }  
            }  
            index++;  
        }      
        return list;  
    } 
    
    /** 
     * 註解名稱與欄位屬性的對應關係 
     *   
     * @param clazz 實體物件類型別 
     * @return Map<String,Field> 
     */  
    private static <T> Map<String, Field> getFields(Class<T> clazz) {  
        Map<String, Field> annoMap = new HashMap<String, Field>();  
        Field[] fileds = clazz.getDeclaredFields();  
        for (Field filed : fileds) {  
            CellField cellField = filed.getAnnotation(CellField.class);  
            if (cellField != null && StringUtils.isNotBlank(cellField.name())) {  
                annoMap.put(cellField.name(), filed);  
            }  
        }  
        return annoMap;  
    } 
    
    /** 
     * 建立實體,設定值 
     *   
     * @param annoMap 註解名稱與欄位屬性的對應的Map 
     * @param titleMap excel列所引與標題名稱對應的Map 
     * @param clazz 實體物件類型別 
     * @param cells 每一行的所有格子 
     * @return List<T> 
     */  
    private static <T> T buildBean(Map<String, Field> annoMap, Map<Integer, String> titleMap, Class<T> clazz, Iterator<Cell> cells) {  
        T t = null;  
        try {  
            t = clazz.newInstance();  
            while (cells.hasNext()) {  
                Cell cell = cells.next(); // 每一格  
                String title = titleMap.get(cell.getColumnIndex());  
                if (annoMap.containsKey(title)) {  
                    Field field = annoMap.get(title);  
                    Class<?> valType = field.getType();
                    Object value = getType(cell.getStringCellValue(), valType);  
                    String fieldName = field.getName();                                                     
                    String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);    
                    Method method = ReflectionUtils.findMethod(clazz, methodName, valType);  
                    if (method != null) {  
                        ReflectionUtils.invokeMethod(method, t, value);  
                    }    
                }  
            }   
        } catch (Exception e) {  
            LOGGER.error("建立泛型實體物件失敗", e);  
        }  
        return t;  
    } 
    
	/** 
     * 轉換成實體屬性對應的型別 
     *   
     * @param value 每一格的數值 
     * @param valType 實體屬性型別 
     * @return Object 轉換為對應型別以obj返回 
     */  
    private static <T> Object getType(String value, Class<T> valType) {  
        try {  
            if (valType == String.class) {   
                return String.valueOf(value);  
            } else if (valType == Date.class) {    
            	SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  // 預設格式
                return sdf.parse(value);    
            } else if (valType == Double.class) {    
                return Double.parseDouble(value);    
            }else if (valType == BigDecimal.class) {    
                return new BigDecimal(value);    
            } else if (valType == Integer.class) {    
                return Integer.parseInt(value);    
            } else if (valType == Long.class) {    
                return Long.parseLong(value);    
            } else if (valType == Boolean.class) {    
                return Boolean.parseBoolean(value);    
            }   
        } catch (Exception e) {  
            LOGGER.error("型別轉換異常", e);  
        }  
        return value;    
    }
      
    /** 
     * 建立Workbook物件 
     *   
     * @param beans 實體集合物件 
     * @param fileName 檔名 
     * @return Workbook 
     */  
    private static <T> Workbook createWorkbook(List<T> beans, String fileName) {  
        // 1.建立2007版工作簿   
        Workbook wb = new XSSFWorkbook();     
        // 2.建立工作表  
        Sheet sheet = wb.createSheet(fileName);  
        // 3.獲取實體類標有@CellField註解的Field物件  
        List<Field> fields = getFields(beans);  
        // 4.設定列寬  
        for (int i = 0; i < fields.size(); i++) {  
            sheet.setColumnWidth((short) i, (short) (20.7 * 150));  
        }  
        // 5.寫入標題  
        Row row = sheet.createRow(0);  
        writeTitles(fields, row);  
        // 6.寫入內容  
        writeContents(beans, fields, sheet);  
        return wb;  
    }  
        
    /** 
     * 建立檔案物件 
     *   
     * @param filePath 儲存路徑 
     * @return File 
     */  
    private static File createFile(String filePath) {  
        File file = null;  
        try {  
            // 建立檔案目錄  
            file = new File(filePath.substring(0, filePath.lastIndexOf('/')));  
            if (!file.exists()) {   
                file.mkdirs();  
            }   
            // 建立檔案路徑  
            file = new File(filePath);    
            if (!file.exists() && file.createNewFile()) {   
                LOGGER.info("建立檔案物件成功");           
            }  
        } catch (IOException e) {  
            LOGGER.error("建立檔案物件失敗", e);  
        }  
        return file;  
    }  
      
    /** 
     * 寫入標題 
     *   
     * @param fields Field物件集合 
     * @param row 行物件 
     */  
    private static void writeTitles(List<Field> fields, Row row) {  
        if (fields != null && !fields.isEmpty()) {  
            for (int i = 0; i < fields.size(); i++) {  
                CellField cellField = fields.get(i).getAnnotation(CellField.class);  
                if (cellField == null) {  
                    continue;  
                }  
                Cell cell = row.createCell(i);  
                // 獲取帶有name屬性的值並寫入  
                if (StringUtils.isNotBlank(cellField.name())) {  
                    cell.setCellValue(cellField.name());  
                }  
            }  
        }  
    }  
      
    /** 
     * 寫入內容 
     *   
     * @param beans 實體物件集合 
     * @param fields Field物件集合 
     * @param sheet 工作表物件 
     * @param <T> 泛型類 
     */  
    private static <T> void writeContents(List<T> beans, List<Field> fields, Sheet sheet) {  
        for (int i = 0; i < beans.size(); i++) {   
            T t = beans.get(i);  
            Row row = sheet.createRow(i + 1);  
            for (int j = 0; j < fields.size(); j++) {   
                CellField cellField = fields.get(j).getAnnotation(CellField.class);  
                if (cellField != null) {  
                    Class<?> valType = fields.get(j).getType(); // 獲取屬性型別  
                    String fieldName = fields.get(j).getName(); // 獲取屬性名  
                    String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);    
                    Method method = ReflectionUtils.findMethod(t.getClass(), methodName);  
                    setValues(method, t, j, valType, cellField, row); // 設定值  
                }  
            }    
        }   
    }  
      
    /** 
     * 設定值 
     *   
     * @param method 方法物件 
     * @param t 泛型物件 
     * @param j 下標 
     * @param valType 型別物件 
     * @param cellField 註解物件 
     * @param row 行物件 
     * @param <T> 泛型 
     */  
    private static <T> void setValues(Method method, T t, int j, Class<?> valType, CellField cellField, Row row) {  
        if (method != null) {  
            Object value = ReflectionUtils.invokeMethod(method, t); 
            if (value != null && valType == Date.class) { // 預設日期型別格式:yyyy-MM-dd HH:mm:ss  
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
                row.createCell(j).setCellValue(sdf.format(((Date) value).getTime()));  
            } else { // 字串  
                row.createCell(j).setCellValue(value == null ? "" : String.valueOf(value));  
            }  
        }   
    }  
                  
    /** 
     * 獲取標有@CellField註解的Field物件 
     *   
     * @param beans 物件集合 
     * @return List<Field> 
     */  
    private static <T> List<Field> getFields(List<T> beans) {  
        Class<? extends Object> cls = beans.get(0).getClass();    
        Field[] declaredFields = cls.getDeclaredFields();    
        List<Field> annoFields = new ArrayList<Field>();     
        // 篩選出標有註解的欄位  
        for (Field field : declaredFields) {  
            CellField anno = field.getAnnotation(CellField.class);   
            if (anno != null) {   
                annoFields.add(field);    
            }  
        }  
        return annoFields;  
    }  
      
}

 

3.測試實體類

 

package cn.com.test.entity;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import cn.com.test.annotation.CellField;

/**
 * 檔案資訊實體類
 * @author:  Rodge
 * @time:    2017年12月26日 下午8:22:14
 * @version: V1.0.0
 */
public class IPOFileInfo {

	/**檔案所屬型別*/
	@CellField(name = "檔案型別")
	private String fileType;
	
	/**檔案所屬編碼 */
	@CellField(name = "檔案編碼")
	private String fileCode;
	
	/**檔名稱 */
	@CellField(name = "檔名稱")
	private String fileName;
	
	/**檔案URL */
	@CellField(name = "檔案URL")
	private String fileUrl;
	
	/**檔案日期 */
	@CellField(name = "檔案日期")
	private String fileDate;
	
	/**MongoDB返回的檔案id */
	@CellField(name = "檔案id")
	private String fileId;

	public String getFileType() {
		return fileType;
	}

	public void setFileType(String fileType) {
		this.fileType = fileType;
	}

	public String getFileCode() {
		return fileCode;
	}

	public void setFileCode(String fileCode) {
		this.fileCode = fileCode;
	}

	public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	public String getFileUrl() {
		return fileUrl;
	}

	public void setFileUrl(String fileUrl) {
		this.fileUrl = fileUrl;
	}
	
	public String getFileDate() {
		return fileDate;
	}

	public void setFileDate(String fileDate) {
		this.fileDate = fileDate;
	}

	public String getFileId() {
		return fileId;
	}

	public void setFileId(String fileId) {
		this.fileId = fileId;
	}

	@Override
	public String toString() {
		return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE);
	}
	
	
}


4.測試類

 

 

package cn.com.test.ehcache;

import java.util.ArrayList;
import java.util.List;
import cn.com.test.entity.IPOFileInfo;
import cn.com.test.util.ExcelUtils;

/**
* excel工具類測試
* @author:  Rodge
* @time:    2017年12月26日 下午10:22:04
* @version: V1.0.0
*/
public class TestExcelUtils {

	public static void main(String[] args) {
		List<IPOFileInfo> list = new ArrayList<>();
		IPOFileInfo info = new IPOFileInfo();
		info.setFileType("1");
		info.setFileCode("001");
		info.setFileDate("2017-12-26");
		info.setFileId("111");
		info.setFileName("測試文件1");
		info.setFileUrl("www.baidu.com");
		list.add(info);
		
		IPOFileInfo info2 = new IPOFileInfo();
		info2.setFileType("2");
		info2.setFileCode("002");
		info2.setFileDate("2017-12-26");
		info2.setFileId("222");
		info2.setFileName("測試文件2");
		info2.setFileUrl("www.baidu.com");
		list.add(info2);
		
		IPOFileInfo info3 = new IPOFileInfo();
		info3.setFileType("3");
		info3.setFileCode("003");
		info3.setFileDate("2017-12-26");
		info3.setFileId("333");
		info3.setFileName("測試文件3");
		info3.setFileUrl("www.baidu.com");
		list.add(info3);
		
		// 生成excel2007文件
		ExcelUtils.createExcel("D:/excel/2017-12-26.xlsx", list);
		
		// 解析excel文件
		List<IPOFileInfo> results = ExcelUtils.parseExcel("D:/excel/2017-12-26.xlsx", IPOFileInfo.class);
		for (IPOFileInfo ipoFileInfo : results) {
			System.out.println(ipoFileInfo);
		}
	}
}