0


Java导出大批量数据(文件格式篇xls,xlsx,csv)

根据最近写出的导出方法 打算在文章中记录总结下学习心得

java导出我准备分为三篇文章介绍
分批查询导出篇https://blog.csdn.net/weixin_56567361/article/details/126647979异步多线程导出篇https://blog.csdn.net/weixin_56567361/article/details/126718950?spm=1001.2014.3001.5501

Java导出excel文件 我分为了xls,xlsx,csv三个文件格式

首先介绍下三种文件格式的优劣
xls文件 xlsx文件csv文件最大65536行 256列最大1048576行 16384列纯文本文件 无行数上线 但无法编辑表头,表内容样式占用空间大 占用空间小运算速度相比xls快些占用空间小 导出后打开很迅速wps,,excel,均可打开(最多显示一千万条数据,推荐notepad等其他文本编辑软件打开)
导出少量数据 需要表头样式的(例如mysql中基础数据) 采用xlsx文件导出

导出大量数据 无表头样式要求(例如clickhouse中历史数据) 采用csv文件导出

xlxs文件导出

导出效果如图 可自定义多级表头 格式颜色等 这里展示基础的单表头导出

首先pom加上 以下导出匀以poi4.1.2版本测试

<!-- excel工具 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>

xls导出则把方法中出现的XSSF改为HSSF

建议使用SXSSFWorkbook

从POI 3.8版本开始 提供了一种基于XSSF的低内存占用的SXSSF方式 对于大型excel文件的创建 一个关键问题就是 要确保不会内存溢出 其实 就算生成很小的excel(比如几Mb)它用掉的内存是远大于excel文件实际的size的 如果单元格还有各种格式(比如,加粗,背景标红之类的)那它占用的内存就更多了 对于大型excel的创建且不会内存溢出的 就只有SXSSFWorkbook了 它的原理很简单,用硬盘空间换内存(就像hash map用空间换时间一样)

controller层

    @GetMapping("/export")
    public void export(HttpServletResponse response, 对象 对象名) throws Exception {
        List<对象> list = Service.getList(对象名);
        //表头
        String[] title = new String[]{"测试1", "测试2","测试3"};
        List<String[]> rows = new ArrayList<>();
        String[] row = null;
        //表数据
        for (对象 名: list) {
            row = new String[]{名.getTest1(),名.getTest2(),名.getTest3()};
            rows.add(row);
        }
        //xlsx文件导出
        SXSSFWorkbook workbook = PoiUtils.exportExcelSXSSF(title, rows);
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode("导出表.xlsx", "utf-8"));
        response.flushBuffer();
        workbook.write(response.getOutputStream());
        //处理工作表在磁盘上产生的临时文件
        workbook.dispose();
    }

PoiUtils

