1. 程式人生 > 實用技巧 >利用POI實現電子表格匯出/匯入操作

利用POI實現電子表格匯出/匯入操作

利用POI實現電子表格匯出/匯入操作

1. 需要的依賴

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>

2. 工具類

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: 楓葉
 * Date: 2020/12/22
 * Description:  excel讀寫工具類,需要poi-4.0.1.jar、poi-ooxml-4.0.1.jar、poi-ooxml-schemas-4.0.1.jar、xmlbeans-3.0.2.jar四個核心jar包
 * 還需要commons-collections4-4.2.jar和commons-compress-1.18.jar兩個輔助包
 * Version: V1.0
 */
@SuppressWarnings("all")
public class POIUtil {

    /**
     * 根據fileType不同讀取excel檔案
     *
     * @param path 請求硬碟物理路徑中的一個excel檔案可以是xls,也可以是xlsx檔案
     * @return 返回資料列表
     */
    public static List<List<String>> readExcelForPOI(InputStream ins, String fileType) throws IOException {
        List<List<String>> lists = new ArrayList<>();
        Workbook wb = null;
        if ("xls".equals(fileType)) {                  // 判斷是2003版本還是2007之後的版本,xls為2003版本,xlsx為2007版本
            wb = new HSSFWorkbook(ins);                 // HSSFWorkbook型別對應2003版本
        } else if ("xlsx".equals(fileType)) {
            wb = new XSSFWorkbook(ins);                 // XSSFWorkbook型別對應2007之後版本
        } else {
            return null;
        }
        Sheet sheet = wb.getSheetAt(0);             //假設讀取第一個工作頁sheet,index預設從0開始,如果存在多個sheet,那麼需要迴圈Sheet判斷
        for (Row row : sheet) {                       //迴圈讀取第一個工作頁中的每一行記錄
            ArrayList<String> list = new ArrayList<>();
            for (Cell cell : row) {                   // 迴圈讀取一行中的每一列資料
                cell.setCellType(CellType.STRING);    // 根據不同型別轉化成字串
                list.add(cell.getStringCellValue());   // 獲取當前列中的資料
            }
            lists.add(list);
        }
        return lists;
    }

    public static List<List<String>> readExcelForPOI(String path) {
        String fileType = path.substring(path.lastIndexOf(".") + 1);   // 獲取檔案的字尾名
        List<List<String>> lists = null;          // 裡面的list代表每一行資料,外面list代表所有行資料,實際專案中,需要把excel中的每一行資料做成POJO物件處理
        InputStream is = null;                                             // 生成輸入流
        try {
            lists = readExcelForPOI(is, fileType);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return lists;
    }

    /**
     * 建立Excel.xls   返回一個Workbook物件,外部呼叫方式:首先建立一個OutputStream物件,然後通過workbook.write(out);完成輸出
     *
     * @param lists 需要寫入excel的資料
     * @param name  檔名
     * @return 返回一個excel poi Workbook物件,如果沒有解析成功,或者沒有傳入資料列表,則會返回null
     * @throws IOException 如果IO失敗會丟擲異常
     */
    public static Workbook creatExcelForPOI(List<List<String>> lists, String name) throws IOException {
        /*
        操作excel五個核心  1.Font字型   2.CellStyle樣式單   3.Sheet頁   4.Row行   5.Cell列
        企業使用的是DOM4J、SAX完成xml解析,POI完成excel解析
        DOM4J:(50M大小以下)解析機制,首先完整讀取xml,把xml所有的內容放入了電腦記憶體中,操作效能極高,而且在使用讀取規則更方便
        SAX:解析機制,基於xml每一個節點位置來迴圈讀取載入
        POI:解析機制,類似於SAX,並且可以和DOM4J、SAX結合完成讀寫操作
         */
        Workbook wb = new HSSFWorkbook();                        //  建立2003 excel物件 HSSFWorkbook型別對應2003版本    XSSFWorkbook型別對應2007之後版本
        Sheet sheet = wb.createSheet(name);                      // 建立第一個sheet(頁),並命名,注意這裡只建立一頁,如果業務需求,可以新增多頁

        Font f = wb.createFont();                                // 建立字型
        f.setFontHeightInPoints((short) 10);                     // 建立字型樣式:字型大小
        f.setColor(IndexedColors.BLACK.getIndex());              // 建立字型樣式:字型型別(這裡設定的是黑體)
        f.setBold(true);                                         // 建立字型樣式:粗體

        CellStyle cs = wb.createCellStyle();                     // 建立單元格每列格式物件
        cs.setFont(f);                                           // 把字型樣式儲存到樣式單中
        cs.setBorderLeft(BorderStyle.THIN);                      // 設定具有邊框的效果:左邊框
        cs.setBorderRight(BorderStyle.THIN);                     // 設定具有邊框的效果:右邊框
        cs.setBorderTop(BorderStyle.THIN);                       // 設定具有邊框的效果:上邊框
        cs.setBorderBottom(BorderStyle.THIN);                    // 設定具有邊框的效果:下邊框
        cs.setAlignment(HorizontalAlignment.CENTER);             // 設定文字居中的效果

        if (lists == null || lists.size() == 0) {                 // 如果沒有傳遞資料列表,則直接返回null
            return null;
        }

        for (int i = 0; i < lists.size(); i++) {                 // 設定每行每列的值  Row 行,Cell 方格 , Row 和 Cell 都是從0開始計數的
            Row row01 = sheet.createRow(i);                      // 在當前sheet頁上建立一個新行
            List<String> listInner = lists.get(i);
            for (int j = 0; j < listInner.size(); j++) {
                sheet.setColumnWidth(j, 256 * 35);            // 設定列寬。第一個引數表示要為第幾列,第二個引數表示列的寬度,值為畫素值。
                Cell cell01 = row01.createCell(j);               // 在row行上建立一列
                cell01.setCellValue(listInner.get(j));         // 在此列上寫入資料
                cell01.setCellStyle(cs);                         // 在此列上新增樣式
            }
        }
        return wb;
    }
}

3. 匯出資料庫中的記錄

這裡使用servlet演示,ssm和boot需將程式碼遷移到controller中

/**
 * @author 楓葉
 * @version 1.0
 * @date 2020/12/23
 */
@WebServlet(value = {
        "/download/yongHuList"
},
        name = "download")
public class DownLoadServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        YongHuService yongHuService = new YongHuServiceImpl();
        //獲取使用者列表
        List<YongHu> yongHuList = yongHuService.selectYhList();
        List<List<String>> rowList = new ArrayList<>(yongHuList.size()+1);

