納稅服務系統【用戶模塊之使用POI導入excel、導出excel】

前言

再次回到咱們的用戶模塊上,咱們發現還有兩個功能沒有完成:javascript

這裏寫圖片描述

對於將網頁中的數據導入或導出到excel文件中,咱們是徹底沒有學習過的。可是呢,在Java中操做excel是相對經常使用的,所以也有組件供咱們使用css

JAVA中操做Excel的有兩種比較主流的工具包java

  • JXL
  • POI

此次咱們主要學習的是POI操做excel。數據庫

JXL有一個缺陷,只能操做03或之前版本的excel,而POI能夠操做97-07版本的。數組

POI基礎

面向對象看excel

首先,要用Java來操做excel的話,確定用對象來指定excel相關的內容的。咱們來看看excel由什麼組成:瀏覽器

這裏寫圖片描述

POI是這樣看的:服務器

  • 整個excel稱做爲工做薄
  • 工做薄下能夠建立不少張表,稱做爲工做表
  • 工做表有很是多的行
  • 行又可細分單元格【指定行的列就能夠定位到工做表任意的位置了】

給咱們一頓分析之後,咱們發現它們之間是有從屬關係的:markdown

  • 工做表從屬於工做薄
  • 行從屬於工做表
  • 單元格從屬於行

這裏寫圖片描述

這裏寫圖片描述


操做Excel步驟

導入POI開發包:app

  • poi-ooxml-3.10.1-20140818.jar,
  • poi-ooxml-schemas-3.10.1-20140818.jar,
  • 以及複製在ooxml-lib目錄下的xmlbeans-2.6.0.jar,dom4j-1.6.1.jar【dom4j通常在項目導入的時候已經存在了】。
  • poi-ooxml-3.11-20141221.jar
/** * 使用POI1無非操做Excel無非就4個步驟: * * 建立/讀取工做薄 * 建立/讀取工做表 * 建立/讀取行 * 建立/讀取單元格 * * * */

建立Excel並寫入數據

@Test
    public void testWrite() throws IOException {

        //建立工做薄
        HSSFWorkbook workbook = new HSSFWorkbook();

        //建立工做表
        HSSFSheet sheet = workbook.createSheet("我是新的工做表");

        //建立行,座標從0開始,我建立的是第三行
        HSSFRow row = sheet.createRow(2);

        //建立單元格,座標也是從0開始,因而就是第三行第三列
        HSSFCell cell = row.createCell(2);

        //往單元格寫數據
        cell.setCellValue("helloWorld");

        //把工做薄寫到硬盤中
        FileOutputStream outputStream = new FileOutputStream("C:\\工做薄.xls");
        workbook.write(outputStream);

        //關閉流
        workbook.close();
        outputStream.close();

    }

這裏寫圖片描述

這裏寫圖片描述


讀取Excel的數據

@Test
    public void testRead() throws IOException {

        //獲取輸入流,讀取Excel數據
        FileInputStream inputStream = new FileInputStream("C:\\工做薄.xls");

        //建立工做薄
        HSSFWorkbook workbook = new HSSFWorkbook(inputStream);

        //獲得工做表
        HSSFSheet sheet = workbook.getSheetAt(0);

        //獲得行
        HSSFRow row = sheet.getRow(2);

        //獲得單元格
        HSSFCell cell = row.getCell(2);

        //獲得單元格的數據
        String cellValue = cell.getStringCellValue();

        System.out.println(cellValue);

    }

這裏寫圖片描述

這裏寫圖片描述


03與07版本

  • 03版本使用的是HSSFWorkbook這麼一個類來操做03的Excel數據
  • 07版本使用的是XSSFWorkbook這麼一個類來操做07的Excel數據

其實他們的方法都是同樣的,僅僅是類的不一樣。而使用哪一個對象,咱們能夠根據後綴名來判斷建立哪一個對象【是03仍是07】dom

@Test
    public void testRead03And07Excel() throws Exception {
        String fileName = "D:\\itcast\\測試.xlsx";
        if(fileName.matches("^.+\\.(?i)((xls)|(xlsx))$")){//判斷是否excel文檔

            boolean is03Excel = fileName.matches("^.+\\.(?i)(xls)$");

            FileInputStream inputStream = new FileInputStream(fileName);

            //一、讀取工做簿
            Workbook workbook = is03Excel ?new HSSFWorkbook(inputStream):new XSSFWorkbook(inputStream);
            //二、讀取第一個工做表
            Sheet sheet = workbook.getSheetAt(0);
            //三、讀取行;讀取第3行
            Row row = sheet.getRow(2);
            //四、讀取單元格;讀取第3行第3列
            Cell cell = row.getCell(2);
            System.out.println("第3行第3列單元格的內容爲:" + cell.getStringCellValue());

            workbook.close();
            inputStream.close();
        }
    }