/**
     * 低占用内存xlsx文件导出
     *
     * @param title
     * @param rows
     * @return
     */
    public static SXSSFWorkbook exportExcelSXSSF(String[] title, List<String[]> rows) {
        //这样表示SXSSFWorkbook只会保留100条数据在内存中,其它的数据都会写到磁盘里,这样的话占用的内存就会很少
        SXSSFWorkbook workbook = new SXSSFWorkbook(100);
        // 生成一个表格
        SXSSFSheet sheet = workbook.createSheet();
        //设置表头白字黑底居中
        Font font = workbook.createFont();
        //设置字体颜色
        font.setColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex());
        font.setFontName("宋体");
        //设置表头边框
        CellStyle headerStyle = workbook.createCellStyle();
        headerStyle.setBorderBottom(BorderStyle.HAIR);
        headerStyle.setBorderLeft(BorderStyle.HAIR);
        headerStyle.setBorderRight(BorderStyle.HAIR);
        headerStyle.setBorderTop(BorderStyle.HAIR);
        // 创建一个居中格式
        headerStyle.setAlignment(HorizontalAlignment.CENTER);
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //五十度灰
        headerStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.GREY_50_PERCENT.getIndex());
        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headerStyle.setFont(font);
        Font contentFont = workbook.createFont();
        contentFont.setFontName("宋体");
        contentFont.setFontHeightInPoints((short) 10);
        //设置表内容边框
        CellStyle bodyStyle = workbook.createCellStyle();
        bodyStyle.setBorderBottom(BorderStyle.HAIR);
        bodyStyle.setBorderLeft(BorderStyle.HAIR);
        bodyStyle.setBorderRight(BorderStyle.HAIR);
        bodyStyle.setBorderTop(BorderStyle.HAIR);
        // 创建一个居中格式
        bodyStyle.setAlignment(HorizontalAlignment.CENTER);
        bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        bodyStyle.setFont(contentFont);
        // 设置表格默认列宽度为15个字节
        sheet.setDefaultColumnWidth((short) 18);
        sheet.setColumnWidth(title.length - 1, (int) ((40 + 0.72) * 256));
        // 循环字段名数组,创建标题行
        SXSSFRow row = sheet.createRow(0);
        for (int j = 0; j < title.length; j++) {
            // 创建列
            SXSSFCell cell = row.createCell(j);
            // 设置单元类型为String
            cell.setCellType(CellType.STRING);
            cell.setCellValue(title[j]);
            cell.setCellStyle(headerStyle);
        }
        for (int i = 0; i < rows.size(); i++) {
            // 因为第一行已经用于创建标题行,故从第二行开始创建
            row = sheet.createRow(i + 1);
            // 如果是第一行就让其为标题行
            String[] rowData = rows.get(i);
            //每一行的数据
            recyclingCellSXSSF(rowData, row, bodyStyle);
        }
        return workbook;
    }

    private static void recyclingCellSXSSF(String[] rowData, SXSSFRow row, CellStyle bodyStyle) {
        for (int j = 0; j < rowData.length; j++) {
            // 创建列
            SXSSFCell cell = row.createCell(j);
            cell.setCellType(CellType.STRING);
            cell.setCellValue(rowData[j]);
            cell.setCellStyle(bodyStyle);
        }
    }

不需要表头样式可注掉

前端我使用的若依框架 这里也展示下代码

index.vue

     /** 导出按钮操作 */
      handleExport() {
        const queryParams = this.queryParams
        this.$confirm('是否确认导出所有信息?', '警告', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        })
          .then(() => {
            this.exportDataFan(queryParams)
          })
      },
      async exportDataFan(params) {
        try {
          const res = await exportChineseParsing(params)
          this.downLoad('导出表.xlsx', res)
        } catch (e) {
          console.log(e)
        }
      },
      downLoad(filename, content) {
        //filename 文件名,content 下载的内容
        var aLink = document.createElement('a')
        var blob = new Blob([content], {
          type: 'application/x-xls'
        })
        var evt = new Event('click')
        aLink.download = filename
        aLink.href = URL.createObjectURL(blob)
        aLink.click()
        URL.revokeObjectURL(blob)
      },

js文件

//导出
export function export(query) {
  return request({
    url: '/路径名/路径名/export',
    method: 'get',
    params: query,
    responseType: 'blob'
    //设置响应数据类型为 blob。这句话很重要!!!
  })
}

csv文件导出

controller层(导出参数也是需要表头,表数据 只用替换下面导出方法)

    @GetMapping("/export")
    public void export(HttpServletResponse response, 对象 对象名) throws Exception {
        List<对象> list = Service.getList(对象名);
        //表头
        String[] title = new String[]{"测试1", "测试2","测试3"};
        List<String[]> rows = new ArrayList<>();
        String[] row = null;
        //表数据
        for (对象 名: list) {
            row = new String[]{名.getTest1(),名.getTest2(),名.getTest3()};
            rows.add(row);
        }
        //创建临时csv文件
        File tempFile = PoiUtils.createTempFile(rows, title);
        //输出csv流文件,提供给浏览器下载
        PoiUtils.outCsvStream(response, tempFile);
        //删除临时文件
        PoiUtils.deleteFile(tempFile);
    }            