        List<String> title = new ArrayList<>(1);
        title.add("使用者名稱");
        title.add("密碼");
        title.add("是否鎖定");
        title.add("部門");
        title.add("許可權");

        rowList.add(title);
        yongHuList.forEach(yongHu -> {
            List<String> row = new ArrayList<>(5);
            row.add(yongHu.getYongHuMing());
            row.add(yongHu.getMiMa());
            row.add(yongHu.getSuoDing() == 0 ? "否" : "是");
            row.add(yongHu.getBuMen().getBmMing());
            row.add(yongHu.getQuanXian().getQuanXianMing());
            rowList.add(row);
        });

        Workbook workbook = POIUtil.creatExcelForPOI(rowList, "使用者");

        if (workbook != null) {
            resp.setCharacterEncoding("UTF-8");
            resp.setHeader("Content-Disposition", "attachment; filename=" + System.currentTimeMillis() + ".xls");
            //獲取響應報文輸出流物件
            ServletOutputStream out = resp.getOutputStream();
            //輸出
            workbook.write(out);
            out.flush();
            out.close();
        } else {
            resp.setContentType("text/html;charset=utf-8");
            resp.getWriter().println(DataResponse.error("系統錯誤,或excel檔案格式錯誤。"));
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}

4. 上傳excel檔案並解析資料存到資料庫

/**
 * @author 楓葉
 * @version 1.0
 * @date 2020/12/23
 */
@WebServlet(value = {
        "/upload/yongHuList"
},
        name = "upload")
public class UpLoadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        YongHuService yongHuService = new YongHuServiceImpl();
        BuMenService buMenService = new BuMenServiceImpl();
        QuanXianService quanXianService = new QuanXianServiceImpl();

        List<BuMen> buMenList = buMenService.chaXunBuMenList();
        List<QuanXian> quanXianList = quanXianService.selectQuanXianList();