Excel樣式

回到咱們的需求中,當咱們使用POI導出數據的時候,Excel應該要有樣式纔好看的。相似下面的模板:

這裏寫圖片描述

在POI中能夠利用格式化對象來格式化excel文檔;也即設置excel內容的樣式。

POI中主要的格式化對象經常使用的有:

  • 合併單元格
  • 設置單元格樣式
    • 設置單元格字體
    • 居中
    • 背景顏色等

POI的樣式對象明顯是屬性工做薄的。應用於工做表

這裏寫圖片描述


合併單元格

屬於工做薄,應用於工做表

建立合併單元格對象的時候要給出4個參數,它們分別表示:

  • 行的起始位置
  • 行的結束位置
  • 列的起始位置
  • 列的結束位置
@Test
    public void testCellRange() throws IOException {

        //建立工做薄
        HSSFWorkbook workbook = new HSSFWorkbook();

        //建立合併單元格對象,從第六行開始到第十行,從第六列開始,到第十列
        CellRangeAddress cellRangeAddress = new CellRangeAddress(5, 9, 5, 9);

        //建立工做表
        HSSFSheet sheet = workbook.createSheet("我是新的工做表");

        //應用於工做表
        sheet.addMergedRegion(cellRangeAddress);

        //建立行,座標從0開始,我建立的是第六行
        HSSFRow row = sheet.createRow(5);

        //建立單元格,座標也是從0開始,因而就是第六行第六列
        HSSFCell cell = row.createCell(5);

        //往單元格寫數據
        cell.setCellValue("helloWorld");

        //把工做薄寫到硬盤中
        FileOutputStream outputStream = new FileOutputStream("C:\\工做薄.xls");
        workbook.write(outputStream);

        //關閉流
        workbook.close();
        outputStream.close();

    }

這裏寫圖片描述

設置單元格樣式

上面的圖咱們能夠發現,咱們已經實現了合併單元格,可是通常咱們都是將字體設置成居中、字體大小等等。POI也提供了相對應的對象給咱們實現:

設置居中

樣式屬於工做薄,應用於單元格:

@Test
    public void test() throws IOException {

        //建立工做薄
        HSSFWorkbook workbook = new HSSFWorkbook();

        //建立樣式對象
        HSSFCellStyle style = workbook.createCellStyle();

        //建立合併單元格對象,從第六行開始到第十行,從第六列開始,到第十列
        CellRangeAddress cellRangeAddress = new CellRangeAddress(5, 9, 5, 9);

        //設置水平居中
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        //設置垂直居中
        style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);

        //建立工做表
        HSSFSheet sheet = workbook.createSheet("我是新的工做表");

        sheet.addMergedRegion(cellRangeAddress);
        //建立行,座標從0開始,我建立的是第六行
        HSSFRow row = sheet.createRow(5);

        //建立單元格,座標也是從0開始,因而就是第六行第六列
        HSSFCell cell = row.createCell(5);

        //往單元格寫數據
        cell.setCellValue("helloWorld");

        //設置單元格的樣式
        cell.setCellStyle(style);

        //把工做薄寫到硬盤中
        FileOutputStream outputStream = new FileOutputStream("C:\\工做薄.xls");
        workbook.write(outputStream);

        //關閉流
        workbook.close();
        outputStream.close();

    }

這裏寫圖片描述

設置字體

字體屬於工做薄,應用於樣式【和css是相似的】

@Test
    public void test() throws IOException {

        //建立工做薄
        HSSFWorkbook workbook = new HSSFWorkbook();

        //建立樣式對象
        HSSFCellStyle style = workbook.createCellStyle();

        //建立合併單元格對象,從第六行開始到第十行,從第六列開始,到第十列
        CellRangeAddress cellRangeAddress = new CellRangeAddress(5, 9, 5, 9);

        //設置水平居中
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        //設置垂直居中
        style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);

        //建立font對象
        HSSFFont font = workbook.createFont();

        //設置字體爲粗體
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        //字體爲23字號
        font.setFontHeightInPoints((short) 23);
        //設置字體的顏色
        font.setColor(HSSFFont.COLOR_RED);

        //字體應用於樣式
        style.setFont(font);

        //建立工做表
        HSSFSheet sheet = workbook.createSheet("我是新的工做表");

        sheet.addMergedRegion(cellRangeAddress);
        //建立行,座標從0開始,我建立的是第六行
        HSSFRow row = sheet.createRow(5);

        //建立單元格,座標也是從0開始,因而就是第六行第六列
        HSSFCell cell = row.createCell(5);

        //往單元格寫數據
        cell.setCellValue("helloWorld");

        //設置單元格的樣式
        cell.setCellStyle(style);

        //把工做薄寫到硬盤中
        FileOutputStream outputStream = new FileOutputStream("C:\\工做薄.xls");
        workbook.write(outputStream);

        //關閉流
        workbook.close();
        outputStream.close();

    }