PoiUtils

    /**
     * 创建临时的csv文件
     *
     * @return
     * @throws IOException
     */
    public static File createTempFile(List<String[]> datas, String[] headers) throws IOException {
        File tempFile = File.createTempFile("vehicle", ".csv");
        CsvWriter csvWriter = new CsvWriter(tempFile.getCanonicalPath(), ',', StandardCharsets.UTF_8);
        // 写表头
        csvWriter.writeRecord(headers);
        for (String[] data : datas) {
            //这里如果数据不是String类型,请进行转换
            for (String datum : data) {
                csvWriter.write(datum, true);
            }
            csvWriter.endRecord();
        }
        csvWriter.close();
        return tempFile;
    }

    /**
     * 普通csv文件传浏览器
     *
     * @param response
     * @param tempFile
     * @throws IOException
     */
    public static void outCsvStream(HttpServletResponse response, File tempFile) throws IOException {
        java.io.OutputStream out = response.getOutputStream();
        byte[] b = new byte[10240];
        java.io.File fileLoad = new java.io.File(tempFile.getCanonicalPath());
        response.reset();
        response.setContentType("application/csv");
        response.setHeader("content-disposition", "attachment; filename=" + URLEncoder.encode("export.csv", "UTF-8"));
        java.io.FileInputStream in = new java.io.FileInputStream(fileLoad);
        int n;
        //为了保证excel打开csv不出现中文乱码
        out.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
        while ((n = in.read(b)) != -1) {
            //每次写入out1024字节
            out.write(b, 0, n);
        }
        in.close();
        out.close();
    }

    /**
     * 删除文件
     *
     * @param file
     * @return
     */
    public static boolean deleteFile(File file) {
        // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
        if (file.exists() && file.isFile()) {
            return file.delete();
        } else {
            return false;
        }
    }

CsvWriter(代码略长)

package com.hnxr.scada.utils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.charset.Charset;

public class CsvWriter {
    private PrintWriter outputStream;
    private String fileName;
    private boolean firstColumn;
    private boolean useCustomRecordDelimiter;
    private Charset charset;
    private UserSettings userSettings;
    private boolean initialized;
    private boolean closed;
    public static final int ESCAPE_MODE_DOUBLED = 1;
    public static final int ESCAPE_MODE_BACKSLASH = 2;

    public CsvWriter(String var1, char var2, Charset var3) {
        this.outputStream = null;
        this.fileName = null;
        this.firstColumn = true;
        this.useCustomRecordDelimiter = false;
        this.charset = null;
        this.userSettings = new UserSettings();
        this.initialized = false;
        this.closed = false;
        if (var1 == null) {
            throw new IllegalArgumentException("Parameter fileName can not be null.");
        } else if (var3 == null) {
            throw new IllegalArgumentException("Parameter charset can not be null.");
        } else {
            this.fileName = var1;
            this.userSettings.Delimiter = var2;
            this.charset = var3;
        }
    }

    public CsvWriter(String var1) {
        this(var1, ',', Charset.forName("ISO-8859-1"));
    }

    public CsvWriter(Writer var1, char var2) {
        this.outputStream = null;
        this.fileName = null;
        this.firstColumn = true;
        this.useCustomRecordDelimiter = false;
        this.charset = null;
        this.userSettings = new UserSettings();
        this.initialized = false;
        this.closed = false;
        if (var1 == null) {
            throw new IllegalArgumentException("Parameter outputStream can not be null.");
        } else {
            this.outputStream = new PrintWriter(var1);
            this.userSettings.Delimiter = var2;
            this.initialized = true;
        }
    }

    public CsvWriter(OutputStream var1, char var2, Charset var3) {
        this(new OutputStreamWriter(var1, var3), var2);
    }

    public char getDelimiter() {
        return this.userSettings.Delimiter;
    }

    public void setDelimiter(char var1) {
        this.userSettings.Delimiter = var1;
    }

    public char getRecordDelimiter() {
        return this.userSettings.RecordDelimiter;
    }

    public void setRecordDelimiter(char var1) {
        this.useCustomRecordDelimiter = true;
        this.userSettings.RecordDelimiter = var1;
    }

    public char getTextQualifier() {
        return this.userSettings.TextQualifier;
    }

    public void setTextQualifier(char var1) {
        this.userSettings.TextQualifier = var1;
    }

    public boolean getUseTextQualifier() {
        return this.userSettings.UseTextQualifier;
    }

