百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

POI导出Excel

toyiye 2024-05-09 18:41 21 浏览 0 评论

  前言

  数据报表是许多项目都有的模块,一般都是导出Excel或者PDF,这里记录下我在项目里用POI导出Excel。项目中,我需要根据页面jqgrid的机架查询条件导出对应的机架数据,jqgrid是分页的,但导出是要导出所有。

  POI

  Apache POI - the Java API for Microsoft Documents,官网:http://poi.apache.org/

  代码编写

  maven引入POI

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

  或者

<dependency><!--Excel工具类(Easy POI)-->
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-base</artifactId>
    <version>3.2.0</version>
</dependency>
<dependency><!--Excel工具类(Easy POI)-->
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-web</artifactId>
    <version>3.2.0</version>
</dependency>
<dependency><!--Excel工具类(Easy POI)-->
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-annotation</artifactId>
    <version>3.2.0</version>
</dependency>

  html、js调用

<dependency><!--Excel工具类(Easy POI)-->
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-base</artifactId>
    <version>3.2.0</version>
</dependency>
<dependency><!--Excel工具类(Easy POI)-->
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-web</artifactId>
    <version>3.2.0</version>
</dependency>
<dependency><!--Excel工具类(Easy POI)-->
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-annotation</artifactId>
    <version>3.2.0</version>
</dependency>
//导出excel
function exportRackExcel() {
    //获取当前jqGrid分页参数
    var postData = $("#rack").jqGrid("getGridParam", "postData");
    postData.page = 1;
    postData.rows = 999999999;//设置每页9亿条记录(相当于无穷大,查询所有)
    //ajax不支持Excel类型,使用location.href或者表单提交
    //window.location.href,get提交,数据会暴露在URL,相对不安全
    //创建临时的、隐藏的form表单,post提交,数据在请求体里,相对安全
    var $form = $(document.createElement('form')).css({display: 'none'}).attr("method", "POST").attr("action", ctx + "/excel");
    for (var key in postData) {
        var $input = $(document.createElement('input')).attr('name', key).val(postData[key]);
        $form.append($input);
    }
    $("body").append($form);
    $form.submit();
    //过河拆桥,提交完成后remove掉
    $form.remove();
}

  纯js写法

//其他操作,同上
let $form = document.createElement('form');
$form.style.display="none";
$form.method="POST";
$form.action=ctx + "/excel";
for (let key in postData) {
if(postData[key]){
  let $input = document.createElement('input');
  $input.name=key;
  $input.value=postData[key];
  $form.appendChild($input);
}
}
document.body.appendChild($form);
$form.submit();
//过河拆桥,提交完成后remove掉
$form.remove();

  controller

    /**
     * 根据当前jqGrid分页情况,创建并导出Excel文件
     *
     * @param entity 机架实体,用来接收查询条件
     * @return ResponseEntity
     */
    @PostMapping("/excel")
    public ResponseEntity createExcel(RackVo entity) {
        //Excel对应的columnNames列名集合 { key,label }
        String[][] excelMap = {
                {"no", "Rack Code"},
                {"rackName", "Rack Name"},
                {"roomName", "Room"},
                {"idc", "IDC Center"},
                {"clientName", "Customer"},
                {"rackTypeName", "Type"},
                {"existentialMode", "Existential Mode"},
                {"maxPower", "Maximum Power(KVA)"},
                {"status", "Status"},
                {"administrate", "Administrate"},
        };
        return DownloadUtil.download(ExportExcelUtil.createExcel("Rack Management", excelMap, rackService.createExcel(entity).getData()).getData(), "机架数据报表");
    }

  两个工具类:导出Excel工具类 ExportExcelUtil,下载工具类 DownloadUtil

/**
 * java POI 导出Excel表工具类
 */
public class ExportExcelUtil {

    //禁止实例化
    private ExportExcelUtil() {
    }