這裏寫圖片描述


實現導出功能

綁定按鈕事件,請求Action處理導出,打開一個輸入框給用戶下載

function doExportExcel() {
            window.open("${basePath}user/user_exportExcel.action");   
        }

Action處理

/************導出Excel*************************/
    public void exportExcel() throws IOException {

        //查找出列表的所有數據
        List<User> list = userServiceImpl.findObjects();

        //導出其實就是讓用戶下載該Excel文件
        HttpServletResponse response = ServletActionContext.getResponse();

        //設置頭和指定名稱
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("列表展現.xls", "UTF-8"));
        //指定返回的類容數據
        response.setContentType("application/x-execl");

        ServletOutputStream outputStream = response.getOutputStream();

        //給Service層作導出Excel操做
        userServiceImpl.exportExcel(list, outputStream);

    }

Service實現

/** * 第一行寫死,字體大小11,居中,粗體,合併單元格 * 第二行寫死,粗體 * 第三行開始,是數據庫列表的數據 */
    @Override
    public void exportExcel(List<User> list, ServletOutputStream outputStream) {

        /***********建立工做薄---樣式---字體--單元格*************/
        HSSFWorkbook workbook = new HSSFWorkbook();

        //第一行的合併單元格
        CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, 0, 4);

        //建立第一行樣式【居中】
        HSSFCellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);

        //建立第二行樣式【居中】
        HSSFCellStyle cellStyle2 = workbook.createCellStyle();
        cellStyle2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        cellStyle2.setAlignment(HSSFCellStyle.ALIGN_CENTER);

        //建立第一行字體
        HSSFFont font = workbook.createFont();
        font.setFontHeightInPoints((short) 23);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

        //建立第二行字體
        HSSFFont font2 = workbook.createFont();
        font2.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

        //字體應用於樣式
        cellStyle.setFont(font);
        cellStyle2.setFont(font2);

        /***********建立工做表*************/
        HSSFSheet sheet = workbook.createSheet("用戶列表");

        //第一行單元格應用於工做表
        sheet.addMergedRegion(cellRangeAddress);

        //設置默認列寬
        sheet.setDefaultColumnWidth(25);

        /***********建立行*************/
        //第一行
        HSSFRow row = sheet.createRow(0);
        HSSFCell cell = row.createCell(0);
        cell.setCellStyle(cellStyle);
        cell.setCellValue("用戶列表");

        //第二行數據也是寫死的,咱們用數組遍歷便可
        String[] data = {"用戶名","賬號", "所屬部門", "性別", "電子郵箱"};
        HSSFRow row1 = sheet.createRow(1);
        for (int i = 0; i < data.length; i++) {
            HSSFCell cell1 = row1.createCell(i);
            cell1.setCellValue(data[i]);

            //加載第二行樣式
            cell1.setCellStyle(cellStyle2);

        }


        /***************行和列在循環的時候,不要重複了。否則會報錯的!!!!*****************/
        //第三行數據就是咱們數據庫保存的數據

        if (list != null) {
            int i=2;
            for (User user : list) {

                //從第三行開始
                HSSFRow row2 = sheet.createRow(i);

                HSSFCell row2Cel0 = row2.createCell(0);
                row2Cel0.setCellValue(user.getName());

                HSSFCell row2Cell = row2.createCell(1);
                row2Cell.setCellValue(user.getAccount());

                HSSFCell row2Cel2 = row2.createCell(2);
                row2Cel2.setCellValue(user.getDept());

                HSSFCell row2Cel3 = row2.createCell(3);
                row2Cel3.setCellValue(user.isGender() ? "男" : "女");

                HSSFCell row2Cel4 = row2.createCell(4);
                row2Cel4.setCellValue(user.getEmail());

                i++;
            }
        }
        try {
            //寫到outputSteam上
            workbook.write(outputStream);

            workbook.close();
            outputStream.close();

        } catch (IOException e) {
            e.printStackTrace();
        }


    }

效果

這裏寫圖片描述


優化一

咱們來看下面這段代碼,他們都要設置居中,字體就除了大小不一樣。其餘都相同。卻佔用了這麼多代碼!!!

//建立第一行樣式【居中】
        HSSFCellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);

        //建立第二行樣式【居中】
        HSSFCellStyle cellStyle2 = workbook.createCellStyle();
        cellStyle2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        cellStyle2.setAlignment(HSSFCellStyle.ALIGN_CENTER);

        //建立第一行字體
        HSSFFont font = workbook.createFont();
        font.setFontHeightInPoints((short) 23);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

        //建立第二行字體
        HSSFFont font2 = workbook.createFont();
        font2.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

        //字體應用於樣式
        cellStyle.setFont(font);
        cellStyle2.setFont(font2);