        // 設定請求物件語言編碼為UTF-8
        req.setCharacterEncoding("UTF-8");
        //固定寫法
        boolean isMultipart = ServletFileUpload.isMultipartContent(req);
        if (isMultipart) {
            // 建立工廠(這裡用的是工廠模式)
            DiskFileItemFactory factory = new DiskFileItemFactory();
            // 獲取ServletContext
            ServletContext servletContext = req.getSession().getServletContext();
            // 獲取從ServletContext中得到上傳來的資料,fileupload固定的引數:javax.servlet.context.tempdir
            File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
            // fileupload封裝上傳來的檔案的各種屬性(上傳來的檔案的大小、檔名等)
            factory.setRepository(repository);
            // fileupload生成對應的資料引數物件
            ServletFileUpload upload = new ServletFileUpload(factory);
            // 把request轉成fileupload中FileItem的例項
            List<FileItem> items = upload.parseRequest(req);
            System.out.println(items);

            FileItem item = items.get(0);
            String fieldName = item.getName();
            System.out.println("檔案:"+fieldName+"讀取成功");
            int lastIndex = fieldName.lastIndexOf(".");
            String houZhui = fieldName.substring(lastIndex + 1);
            System.out.println(houZhui);
            if (!("xls".equals(houZhui) || "xlsx".equals(houZhui))) {
                resp.getWriter().println(DataResponse.error("只能傳Excel檔案"));
                return;
            }
            InputStream fis = item.getInputStream();
            List<List<String>> lists = POIUtil.readExcelForPOI(fis, houZhui);
            if (lists == null || lists.size() == 0) {
                resp.getWriter().println(DataResponse.error("檔案中沒有資料。"));
                return;
            }
            List<YongHu> yongHuList = new ArrayList<>(lists.size() - 1);
            for (int i = 1; i < lists.size(); i++) {
                YongHu yongHu = new YongHu();
                List<String> row = lists.get(i);
                String col1 = row.get(0);
                String col2 = row.get(1);
                String col3 = row.get(2);
                String col4 = row.get(3);
                String col5 = row.get(4);
                yongHu.setYongHuMing(col1);
                yongHu.setMiMa(col2);
                short suoDing = (short) ("是".equals(col3) ? 1 : 0);
                yongHu.setSuoDing(suoDing);

                //將部門名對映成部門id
                int bmId = 0;
                for (BuMen buMen : buMenList) {
                    if (buMen.getBmMing().equals(col5)) {
                        bmId = buMen.getBid();
                        break;
                    }
                }
                yongHu.setBmId(bmId);
                //將許可權名對映成許可權id
                int qxId = 0;
                for (QuanXian quanXian : quanXianList) {
                    if (quanXian.getQuanXianMing().equals(col5)){
                        qxId=quanXian.getQxId();
                        break;
                    }
                }
                yongHu.setQxId(qxId);
                yongHuList.add(yongHu);
            }
            //資料存入資料庫
            if (yongHuService.addYhList(yongHuList)) {
                resp.getWriter().println(DataResponse.ok("成功匯入" + yongHuList.size() + "條資料"));
                return;
            }
            resp.getWriter().println(DataResponse.error("匯入失敗,請嚴格按照檔案模板的格式寫入EXCEL。"));
        }
    }
}

5. 上面用到的返回封裝類DataResponse如下

public class DataResponse{
    private final Map<String,Object> result;

    @Override
    public String toString() {
            //這裡也用到了fastJSON並且設定了日期的格式化格式
        JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd hh:mm:ss";
        return JSON.toJSONString(result,SerializerFeature.WriteDateUseDateFormat);
    }

    public DataResponse(Integer code,String msg,Object data){
        result = new HashMap<>();
        result.put("code",code);
        result.put("msg",msg);
        result.put("data",data);
    }

    public DataResponse(Integer code,String msg){
        this(code,msg,null);
    }

    public DataResponse() {
        result=new HashMap<>();
    }

    public DataResponse put(String key,Object value){
        result.put(key,value);
        return this;
    }

    public static DataResponse ok(){
        return new DataResponse(200,"成功!");
    }

    public static DataResponse ok(String msg){
        return new DataResponse(200,msg);
    }

    public static DataResponse ok(int code,String msg){
        return new DataResponse(code,msg);
    }
    public static DataResponse ok(String msg,Object data){
        return new DataResponse(200,msg,data);
    }
    public static DataResponse ok(int code,String msg,Object data){
        return new DataResponse(200,msg,data);
    }

    public static DataResponse error(){
        return new DataResponse(500,"伺服器錯誤,操作失敗!");
    }

    public static DataResponse error(String msg){
        return new DataResponse(500,msg);
    }

    public static DataResponse error(int code,String msg){
        return new DataResponse(code,msg);
    }

    public Object get(String key){
        return result.get(key);
    }

    public Object getData(){
        return result.get("data");
    }

    public void setCode(int code) {
        result.put("code",code);
    }

    public void setMsg(String msg) {
        result.put("msg",msg);
    }

    public void setData(Object data) {
        result.put("data",data);
    }

}