开发者社区> SH的全栈笔记> 正文

想在Java中实现Excel和Csv的导出吗?看这就对了

简介: 前言 最近在项目中遇到一个需求,需要后端提供一个下载Csv和Excel表格的接口。这个接口接收前端的查询参数,针对这些参数对数据库做查询操作。将查询到的结果生成Excel和Csv文件,再以字节流的形式返回给前端。
+关注继续查看

前言

最近在项目中遇到一个需求,需要后端提供一个下载Csv和Excel表格的接口。这个接口接收前端的查询参数,针对这些参数对数据库做查询操作。将查询到的结果生成Excel和Csv文件,再以字节流的形式返回给前端。

前端拿到这个流文件之后,最开始用ajax来接收,但是前端发送的请求却被浏览器cancel掉了。后来发现,发展了如此之久的Ajax居然不支持流文件下载。后来前端换成了最原始的XMLHttpRequest,才修复了这个问题。

首先给出项目源码的地址。这是源码,欢迎大家star或者提MR。

Csv

新建controller

先来一个简单的例子。首先在controller中新建这样一个接口。

@GetMapping("csv")
public void csv(
        HttpServletRequest request,
        HttpServletResponse response
) throws IOException {
    String fileName = this.getFileName(request, "测试数据.csv");
    response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");

    LinkedHashMap<String, Object> header = new LinkedHashMap<>();
    LinkedHashMap<String, Object> body = new LinkedHashMap<>();
    header.put("1", "姓名");
    header.put("2", "年龄");
    List<LinkedHashMap<String, Object>> data = new ArrayList<>();
    body.put("1", "小明");
    body.put("2", "小王");
    data.add(header);
    data.add(body);
    data.add(body);
    data.add(body);
    FileCopyUtils.copy(ExportUtil.exportCSV(data), response.getOutputStream());
}

其中this.getFileName(request, "测试数据.csv")函数是用来获取导出文件名的函数。单独提出来是因为不同浏览器使用的默认的编码不同。例如,如果使用默认的UTF-8编码。在chrome浏览器中下载会出现中文乱码。代码如下。

private String getFileName(HttpServletRequest request, String name) throws UnsupportedEncodingException {
    String userAgent = request.getHeader("USER-AGENT");
    return userAgent.contains("Mozilla") ? new String(name.getBytes(), "ISO8859-1") : name;
}

response.getOutputStream()则是用于创建字节输出流,在导出csv文件的controller代码结尾,通过工具类中的复制文件函数将字节流写入到输出流中,从而将csv文件以字节流的形式返回给客户端。

当前端通过http请求访问服务器接口的时候,http中的所有的请求信息都会封装在HttpServletRequest对象中。例如,你可以通过这个对象获取到请求的URL地址,请求的方式,请求的客户端IP和完整主机名,Web服务器的IP和完整主机名,请求行中的参数,获取请求头的参数等等。

针对每一次的HTTP请求,服务器会自动创建一个HttpServletResponse对象和请求对象相对应。响应对象可以对当前的请求进行重定向,自定义响应体的头部,设置返回流等等。

新建导出工具类

我们新建一个导出工具类,来专门负责导出各种格式的文件。代码如下。

public class ExportUtil {