因而我就抽取成一個方法來獲得樣式

  • 須要工做薄來建立樣式
  • 只有字體的大小是變化的
/** * @param workbook 當前使用工做薄 * @param fontSize 字體大小 * * */
    public HSSFCellStyle createStyle(HSSFWorkbook workbook, short fontSize) {

        HSSFCellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);

        HSSFFont font = workbook.createFont();
        font.setFontHeightInPoints(fontSize);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        cellStyle.setFont(font);

        return cellStyle;
    }

當使用的時候,代碼就變成了這樣調用:

HSSFCellStyle cellStyle = createStyle(workbook, (short) 24);
        HSSFCellStyle cellStyle2 = createStyle(workbook, (short) 13);

優化二

咱們的Service業務層的代碼看起來太多了。這樣咱們維護起來就不方便了。

個人作法是:把代碼抽取成Utils的方法,Service層調用就行了。

這裏寫圖片描述


實現導入功能

如今我有這麼一個Excel文件,要把信息存儲到數據庫中,而且在瀏覽器顯示出來

這裏寫圖片描述

其實導入Excel就是文件上傳,只不過不用把文件保存在服務器的硬盤數據中而是保存在數據庫中,輸出到瀏覽器就好了。

function doImportExcel() {
            document.forms[0].action = "${basePath}user/user_importExcel.action";
            document.forms[0].submit();

        }

Action封裝文件上傳

/*************上傳Excel************************/
    private File userExcel;
    private String userExcelFileName;
    private String userExcelContentType;

    public void setUserExcel(File userExcel) {
        this.userExcel = userExcel;
    }

    public void setUserExcelFileName(String userExcelFileName) {
        this.userExcelFileName = userExcelFileName;
    }

    public void setUserExcelContentType(String userExcelContentType) {
        this.userExcelContentType = userExcelContentType;
    }

Action處理

主要判斷有沒有上傳文件。給Service層處理

/************導入Excel*************************/
    public String importExcel() throws IOException {

        //一、獲取excel文件
        if(userExcel != null){
            //是不是excel
            if(userExcelFileName.matches("^.+\\.(?i)((xls)|(xlsx))$")){
                //二、導入
                userServiceImpl.importExcel(userExcel, userExcelFileName);
            }
        }

        return "list";
    }

Utils封裝成集合返回

public static List<User> importExcel(File userExcel, String userExcelFileName) {

        try {
            FileInputStream fileInputStream = new FileInputStream(userExcel);
            boolean is03Excel = userExcelFileName.matches("^.+\\.(?i)(xls)$");
            //一、讀取工做簿
            Workbook workbook = is03Excel ? new HSSFWorkbook(fileInputStream) : new XSSFWorkbook(fileInputStream);
            //二、讀取工做表
            Sheet sheet = workbook.getSheetAt(0);
            //三、讀取行
            List<User> users = new ArrayList<>();
            if (sheet.getPhysicalNumberOfRows() > 2) {
                User user = null;
                for (int k = 2; k < sheet.getPhysicalNumberOfRows(); k++) {
                    //四、讀取單元格
                    Row row = sheet.getRow(k);
                    user = new User();
                    //用戶名
                    Cell cell0 = row.getCell(0);
                    user.setName(cell0.getStringCellValue());
                    //賬號
                    Cell cell1 = row.getCell(1);
                    user.setAccount(cell1.getStringCellValue());
                    //所屬部門
                    Cell cell2 = row.getCell(2);
                    user.setDept(cell2.getStringCellValue());
                    //性別
                    Cell cell3 = row.getCell(3);
                    user.setGender(cell3.getStringCellValue().equals("男"));
                    //手機號
                    String mobile = "";
                    Cell cell4 = row.getCell(4);
                    try {
                        mobile = cell4.getStringCellValue();
                    } catch (Exception e) {
                        double dMobile = cell4.getNumericCellValue();
                        mobile = BigDecimal.valueOf(dMobile).toString();
                    }
                    user.setMobile(mobile);

                    //電子郵箱
                    Cell cell5 = row.getCell(5);
                    user.setEmail(cell5.getStringCellValue());
                    //生日
                    Cell cell6 = row.getCell(6);
                    if (cell6.getDateCellValue() != null) {
                        user.setBirthday(cell6.getDateCellValue());
                    }
                    //默認用戶密碼爲 123456
                    user.setPassword("123456");
                    //默認用戶狀態爲 有效
                    user.setState(User.USER_STATE_VALID);

                    users.add(user);

                }
            }
            workbook.close();
            fileInputStream.close();
            return users;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

Service調用

public void importExcel(File userExcel, String userExcelFileName) {
        List<User> users = ExcelUtils.importExcel(userExcel, userExcelFileName);
        for (User user : users) {
            save(user);
        }
    }
相關文章
相關標籤/搜索