本文结构:
- 废话
- 动手读csv
- 写csv
工作中需要读写csv,由于csv 文件较大,数据比较多,所以需要选择一个速度快的csv 读取插件。经过比较选择opencsv 。官方解释:Opencsv是一个易于使用的CSV(逗号分隔值)解析器库。之所以开发它,是因为当时所有CSV解析器都没有商业友好的许可证。Java 7是目前支持的最小版本。
Opencsv
Opencsv支持您可能想要做的所有基本CSV类型的事情:
-
每行的任意数量的值。
-
忽略引用元素中的逗号。
-
处理带有嵌入式回车的带引号的条目(即跨越多行的条目)。
-
可配置的分隔符和引号字符(或使用合理的默认值)。
所有这些事情都可以通过可塑性方法的表现来完成阅读和写作:
-
往返于一个字符串数组。
-
来自带注释的bean。
-
来自数据库一次读取所有条目,或使用Iterator风格的模型
废话不多说:官方文档[http://opencsv.sourceforge.net/]
跟我一起做 读csv
引入依赖
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>4.2</version>
</dependency>
读取csv 映射为对象(此时会按照csv 的顺序进行映射全量,如果需要只取csv中的部分列则看下一部分)
public static <T> List<T> getCsvData(Class<T> clazz) {
InputStreamReader in=null;
try {
in = new InputStreamReader(new FileInputStream("xxxx.csv"), "gbk");
} catch (Exception e) {
}
System.out.println(System.currentTimeMillis());
HeaderColumnNameMappingStrategy<T> strategy = new HeaderColumnNameMappingStrategy<>();
strategy.setType(clazz);
CsvToBean<T> csvToBean = new CsvToBeanBuilder<T>(in)
.withSeparator(',')
.withQuoteChar('\'')
.withMappingStrategy(strategy).build();
List<T> list= csvToBean.parse();
System.out.println(System.currentTimeMillis());
System.out.println(list.size());
return list;
}
如果只需要取csv 中部分列的值映射到对象里,则只需要在对象中添加注解 @CsvBindByName即可
(required = true ) 参数指csv 中该列的值不能为空
public class Student {
@CsvBindByName(column = "name", required = true)
private String name;
@CsvBindByName(column = "address", required = false)
private String address;
@CsvBindByName(column = "age", required = false)
private Integer age;
//不需要从csv中取值
private Integer sex;
当然这样的取值还是不够自由,那么Opencsv还提供了更自由的方式
读取csv 的任意行和列。而且csv很大的话,速度也很快哦,只要内存跟得上。
minLine 为读取的起始行,当然你也可以再设置一个结束行的字段,每次判断行号。当然也需要自己设置一个行号,每次自增。
private static Set<String> readCsv(int minLine, String path) throws IOException {
Integer lineNum = minLine;
FileReader fReader = new FileReader(new File(path));
CSVReader csvReader = new CSVReader(fReader);
String[] values;
Set<String> set = new HashSet<>();
//跳过前方minLine 条数据
csvReader.skip(minLine);
while ((values = csvReader.readNext()) != null) {
// values 是一个数组,存储了csv当前行的所有元素,在此你可以将数组中的元素取出来放入你的对象中
set.add(StringUtils.join(values, Constants.CSV_SPLIT));
lineNum++;
}
return set;
}
写csv
public static void main(String[] args) throws Exception {
File file = new File("xxx.csv");
Writer writer = new FileWriter(file);
//分隔符默认为逗号
CSVWriter csvWriter = new CSVWriter(writer);
String[] strs = {"abc" , "abc" , "abc"};
csvWriter.writeNext(strs);
csvWriter.close();
}
写 csv 方法2
此方法不用引入其他依赖,jdk自身的就够了,目前我使用此方法
controller 层
@GetMapping("/csv")
public void csv(HttpServletRequest request, HttpServletResponse response, String keyword) throws Exception{
//查询数据库获取导出数据
List<Student> list = xxxService.exportList(keyword);
String fileName = ExportUtil.getFileName(request, "测试数据" + System.currentTimeMillis() + ".csv");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
// 表头
LinkedHashMap<String, Object> header = new LinkedHashMap<>();
header.put("1", "姓名");
header.put("2", "年龄");
header.put("3", "性别");
header.put("4", "地址");
List<LinkedHashMap<String, Object>> data = new ArrayList<>();
data.add(header);
list.forEach(x->{
LinkedHashMap<String, Object> body = new LinkedHashMap<>();
body.put("1", x.getName());
body.put("2", x.getAge());
body.put("3", x.getSex());
body.put("4", x.getAddress());
data.add(body);
});
FileCopyUtils.copy(ExportUtil.exportCSV(data), response.getOutputStream());
}
ExportUtil 工具类
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @author yyb
* @time 2020/3/9
*/
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(",");
}
}
}
public static 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;
}
}
好了,打完收工。