java后台使用opencsv生成csv文件,并返回给前端下载(附前后端代码)

该博客详细介绍了如何通过前端选择条件触发后端查询数据库,将查询结果转换为CSV文件并下载。在后端,通过Controller、实现类和核心util方法实现了CSV文件的生成与读取。前端使用Ajax调用接口,接收到文件流后进行下载。解决CSV文件在Excel中打开出现乱码的问题,通过在文件流中添加BOM头来确保正确显示。
摘要由CSDN通过智能技术生成

最近有一个需求,需要 前端页面选择相应条件,后端根据条件查询数据库,将查出的实体类集合转换为csv文件返回给前端,随后前端进行下载。

找了挺多的资料,现在进行一个整理和分享。

1.Controller层:

/**
 * 导出csv文件
 */
@RequestMapping("/exportcsv")
@RequiresPermissions("xxx:xxxx:xxx")
public String exportCsv(HttpServletResponse response) {
    return xxxService.exportCsvFile(response);
}

2.实现类部分:

  public void exportCsv(HttpServletResponse response) throws CsvRequiredFieldEmptyException, CsvDataTypeMismatchException, IOException {
    List<xxxx> xxxxList = new ArrayList<>();
    String[] header = {CsvHeaders.EATID, CsvHeaders.EATCOMPANYNAME, CsvHeaders.YEAR, CsvHeaders.EMFCOMPANYNAME,
      CsvHeaders.RESOURCENAME, CsvHeaders.EMISSIONNAME, CsvHeaders.SCOPE, CsvHeaders.CATEGORY, CsvHeaders.ACTIVITYITEM,
      CsvHeaders.ACTIVITYVOLUMEUNIT};
    CSVUtils.generateCsvFile(xxxxList , "exportResults.csv", header);
    CSVUtils.readCsvFileStream("exportResults.csv", response);
  }

