zoukankan      html  css  js  c++  java
  • spring boot使用AbstractXlsView导出excel

    一、maven依赖jar包

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

    二、导出view

    public class ExportMemberVo {
        private String name;
    
        private Integer gender;
    
        private String idCard;
    
        private String bankNo;
    
        private String bankName;
    
        private String phone;
    
        /**
         * 性别处理
         */
        public String getGender() {
            return gender == 0 ? "男" : "女";
        }
        /****为了节省篇幅,省略setter/getter/constructor****/
    }
    
    

    三、导出Excel核心处理代码,继承自AbstractXlsView ,并实现buildExcelDocument

    import export.entity.ExportMemberVo;
    import org.apache.poi.hssf.util.HSSFColor;
    import org.apache.poi.ss.usermodel.*;
    import org.springframework.web.servlet.view.document.AbstractXlsView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.net.URLEncoder;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @Author Kent.Wang
     * @Date 2017/6/26
     */
    public abstract class ExcelView extends AbstractXlsView {
    
        @Override
        protected void buildExcelDocument(Map<String, Object> map,
                                          Workbook workbook,
                                          HttpServletRequest request,
                                          HttpServletResponse response) throws Exception {
            String excelName = map.get("name").toString() + ".xls";
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(excelName,"utf-8"));
            response.setContentType("application/ms-excel; charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            @SuppressWarnings("unchecked")
            List<ExportMemberVo> list = (List<ExportMemberVo>) map.get("members");
            Sheet sheet = workbook.createSheet("User Detail");
            sheet.setDefaultColumnWidth(30);
            CellStyle style = workbook.createCellStyle();
            Font font = workbook.createFont();
            font.setFontName("Arial");
            style.setFillForegroundColor(HSSFColor.BLUE.index);
            style.setFillPattern((short) 1);
            font.setBold(true);
            font.setColor(HSSFColor.WHITE.index);
            style.setFont(font);
            Row header = sheet.createRow(0);
            header.createCell(0).setCellValue("姓名");
            header.getCell(0).setCellStyle(style);
            header.createCell(1).setCellValue("性别");
            header.getCell(1).setCellStyle(style);
            header.createCell(2).setCellValue("手机号");
            header.getCell(2).setCellStyle(style);
            header.createCell(3).setCellValue("身份证号");
            header.getCell(3).setCellStyle(style);
            header.createCell(4).setCellValue("银行卡号");
            header.getCell(4).setCellStyle(style);
            int rowCount = 1;
            for (ExportMemberVo user : list) {
                Row userRow = sheet.createRow(rowCount++);
                userRow.createCell(0).setCellValue(user.getName());
                userRow.createCell(1).setCellValue(user.getGender());
                userRow.createCell(2).setCellValue(user.getPhone());
                userRow.createCell(3).setCellValue(user.getIdCard());
                userRow.createCell(4).setCellValue(user.getBankNo());
            }
        }
    }

    四、controller代码

     @RequestMapping(value = "", method = RequestMethod.GET)
      public ModelAndView download() {
    
          List<ExportMemberVo> list = new ArrayList<ExportMemberVo>();
          for (int i = 0; i < 5; i++) {
              ExportMemberVo exportMemberVo = new ExportMemberVo();
              exportMemberVo.setName("Kent" + i);
              @SuppressWarnings("unchecked")
              int gender = ThreadLocalRandom.current().nextInt(0, 2);
              exportMemberVo.setGender(gender);
              exportMemberVo.setPhone("182xxxxxxxx");
              exportMemberVo.setBankName("建设银行");
              list.add(exportMemberVo);
          }
    
          Map<String, Object> map = new HashMap<String, Object>();
          map.put("members", list);
          map.put("name", "魅力城市");
          ExcelView excelView = new UserInfoExcelView();
          return new ModelAndView(excelView, map);
      }

    运行,访问download结果如下: 
    这里写图片描述

    这是网络上一般的导出方法,没什么特别的,拿来即用,在使用的过程中也碰到一些问题,和疑问,下面谈谈我自己的理解。

    五、火狐浏览器导出excel中文乱码问题

     这个问题是因浏览器的不同所造成的,那只要对response.setHeader做些处理就可以了。我只测试了chrome和firefox,其他浏览器或许还有些差异,在此不一一枚举。

    String Agent = request.getHeader("User-Agent");
    if (null != Agent) {
        Agent = Agent.toLowerCase();
        if (Agent.indexOf("firefox") != -1) {
            response.setHeader("content-disposition", String.format("attachment;filename*=utf-8'zh_cn'%s", URLEncoder.encode(excelName, "utf-8")));
    
        } else {
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(excelName, "utf-8"));
        }
    }

    六、buildExcelDocument是怎么被调用的

     我们看到继承自AbstractXlsView 的buildExcelDocument方法是protected的,只能被同一包下面和子类调用,理论上controller不会去继承ExcelView ,也不会在同一包下,那我们如何去调用他,在springMVC和spring boot下,我们需要用到ModelAndView。 
    我通过两幅图来列举下buildExcelDocument调用的过程:

    download方法执行return之后会大致执行如下过程,我们可以看到buildExcelDocument是如何被调用的,具体过程有兴趣可以自己debug。

    这里写图片描述

    其中比较重要的一个环节是view.render,这里用到了Java的多态特性,AbstractView是继承自View的,ExcelView自然也继承View,所以获取到的ModelAndView中的view执行view.render实际上会去调用AbstractView的render方法,然后AbstractView中有个抽象方法renderMergeOutputModel,供子类实现不同的输出模型,输出Excel文件就是其中的一个子类,还有输出PDF文件也是同理实现该方法。

    具体handle方法返回ModelAndView的过程如下: 
    这里写图片描述

    七、包装一下Excel输出模版

     我们可能需要输出许多Excel,有成员信息,商品信息,规格信息等等。所以将所有设置Excel的代码全放在ExcelView类中有点不合时宜,很显然至少列名和值的绑定都写死了,难以扩展。

    仿照AbstractView中用到的模板方法模式,我们也可以将具体设置Sheet提取出来,写个抽象方法,由子类去实现具体设置Sheet.

    修改过的ExcelView如下:

    public CellStyle cellStyle;
    
    /**
     * 设置样式
     *
     * @param workbook
     */
    protected abstract void setStyle(Workbook workbook);
    
    /**
     * 设置Row,由子类实现
     *
     * @param sheet
     * @param map
     */
    protected abstract void setRow(Sheet sheet, Map<String, Object> map);
    
    @Override
    protected void buildExcelDocument(Map<String, Object> map,
                                      Workbook workbook,
                                      HttpServletRequest request,
                                      HttpServletResponse response) throws Exception {
        String excelName = map.get("name").toString() + ".xls";
        String Agent = request.getHeader("User-Agent");
        if (null != Agent) {
            Agent = Agent.toLowerCase();
            if (Agent.indexOf("firefox") != -1) {
                response.setHeader("content-disposition", String.format("attachment;filename*=utf-8'zh_cn'%s", URLEncoder.encode(excelName, "utf-8")));
    
            } else {
                response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(excelName, "utf-8"));
            }
        }
        response.setContentType("application/ms-excel; charset=UTF-8");
        Sheet sheet = workbook.createSheet("User Detail");
        sheet.setDefaultColumnWidth(30);
        this.setStyle(workbook);
        setRow(sheet, map);
    }

     我们写个导出成员信息UserInfoExcelView ,所有Sheet设置都在这里完成:

    public class UserInfoExcelView extends ExcelView {
    
    
        @Override
        public void setRow(Sheet sheet, Map<String, Object> map) {
    
            // create header row
            Row header = sheet.createRow(0);
            header.createCell(0).setCellValue("姓名");
            header.getCell(0).setCellStyle(super.cellStyle);
            header.createCell(1).setCellValue("性别");
            header.getCell(1).setCellStyle(super.cellStyle);
            header.createCell(2).setCellValue("手机号");
            header.getCell(2).setCellStyle(super.cellStyle);
            header.createCell(3).setCellValue("身份证号");
            header.getCell(3).setCellStyle(super.cellStyle);
            header.createCell(4).setCellValue("银行卡号");
            header.getCell(4).setCellStyle(super.cellStyle);
    
            @SuppressWarnings("unchecked")
            List<ExportMemberVo> list = (List<ExportMemberVo>) map.get("members");
            int rowCount = 1;
            for (ExportMemberVo user : list) {
                Row userRow = sheet.createRow(rowCount++);
                userRow.createCell(0).setCellValue(user.getName());
                userRow.createCell(1).setCellValue(user.getGender());
                userRow.createCell(2).setCellValue(user.getPhone());
                userRow.createCell(3).setCellValue(user.getIdCard());
                userRow.createCell(4).setCellValue(user.getBankNo());
            }
        }
    
        @Override
        protected void setStyle(Workbook workbook) {
            DefaultCellStyle defaultCellStyle = new DefaultCellStyleImpl();
            super.cellStyle = defaultCellStyle.setCellStyle(workbook);
        }
    
    }

    如果还需要导出其他excel,同样继承下ExcelView并实现setRow和setStyle就可以了。

    由于样式可能会设置通用的,但又有扩展的可能性,所以可以实现一个默认样式接口。

    DefaultCellStyle.java

    public interface DefaultCellStyle {
        CellStyle setCellStyle(Workbook workbook);
    }

    DefaultCellStyleImpl.java

    public class DefaultCellStyleImpl implements DefaultCellStyle {
        @Override
        public CellStyle setCellStyle(Workbook workbook) {
            // create style for header cells
            CellStyle cellStyle = workbook.createCellStyle();
            Font font = workbook.createFont();
            font.setFontName("Arial");
            cellStyle.setFillForegroundColor(HSSFColor.BLUE.index);
            cellStyle.setFillPattern((short) 1);
            font.setBold(true);
            font.setColor(HSSFColor.WHITE.index);
            cellStyle.setFont(font);
            return cellStyle;
        }
    }

    https://blog.csdn.net/wang124454731/article/details/73850645

  • 相关阅读:
    Socket编程实践(6) --TCPNotes服务器
    2014百度之星预赛(第二场)——Best Financing
    操作和维护经常使用的命令
    HDU 5073 Galaxy(Anshan 2014)(数学推导,贪婪)
    [Angular2] @Ngrx/store and @Ngrx/effects learning note
    [AngularJS Ng-redux] Integrate ngRedux
    [AngularFire2] Update multi collections at the same time with FirebaseRef
    [AngularJS] Write a simple Redux store in AngularJS app
    [AngularJS] Using an AngularJS directive to hide the keyboard on submit
    [Javascript] Validate Data with the Every() Method
  • 原文地址:https://www.cnblogs.com/softidea/p/9363963.html
Copyright © 2011-2022 走看看