    public static byte[] exportCSV(List<LinkedHashMap<String, Object>> exportData) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        BufferedWriter buffCvsWriter = null;
        try {
            buffCvsWriter = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
            // 将body数据写入表格
            for (Iterator<LinkedHashMap<String, Object>> iterator = exportData.iterator(); iterator.hasNext(); ) {
                fillDataToCsv(buffCvsWriter, iterator.next());
                if (iterator.hasNext()) {
                    buffCvsWriter.newLine();
                }
            }
            // 刷新缓冲
            buffCvsWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            if (buffCvsWriter != null) {
                try {
                    buffCvsWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return out.toByteArray();
    }

    private static void fillDataToCsv(BufferedWriter buffCvsWriter, LinkedHashMap row) throws IOException {
        Map.Entry propertyEntry;
        for (Iterator<Map.Entry> propertyIterator = row.entrySet().iterator(); propertyIterator.hasNext(); ) {
            propertyEntry = propertyIterator.next();
            buffCvsWriter.write("\"" + propertyEntry.getValue().toString() + "\"");
            if (propertyIterator.hasNext()) {
                buffCvsWriter.write(",");
            }
        }
    }
}

fillDataToCsv主要是抽离出来为csv填充一行一行的数据的。

运行

然后运行项目,调用http://localhost:8080/csv,就可以下载示例的csv文件。示例如下。

image

Excel

新建controller

新建下载xlsx文件的接口。

@GetMapping("xlsx")
public void xlsx(
        HttpServletRequest request,
        HttpServletResponse response
) throws IOException {
    String fileName = this.getFileName(request, "测试数据.xlsx");
    response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");

    List<LinkedHashMap<String, Object>> datas = new ArrayList<>();
    LinkedHashMap<String, Object> data = new LinkedHashMap<>();
    data.put("1", "姓名");
    data.put("2", "年龄");
    datas.add(data);
    for (int i = 0; i < 5; i++) {
        data = new LinkedHashMap<>();
        data.put("1", "小青");
        data.put("2", "小白");
        datas.add(data);
    }

    Map<String, List<LinkedHashMap<String, Object>>> tableData = new HashMap<>();
    tableData.put("日报表", datas);
    tableData.put("周报表", datas);
    tableData.put("月报表", datas);

    FileCopyUtils.copy(ExportUtil.exportXlsx(tableData), response.getOutputStream());
}

补充工具类

上面新建的导出工具类中,只有导出csv的函数,接下来我们要添加导出xlsx的函数。

public static byte[] exportXlsx(Map<String, List<LinkedHashMap<String, Object>>> tableData) {
    ByteArrayOutputStream out = new ByteArrayOutputStream();

    try {
        HSSFWorkbook workbook = new HSSFWorkbook();
        // 创建多个sheet
        for (Map.Entry<String, List<LinkedHashMap<String, Object>>> entry : tableData.entrySet()) {
            fillDataToXlsx(workbook.createSheet(entry.getKey()), entry.getValue());
        }

        workbook.write(out);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return out.toByteArray();
}

/**
 * 将linkedHashMap中的数据,写入xlsx表格中
 *
 * @param sheet
 * @param data
 */
private static void fillDataToXlsx(HSSFSheet sheet, List<LinkedHashMap<String, Object>> data) {
    HSSFRow currRow;
    HSSFCell cell;
    LinkedHashMap row;
    Map.Entry propertyEntry;
    int rowIndex = 0;
    int cellIndex = 0;
    for (Iterator<LinkedHashMap<String, Object>> iterator = data.iterator(); iterator.hasNext(); ) {
        row = iterator.next();
        currRow = sheet.createRow(rowIndex++);
        for (Iterator<Map.Entry> propertyIterator = row.entrySet().iterator(); propertyIterator.hasNext(); ) {
            propertyEntry = propertyIterator.next();
            if (propertyIterator.hasNext()) {
                String value = String.valueOf(propertyEntry.getValue());
                cell = currRow.createCell(cellIndex++);
                cell.setCellValue(value);
            } else {
                String value = String.valueOf(propertyEntry.getValue());
                cell = currRow.createCell(cellIndex++);
                cell.setCellValue(value);
                break;
            }
        }
        if (iterator.hasNext()) {
            cellIndex = 0;
        }
    }
}

fillDataToXlsx的用途与csv一样,为xlsx文件的每一行刷上数据。

运行

然后运行项目,调用http://localhost:8080/xlsx,就可以下载示例的csv文件。示例如下。

image

项目地址

最后再次给出项目地址,大家如果没有理解到其中的一些地方,不妨把项目clone下来,自己亲自操作一波。

参考

这是在解决请求被浏览器cancel掉的过程中,很重要的一个参考,分享给大家。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
JAVA导出项目中的所有源码到同一个文本文件中
JAVA导出项目中的所有源码到同一个文本文件中
19 0
开源SPL助力JAVA处理公共数据文件(txt/csv/json/xml/xsl)
开源SPL助力JAVA处理公共数据文件(txt/csv/json/xml/xsl)
2088 0
Java POI导出excel经典实现-交叉报表斜表头
Java POI导出excel经典实现-交叉报表斜表头
65 0
java解析CSV文件三种方法(openCSV)
java解析CSV文件三种方法(openCSV)
90 0
Error:java: 不允许在使用 --release 时从系统模块 java.xml 导出程序包:
Error:java: 不允许在使用 --release 时从系统模块 java.xml 导出程序包:
42 0
IntelliJ IDEA 导出 Java 可执行 Jar 包
IntelliJ IDEA 导出 Java 可执行 Jar 包
112 0
java导出excel:Could not initialize class sun.awt.X11GraphicsEnvironment
java导出excel:Could not initialize class sun.awt.X11GraphicsEnvironment
49 0
java导出excel合并单元格
java导出excel合并单元格
306 0
Java Swing之数据库表结构导出工具(Excel、Word等)
EasyDataBaseExport是一款表结构信息生成工具,支持mysql、Oracle、Sqlserver、PostgreSql、达梦、虚谷、DB2等数据库,同时支持Excel、Word、Markdown、Html、Pdf文档导出
75 0
Java读取csv文件的三种方式
Java读取csv文件的三种方式
872 0
+关注
SH的全栈笔记
欢迎关注 SH的全栈笔记
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
Java工程师必读手册
立即下载
Java应用提速(速度与激情)
立即下载
Java单元测试实战
立即下载