    /**
     * 只支持一级表头
     *
     * @param titleName   表标题
     * @param columnNames 列名集合,key是用来设置填充数据时对应单元格的值,label就是对应的列名,生成Excel表时,
     *                    第一维数组下标0对应值为Excel表最左边的列的列名 例:{ { key,label },{ key,label } }
     * @param dataLists   数据集合,key对应的是列名集合的key,value是要填充到单元格的值 例:ArrayList<HashMap<String key, String vaule>>
     * @return ResultModel<Workbook>
     */
    public static ResultModel<Workbook> createExcel(String titleName, String[][] columnNames, ArrayList<HashMap<String, String>> dataLists) {
      
        //创建HSSFWorkbook对象(excel的文档对象)
        HSSFWorkbook wb = new HSSFWorkbook();
        //建立新的sheet对象(excel的表单)
        HSSFSheet sheet = wb.createSheet(titleName);//设置表单名

        //1、标题名
        //创建标题行,参数为行索引(excel的行),可以是0~65535之间的任何一个
        HSSFRow row1 = sheet.createRow(0);

        //标题的字体
        HSSFFont font1 = wb.createFont();
        font1.setFontHeightInPoints((short) 12);
        font1.setFontName("黑体");

        //标题的样式
        HSSFCellStyle style1 = wb.createCellStyle();
        style1.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平居中
        style1.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//垂直居中

        // 把字体 应用到当前样式
        style1.setFont(font1);

        //自动换行
        style1.setWrapText(true);

        //自定义填充颜色(天空蓝)
        style1.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        style1.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());

        // 设置边框
        style1.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style1.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style1.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style1.setBorderTop(HSSFCellStyle.BORDER_THIN);

        createCell(row1, 0, style1, titleName);
        //合并单元格CellRangeAddress构造参数依次表示起始行,截至行,起始列, 截至列
        sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, columnNames.length - 1));

        //2、列名
        //创建列名行
        //列名的字体
        HSSFFont font2 = wb.createFont();
        font2.setFontHeightInPoints((short) 12);
        font2.setFontName("新宋体");

        //列名的样式
        HSSFCellStyle style2 = wb.createCellStyle();
        style2.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平居中
        style2.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//垂直居中

        // 把字体 应用到当前样式
        style2.setFont(font2);

        //自动换行
        style2.setWrapText(true);

        //自定义填充颜色(浅蓝色)
        style2.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        style2.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());

        // 设置边框
        style2.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style2.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style2.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style2.setBorderTop(HSSFCellStyle.BORDER_THIN);

        HSSFRow row2 = sheet.createRow(1);
        for (int i = 0; i < columnNames.length; i++) {
            //单元格宽度
            sheet.setColumnWidth(i, 20 * 256);
            createCell(row2, i, style2, columnNames[i][1]);//例:[[key,label],[key,label]] 取label
        }

        //3、填充数据
        //内容的字体
        HSSFFont font3 = wb.createFont();
        font3.setFontHeightInPoints((short) 12);
        font3.setFontName("新宋体");

        //内容的样式
        HSSFCellStyle style3 = wb.createCellStyle();
        style3.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平居中
        style3.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//垂直居中

        // 把字体 应用到当前样式
        style3.setFont(font3);

        //自动换行
        style3.setWrapText(true);

        //默认无填充
        style3.setFillPattern(FillPatternType.NO_FILL);
        style3.setFillForegroundColor(IndexedColors.RED.getIndex());

        // 设置边框
        style3.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style3.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style3.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style3.setBorderTop(HSSFCellStyle.BORDER_THIN);

        int index = 2;//标题行、列名行,所以数据行默认从第三行开始
        for (HashMap<String, String> map : dataLists) {
            //创建内容行
            HSSFRow row3 = sheet.createRow(index);
            for (int i = 0; i < columnNames.length; i++) {
                String val = map.get(columnNames[i][0]);
                createCell(row3, i, style3, val == null ? "" : val);//例:[[key,label],[key,label]] 取key
            }
            index++;
        }

        return ResultModel.of(wb);
    }

    /**
     * 创建一个单元格
     *
     * @param row       行
     * @param column    列
     * @param cellStyle 单元格样式
     * @param text      值
     */
    private static void createCell(Row row, int column, CellStyle cellStyle, String text) {
        Cell cell = row.createCell(column);  // 创建单元格
        cell.setCellValue(text);  // 设置值
        cell.setCellStyle(cellStyle); // 设置单元格样式
    }
}
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 文件下载工具类
 */
public class DownloadUtil{
    /**
     * 快速下载
     */
    public static ResponseEntity download(byte[] fileBytes, String fileName) {
        //设置文件
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", new String(fileName.getBytes(StandardCharsets.UTF_8),StandardCharsets.ISO_8859_1));
        //下载文件
        return new ResponseEntity<>(fileBytes, headers, HttpStatus.CREATED);
    }