    public void setUseTextQualifier(boolean var1) {
        this.userSettings.UseTextQualifier = var1;
    }

    public int getEscapeMode() {
        return this.userSettings.EscapeMode;
    }

    public void setEscapeMode(int var1) {
        this.userSettings.EscapeMode = var1;
    }

    public void setComment(char var1) {
        this.userSettings.Comment = var1;
    }

    public char getComment() {
        return this.userSettings.Comment;
    }

    public boolean getForceQualifier() {
        return this.userSettings.ForceQualifier;
    }

    public void setForceQualifier(boolean var1) {
        this.userSettings.ForceQualifier = var1;
    }

    public void write(String var1, boolean var2) throws IOException {
        this.checkClosed();
        this.checkInit();
        if (var1 == null) {
            var1 = "";
        }

        if (!this.firstColumn) {
            this.outputStream.write(this.userSettings.Delimiter);
        }
        //默认false
        boolean var3 = this.userSettings.ForceQualifier;
        if (!var2 && var1.length() > 0) {
            var1 = var1.trim();
        }

        if (!var3 && this.userSettings.UseTextQualifier && (var1.indexOf(this.userSettings.TextQualifier) > -1 || var1.indexOf(this.userSettings.Delimiter) > -1 || !this.useCustomRecordDelimiter && (var1.indexOf(10) > -1 || var1.indexOf(13) > -1) || this.useCustomRecordDelimiter && var1.indexOf(this.userSettings.RecordDelimiter) > -1 || this.firstColumn && var1.length() > 0 && var1.charAt(0) == this.userSettings.Comment || this.firstColumn && var1.length() == 0)) {
            var3 = true;
        }

        if (this.userSettings.UseTextQualifier && !var3 && var1.length() > 0 && var2) {
            char var4 = var1.charAt(0);
            if (var4 == ' ' || var4 == '\t') {
                var3 = true;
            }

            if (!var3 && var1.length() > 1) {
                char var5 = var1.charAt(var1.length() - 1);
                if (var5 == ' ' || var5 == '\t') {
                    var3 = true;
                }
            }
        }

        if (var3) {
            this.outputStream.write(this.userSettings.TextQualifier);
            if (this.userSettings.EscapeMode == 2) {
                var1 = replace(var1, "\\", "\\\\");
                var1 = replace(var1, "" + this.userSettings.TextQualifier, "\\" + this.userSettings.TextQualifier);
            } else {
                var1 = replace(var1, "" + this.userSettings.TextQualifier, "" + this.userSettings.TextQualifier + this.userSettings.TextQualifier);
            }
        } else if (this.userSettings.EscapeMode == 2) {
            var1 = replace(var1, "\\", "\\\\");
            var1 = replace(var1, "" + this.userSettings.Delimiter, "\\" + this.userSettings.Delimiter);
            if (this.useCustomRecordDelimiter) {
                var1 = replace(var1, "" + this.userSettings.RecordDelimiter, "\\" + this.userSettings.RecordDelimiter);
            } else {
                var1 = replace(var1, "\r", "\\\r");
                var1 = replace(var1, "\n", "\\\n");
            }

            if (this.firstColumn && var1.length() > 0 && var1.charAt(0) == this.userSettings.Comment) {
                if (var1.length() > 1) {
                    var1 = "\\" + this.userSettings.Comment + var1.substring(1);
                } else {
                    var1 = "\\" + this.userSettings.Comment;
                }
            }
        }

        this.outputStream.write(var1);
        if (var3) {
            this.outputStream.write(this.userSettings.TextQualifier);
        }

        this.firstColumn = false;
    }

    public void write(String var1) throws IOException {
        this.write(var1, false);
    }

    public void writeComment(String var1) throws IOException {
        this.checkClosed();
        this.checkInit();
        this.outputStream.write(this.userSettings.Comment);
        this.outputStream.write(var1);
        if (this.useCustomRecordDelimiter) {
            this.outputStream.write(this.userSettings.RecordDelimiter);
        } else {
            this.outputStream.println();
        }

        this.firstColumn = true;
    }

