用 Java 怎么导出 CSV 格式的文件

大家在做 web 开发时常碰到的一个需求就是:将网页上某个列表页的数据导出。有的要求导出成 excel 格式的文件,有的要求导出成 csv 格式的文件。今天就来给大家介绍怎么利用 java 导出 csv 格式的文件。

什么是 CSV 格式的文件

逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符不一定是逗号),其文件以纯文本形式存储表格数据。纯文本意味着该文件是一个字符序列,即二进制编码的数据都可以被解读成人类可读的形式。CSV 文件由任意数目的记录组成,记录之间以 某种换行符 分隔;每条记录由字段组成,字段之间的分隔符是特定的字符或字符串,最常见的是逗号或制表符。通常,所有记录都拥有完全相同的字段。

下面就是一个符合 csv 格式的示例文件

1
2
3
4
5
nama,age,gender
张三,18,男
李四,20,男
小红,12,女
小明,12,男

导出需要注意的点

  • 可以通过重写数据模型的 toString 方法来提高生成 csv 文件的速度
  • 返回文件的具体编码格式根据需求来定,这里用的是 UTF-8 编码
  • 根据业务要求和服务器性能设定一个单次导出上限值

具体实现

这里是后端直接提供 HTTP 导出接口。

定义一个数据模型

根据要导出的字段来定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.nongfenqi.transport.export;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;

/**
* 导出物流列表的数据模型
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class LogisticsExport implements Serializable {

//物流单编号
private String logisticsInfoId;

//发货单编号
private String deliveryOrderId;

//厂家名称
private String supplierName;

//仓库名称
private String warehouseName;

//品牌名称
private String goodsBrandName;

//商品名称
private String goodsName;

//包装数 单位:袋
private BigDecimal goodsPackageNumber;

//重量 单位:吨
private BigDecimal goodsNumber;

//收货人姓名
private String customerName;

//收货人手机号
private String customerPhone;

//收货地址
private String receiptAddress;

//紧急联系人1
private String firstEmergencyContacter;

//紧急联系人1手机号
private String firstEmergencyContacterPhone;

//紧急联系人2
private String secondEmergencyContacter;

//紧急联系人2手机号
private String secondEmergencyContacterPhone;

//这里重写 toString 方法,是为了方便导出 CSV 文件。
@Override
public String toString() {
return logisticsInfoId + "," +
deliveryOrderId + "," +
supplierName + "," +
warehouseName + "," +
goodsBrandName + "," +
goodsName + "," +
goodsPackageNumber + "," +
goodsNumber + "," +
customerName + "," +
receiptAddress + "," +
firstEmergencyContacter + "," +
firstEmergencyContacterPhone + "," +
secondEmergencyContacter + "," +
secondEmergencyContacterPhone;

}

}

写一个工具类

工具类中包含了两个方法:一个是将数据模型转换成临时的 csv 文件;另一个是将临时 csv 文件写入到 HttpServletResponse 中返回给浏览器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.nongfenqi.transport.common;

import com.nongfenqi.transport.export.LogisticsExport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.util.List;

/**
* 导出工具类
*/
@Service
@Slf4j
public class ExportUtil {

// csv 文件的标题行
private static String TITLE_LINE =
"物流单编号" + "," +
"发货单编号" + "," +
"厂家名称" + "," +
"发货基地" + "," +
"品牌名称" + "," +
"商品名称" + "," +
"包装数 单位:袋" + "," +
"重量 单位:吨" + "," +
"收货人姓名" + "," +
"收货地址" + "," +
"紧急联系人1" + "," +
"紧急联系人1手机号" + "," +
"紧急联系人2" + "," +
"紧急联系人2手机号" + ",";

/**
* 创建一个 CSV 格式的临时文件
*
* @param logisticsExports
* @return
* @throws IOException
*/
public static File createLogisticsCVSFile(List<LogisticsExport> logisticsExports) throws IOException {
File tempFile = File.createTempFile(DateFormat.getDateTimeInstance().toString(), ".csv");
OutputStream out = new FileOutputStream(tempFile);
BufferedWriter bw = new BufferedWriter(new FileWriter(tempFile, true));
bw.write(TITLE_LINE);
bw.newLine();
for (LogisticsExport logistics : logisticsExports) {
bw.write(logistics.toString());
bw.newLine();
}
bw.close();
return tempFile;
}

/**
* 导出 CSV 文件
*
* @param response
* @param file
* @param fileName
* @throws IOException
*/
public static void exportCSVFile(HttpServletResponse response, File file, String fileName)
throws IOException {
response.setContentType("application/csv;charset=UTF-8");
response.setHeader("Content-Disposition",
"attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));

InputStream in = null;
try {
in = new FileInputStream(file);
int len = 0;
byte[] buffer = new byte[1024];
response.setCharacterEncoding("UTF-8");
OutputStream out = response.getOutputStream();
while ((len = in.read(buffer)) > 0) {
out.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
out.write(buffer, 0, len);
}
} catch (FileNotFoundException e) {
log.warn(e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}

}

写一个 HTTP 接口

利用 Spring 框架,写一个 RestController 接口给前端或者浏览器端调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.nongfenqi.transport.controller;

import com.nongfenqi.transport.PageQueryReq;
import com.nongfenqi.transport.QuerySortReq;
import com.nongfenqi.transport.common.ExportUtil;
import com.nongfenqi.transport.export.LogisticsExport;
import com.nongfenqi.transport.logistics.LogisticsQuery;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;


/**
* 物流公司业务接口
*/
@RestController
@RequestMapping("/logistics")
public class TempLogisticsController {

@RequestMapping(value = "export", method = RequestMethod.GET)
public void exportLogisticsList(
@ModelAttribute PageQueryReq pageQuery,
@ModelAttribute QuerySortReq querySortReq,
@ModelAttribute LogisticsQuery logisticsQuery,
HttpServletResponse response
) throws IOException {

// 根据条件筛选出具体需要导出的数据列表
LogisticsExport logisticsExport = LogisticsExport.builder()
.logisticsInfoId("1")
.deliveryOrderId("2")
.build();
List<LogisticsExport> logisticsExports = Collections.singletonList(logisticsExport);

// 创建一个 CSV 格式的临时文件
File logisticsCVSFile = ExportUtil.createLogisticsCVSFile(logisticsExports);

// 将 CSV 文件写入到 HttpServletResponse 当中,返回给浏览器
ExportUtil.exportCSVFile(response, logisticsCVSFile, "logistics.csv");
}

}


引用