    /**
     * 快速下载
     */
    public static ResponseEntity download(File file) {

        return download(getByteArray(file), file.getName());
    }

    /**
     * 快速下载
     */
    public static ResponseEntity download(Workbook workbook, String fileName) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            fileName = fileName + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".xls";
            workbook.write(outputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return download(outputStream.toByteArray(), fileName);
    }

    //获取文件的字节数组
    private static byte[] getByteArray(File file) {
        if (!file.exists()) {
            throw new RuntimeException("File Not Found:" + file.getPath());
        }
        ByteArrayOutputStream bos = new ByteArrayOutputStream((int) file.length());
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(file));
            int buf_size = 1024;
            byte[] buffer = new byte[buf_size];
            int len;
            while (-1 != (len = in.read(buffer, 0, buf_size))) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            try {
                assert in != null;
                in.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //获取文件名后缀
    private static String getSuffix(String fileName) {
        int lastPointIndex = fileName.lastIndexOf(".");
        if (StringUtils.isEmpty(fileName) || lastPointIndex == -1) {
            return null;
        }
        return fileName.substring(lastPointIndex + 1);
    }
}

  获取封装数据的service层 createExcel,直接到取page分页方法,遍历机架数据集合,设置Map<key,value>,add到list<Map>中,最后将封装好的数据return回controller,传入工具类,最后下载。

    /**
     * 根据当前jqGrid分页情况,创建并导出Excel文件
     *
     * @param entity 查询条件
     * @return 封装好的数据集合
     */
    @Override
    public ResultModel<ArrayList<HashMap<String, String>>> createExcel(RackVo entity) {
        ArrayList<HashMap<String, String>> dataLists = new ArrayList<HashMap<String, String>>();
        //直接调page分页方法,获取当前jqGrid分页条件对应的数据集合,
        ResultModel<PageInfo<RackVo>> rm = page(entity);
        if (rm.isFlag()) {
            List<RackVo> rackVoList = rm.getData().getRows();
            for (RackVo rackVo : rackVoList) {
                HashMap<String, String> map = new HashMap<String, String>(16);
                map.put("no", rackVo.getNo() != null ? rackVo.getNo() : "");
                map.put("rackName", rackVo.getName() != null ? rackVo.getName() : "");
                map.put("roomName", rackVo.getRoom() != null ? rackVo.getRoom().getRoomname() : "");
                map.put("idc", rackVo.getOrg() != null ? rackVo.getOrg().getOrgName() : "");
                map.put("clientName", rackVo.getCustomer() != null ? rackVo.getCustomer().getClientname() : "");
                map.put("rackTypeName", rackVo.getRacktype() != null ? rackVo.getRacktype().getName() : "");
                map.put("existentialMode", "1".equals(rackVo.getExistentialMode()) ? "Physical" : "Virtual");
                map.put("maxPower", rackVo.getMaxpower() != null ? rackVo.getMaxpower() : "");
                String status = rackVo.getServiceStatus();
                switch (status != null ? status : "") {
                    case "1":
                        status = "Idle";
                        break;
                    case "2":
                        status = "Reserved";
                        break;
                    case "3":
                        status = "Occupied";
                        break;
                    default:
                        status = "";
                        break;
                }
                map.put("status", status);
                String administrate = rackVo.getAdministrate();
                switch (administrate != null ? administrate : "") {
                    case "R":
                        administrate = "Cust Own";
                        break;
                    case "U":
                        administrate = "CTG Own";
                        break;
                    default:
                        administrate = "";
                        break;
                }
                map.put("administrate", administrate);
                dataLists.add(map);
            }
        }
        return ResultModel.of(dataLists);
    }

  效果

  从开发阶段到测试阶段,导了无数次,没毛病

  小升级