    public void writeRecord(String[] var1, boolean var2) throws IOException {
        if (var1 != null && var1.length > 0) {
            for(int var3 = 0; var3 < var1.length; ++var3) {
                this.write(var1[var3], var2);
            }

            this.endRecord();
        }

    }

    public void writeRecord(String[] var1) throws IOException {
        this.writeRecord(var1, false);
    }

    public void endRecord() throws IOException {
        this.checkClosed();
        this.checkInit();
        if (this.useCustomRecordDelimiter) {
            this.outputStream.write(this.userSettings.RecordDelimiter);
        } else {
            this.outputStream.println();
        }

        this.firstColumn = true;
    }

    private void checkInit() throws IOException {
        if (!this.initialized) {
            if (this.fileName != null) {
                this.outputStream = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.fileName), this.charset));
            }

            this.initialized = true;
        }

    }

    public void flush() {
        this.outputStream.flush();
    }

    public void close() {
        if (!this.closed) {
            this.close(true);
            this.closed = true;
        }

    }

    private void close(boolean var1) {
        if (!this.closed) {
            if (var1) {
                this.charset = null;
            }

            try {
                if (this.initialized) {
                    this.outputStream.close();
                }
            } catch (Exception var3) {
            }

            this.outputStream = null;
            this.closed = true;
        }

    }

    private void checkClosed() throws IOException {
        if (this.closed) {
            throw new IOException("This instance of the CsvWriter class has already been closed.");
        }
    }

    protected void finalize() {
        this.close(false);
    }

    public static String replace(String var0, String var1, String var2) {
        int var3 = var1.length();
        int var4 = var0.indexOf(var1);
        if (var4 <= -1) {
            return var0;
        } else {
            StringBuffer var5 = new StringBuffer();

            int var6;
            for(var6 = 0; var4 != -1; var4 = var0.indexOf(var1, var6)) {
                var5.append(var0.substring(var6, var4));
                var5.append(var2);
                var6 = var4 + var3;
            }

            var5.append(var0.substring(var6));
            return var5.toString();
        }
    }

    public class UserSettings {
        public char TextQualifier = '"';
        public boolean UseTextQualifier = true;
        public char Delimiter = ',';
        public char RecordDelimiter = 0;
        public char Comment = '#';
        public int EscapeMode = 1;
        public boolean ForceQualifier = false;

        public UserSettings() {
        }
    }

    private class Letters {
        public static final char LF = '\n';
        public static final char CR = '\r';
        public static final char QUOTE = '"';
        public static final char COMMA = ',';
        public static final char SPACE = ' ';
        public static final char TAB = '\t';
        public static final char POUND = '#';
        public static final char BACKSLASH = '\\';
        public static final char NULL = '\u0000';

        private Letters() {
        }
    }
}

前端代码

index.vue

    //导出
    exportFile() {
      let params = Object.assign({}, this.form)
      this.$confirm('是否确认导出文件?', '警告', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.exportDataFan(params)
      })
    },

    async exportDataFan(params) {
      try {
        const res = await exportData(params)
        this.downLoad('导出文件.csv', res)
      } catch (e) {
        console.log(e)
      }
    },

    downLoad(filename, content) {
      //filename 文件名,content 下载的内容
      console.log(filename)
      console.log(content)
      var aLink = document.createElement('a')
      var blob = new Blob([content], {
        // type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' //文件类型
        type: 'application/x-xls'
        //type: 'text/csv,charset=UTF-8'
      })
      console.log(blob)
      var evt = new Event('click')
      aLink.download = filename
      aLink.href = URL.createObjectURL(blob)
      aLink.click()
      URL.revokeObjectURL(blob)
    }

js文件

//导出
export function exportData(query){
  return request({
    url:'/地址/地址/export',
    method:'get',
    params:query,
    responseType: 'blob'
  })
}

到此三种导出方法已经介绍完成 大家根据需求调整代码

有遇到什么问题可以留言告诉我哦 欢迎评论区讨论🤪

标签: java 开发语言 idea

本文转载自: https://blog.csdn.net/weixin_56567361/article/details/126640185
版权归原作者 夜の雨 所有, 如有侵权,请联系我们删除。

“Java导出大批量数据(文件格式篇xls,xlsx,csv)”的评论:

还没有评论