3.核心util导出方法:

 /**
   * Generate a CSV file from the queried data.
   *
   * @param exportResults
   * @param fileName
   * @param header
   * @throws IOException
   * @throws CsvDataTypeMismatchException
   * @throws CsvRequiredFieldEmptyException
   */
  public static void generateCsvFile(List<xxxx> exportResults, String fileName, String[] header)
    throws IOException, CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
    Writer writer = new FileWriter(fileName);
    // write header
    try (CSVWriter csvWriter = new CSVWriter(writer, ICSVWriter.DEFAULT_SEPARATOR, ICSVWriter.NO_QUOTE_CHARACTER,
      ICSVWriter.NO_ESCAPE_CHARACTER, ICSVWriter.DEFAULT_LINE_END)) {
      csvWriter.writeNext(header);
      // write content
      StatefulBeanToCsv<xxxx> beanToCsv = new StatefulBeanToCsvBuilder<xxxx>(
        writer).build();
      beanToCsv.write(exportResults);
      csvWriter.close();
    }
    writer.close();
  }

  /**
   * Read the csv file stream and return to the front-end download.
   *
   * @param fileName
   * @param response
   * @throws UnsupportedEncodingException
   */
  public static void readCsvFileStream(String fileName, HttpServletResponse response)
    throws UnsupportedEncodingException {
    String myFileName = new String(fileName.getBytes("utf-8"), "gbk");
    File file = new File(myFileName);
    if (file.exists()) {
      response.setContentType("application/force-download");// Set force download not to open
      response.addHeader("Content-Disposition", "attachment;fileName=" + myFileName);// set filename
      response.setCharacterEncoding("UTF-8");
      byte[] uft8bom = {(byte) 0xef, (byte) 0xbb, (byte) 0xbf};
      byte[] buffer = new byte[1024];
      FileInputStream fis = null;
      BufferedInputStream bis = null;
      try {
        fis = new FileInputStream(file);
        bis = new BufferedInputStream(fis);
        OutputStream os = response.getOutputStream();
        os.write(uft8bom);
        int i = bis.read(buffer);
        while (i != -1) {
          os.write(buffer, 0, i);
          i = bis.read(buffer);
        }
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        if (bis != null) {
          try {
            bis.close();
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
        if (fis != null) {
          try {
            fis.close();
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
      }
    }
    if (file.delete()) {
      LogUtil.info(file.getName() + " file has been deleted!");
    } else {
      LogUtil.info("File deletion failed!");
    }
  }

前端选择相应条件,查询数据库,并把查询出的数据导出成csv文件。

其中有一个问题是生成csv文件返回给前端下载之后,使用execl打开出现了乱码。这边选择的解决方法是:在将文件写入文件流放入response的时候,文件流增加bom头。

4.前端接受文件流,进行下载

$.ajax({
                            url: process.env.WEB_ENV + '/exportcsv',
                            type: 'GET',
                            headers: {
                                Authorization: `Bearer ${app.config.globalProperties.$keycloak.token}`
                            },
                            data: params,
                            // The type of data sent to the server
                            contentType: 'application/json;charset=utf-8',
                            // Specify the type returned by the server, because we want to return a file stream, so the type is binary data
                            dataType: 'binary',
                            // The properties of the native XMLHttpRequest, set the response type to blob, and receive the file stream
                            xhrFields: {
                                responseType: 'blob'
                            },
                            success: function (result, status, xhr) {
                                // Browser Compatible
                                const download_URL = (
                                    window.URL || window.webkitURL
                                ).createObjectURL(result)

                                // Create a tag, simulate click to download
                                const a_link = document.createElement('a')
                                a_link.href = download_URL
                                // Use the download attribute of the a tag to specify the file name
                                a_link.download =
                                    labels.company?.[params.companyId] +
                                    '_' +
                                    params.enableYear +
                                    '_算定ロジック_' +
                                    Moment(new Date()).format('YYYYMMDDHHmmss').valueOf() +
                                    '.csv'
                                document.body.appendChild(a_link)
                                a_link.click()

                                setTimeout(function () {
                                    // Remove temporary file paths in memory and a tags created for download
                                    URL.revokeObjectURL(download_URL)
                                    a_link.remove()
                                }, 10000)
                            },
                            error: function (xhr, textStatus, errorMessage) {
                                const errorInfo = decodeURIComponent(
                                    xhr.getResponseHeader('errorInfo')
                                )
                                // display error messages
                                alert(errorInfo)
                            }
                        })

这样就实现了前端调用后端接口生成下载csv文件的一个完整需求。

可以使用Java 8中的函数式接口来实现按批次接收数据并通过CSVWriter写入文件。 首先,我们需要定义一个函数式接口来表示数据处理函数: ```java @FunctionalInterface public interface DataHandler<T> { void handle(List<T> data, CSVWriter writer) throws IOException; } ``` 该接口接受一个数据列表和一个CSVWriter对象,并将数据写入CSV文件中。 然后,我们可以实现一个方法来按批次接收数据并调用数据处理函数: ```java public static <T> void processDataInBatches(Iterable<T> data, int batchSize, CSVWriter writer, DataHandler<T> handler) throws IOException { List<T> batch = new ArrayList<>(batchSize); for (T item : data) { batch.add(item); if (batch.size() == batchSize) { handler.handle(batch, writer); batch.clear(); } } if (!batch.isEmpty()) { handler.handle(batch, writer); } } ``` 该方法接受一个数据集合,以及批次大小、CSVWriter对象和数据处理函数。它会遍历数据集合,将数据添加到批次列表中,当批次列表大小达到指定大小时,将调用数据处理函数将批次数据写入CSV文件中。如果还有剩余的数据没有写入,最后再调用一次数据处理函数。 最后,我们可以定义一个数据处理函数来将数据写入CSV文件中: ```java DataHandler<String[]> csvHandler = (data, writer) -> { for (String[] row : data) { writer.writeNext(row); } }; ``` 该函数接受一个字符串数组列表和一个CSVWriter对象,将每个字符串数组写入CSV文件中。 完整示例代码如下: ```java import com.opencsv.CSVWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @FunctionalInterface public interface DataHandler<T> { void handle(List<T> data, CSVWriter writer) throws IOException; } public class CsvWriterExample { public static void main(String[] args) throws IOException { String[] header = {"id", "name", "age"}; List<String[]> data = Arrays.asList( new String[]{"1", "Alice", "25"}, new String[]{"2", "Bob", "30"}, new String[]{"3", "Charlie", "35"}, new String[]{"4", "Dave", "40"} ); int batchSize = 2; String fileName = "data.csv"; try (CSVWriter writer = new CSVWriter(Files.newBufferedWriter(Paths.get(fileName)))) { writer.writeNext(header); DataHandler<String[]> csvHandler = (batch, w) -> { for (String[] row : batch) { w.writeNext(row); } }; processDataInBatches(data, batchSize, writer, csvHandler); } } public static <T> void processDataInBatches(Iterable<T> data, int batchSize, CSVWriter writer, DataHandler<T> handler) throws IOException { List<T> batch = new ArrayList<>(batchSize); for (T item : data) { batch.add(item); if (batch.size() == batchSize) { handler.handle(batch, writer); batch.clear(); } } if (!batch.isEmpty()) { handler.handle(batch, writer); } } } ``` 该示例代码将数据按批次写入CSV文件中。在main方法中,我们定义了一个包含数据的字符串数组列表和一个批次大小。然后,我们创建一个CSVWriter对象,并定义一个数据处理函数将批次数据写入CSV文件中。最后,我们调用processDataInBatches方法将数据按批次写入CSV文件中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值