  excelMap,Excel对应的columnNames列名集合 { key,label },可以不用再controller设置了,直接从页面jqgrid抓取,传入controller就行(滑稽脸~)

//获取jqgrid头部标题tr,有多少个tr就有多少级标题
var thead_tr = $(".ui-jqgrid-htable").find("tr.ui-jqgrid-labels");

//遍历thead_tr找出每一个标题,并保存到对象中
var titles = [];
thead_tr.each(function(index_tr,element_tr){
    titles.push([]);
    $(element_tr).find("th").each(function(index_th,element_th){
        //内容
        var label = $(element_th).text();        
        
        //所占行 rowspan 默认1
        var rowspan = $(element_th).attr("rowspan") || 1;
        
        //所占列 colspan 默认1
        var colspan = $(element_th).attr("colspan") || 1;
        
        //键
        var key = $(element_th).attr("id");
        key = key.substring(key.lastIndexOf("_")+1,key.length);
        
        if(label){
            titles[index_tr].push({
                label:label,
                key:key,
                rowspan:rowspan,
                colspan:colspan,
            });
        }
    });
});
//JSON.stringify(titles)
console.log(titles);

  更新

  2020-10-20更新

  直接构造form表单提交,我们不能设置请求头信息,有些需求不能满足(例如在前后端分离的项目中,需要在请求头传递token令牌),当我们导出Excel功能需要设置请求头信息时应该如何操作呢?封装原生Ajax,利用responseType: 'blob'属性,接收二进制数据,构建Blob对象,将二进制数据转成文件,利用a标签下载文件

//封装原生Ajax
var Ajax={
get: function(options) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', options.url, true);
  //设置请求头
  xhr.setRequestHeader("Authorization",  'Bearer ' + store.getters.token);

  xhr.onload = function() {
    let response = null;

    // responseType="" / "text"时,响应的结果从xhr.responseText获取
    if(xhr.responseType === "" || xhr.responseType === "text"){
      response = xhr.responseText;
    }

    //200 请求成功
    if (xhr.status === 200) {
      options.success.call(response);
    }

    //其他情况,请求失败
    if(options.error){
      options.error.call(xhr.error);
    }
  };
  xhr.send();
},
post: function (options) {
  let xhr = new XMLHttpRequest();
  xhr.open("POST", options.url, true);
  //设置请求头
  xhr.setRequestHeader("Content-Type", "application/json");
  xhr.setRequestHeader("Authorization",  'Bearer ' + store.getters.token);

  //设置响应内容类型、超时时间
  options.responseType ? xhr.responseType = options.responseType : xhr.responseType = "text";
  options.timeout ? xhr.timeout = options.timeout : xhr.timeout = 30000;

  xhr.onload  = function() {
    let response = null;

    // responseType="" / "text"时,响应的结果从xhr.responseText获取
    if(xhr.responseType === "" || xhr.responseType === "text"){
      response = xhr.responseText;
    }

    //200 请求成功
    if (xhr.status === 200) {
      options.success.call(response);
    }

    // responseType = "blob"时,响应的是Blob二进制数据,直接调用下载
    if(xhr.status === 201){
      download(xhr,options.success)
    }

    //其他情况,请求失败
    if(options.error){
      options.error.call(xhr.error);
    }
  };
  xhr.send(JSON.stringify(options.data));
}
};

//Blob响应,转成文件下载
function download(response,callback) {
//创建一个隐藏的下载a标签
let url = window.URL.createObjectURL(new Blob([response.response]));
let link = document.createElement("a");
link.style.display = "none";
link.href = url;

//设置文件名,文件名从响应头中获取(PS:可能会存在中文乱码、文件后缀多个下划线等问题)
let fileName = response.getAllResponseHeaders().split("\n")[4].split(":")[1].split(";")[2].split("=")[1].replace(/"/g,"");
fileName = decodeURIComponent(escape(fileName));
console.log("文件名:" + fileName);
link.setAttribute("download", fileName);

document.body.appendChild(link);
link.click();

//过河拆桥
link.remove();

if(callback){
  callback();
}
}

  使用

//获取当前分页参数
let postData = vue.getPageParameter();
postData.page = 1;
postData.pageSize = 999999999;//设置每页9亿条记录(相当于无穷大,查询所有)

console.log("开始导出...");
Ajax.post({
  url:vue.excelUrl,
  data:postData,
  timeout: 30000,
  responseType: 'blob',
  success:function () {
    console.log("导出完成,请您注意浏览器的下载管理器!");
  }
});

  效果

  后缀多了个下划线,很奇怪...,删除下划线文件能正常打开,数据、单元格背景等正常

版权声明

作者:huanzi-qch

出处:https://www.cnblogs.com/huanzi-qch

若标题中有“转载”字样,则本文版权归原作者所有。若无转载字样,本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利.

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码