Java导入导出csv格式文件完整版详解(附代码)
作者:Zachary天使
在Java中你可以使用不同的库来导出CSV格式的文件,这篇文章主要给大家介绍了关于Java导入导出csv格式文件的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
1.首先介绍下什么是csv文件?
CSV(Comma-Separated Values,逗号分隔的值)是一种简单、实用的文件格式,用于存储和表示包括文本、数值等各种类型的数据。CSV 文件通常以 .csv
作为文件扩展名。这种文件格式的一个显著特点是:文件内的数据以逗号 ,
分隔,呈现一个表格形式。CSV 文件已广泛应用于存储、传输和编辑数据。
2.csv文件结构是什么?
CSV 文件的结构相对简单,通常由以下组成:
- 每行表示一条记录:CSV 文件中的每一行代表一条记录,相当于数据库中的一行数据。
- 逗号分隔:每行数据中,使用逗号
,
进行数据分隔,代表不同的数据。 - 引号包围:当数据单元格中的内容含有逗号时,为避免混淆,需要引号 (单引号
'
或双引号"
)将这个数据包围起来,防止误认为是两个不同数据。
例如:
姓名,年龄,性别
张三,25,男
李四,28,男
王五,22,女
上面的例子中,CSV 文件包含三列(姓名、年龄和性别),每行都由逗号 ,
分隔的三个数据项组成。
3.csv文件在导入和导出过程中需要注意什么呢?
在导出csv文件时,通常都是通过文件流的格式进行导出。所以我们一定要注意文件逗号的处理以及换行。话不多说,上代码。
4.csv文件的导出工具类
package org.springjmis.SupSim.utils; import cn.hutool.json.JSON; import com.alibaba.nacos.common.utils.CollectionUtils; import org.springjmis.core.tool.utils.ObjectUtil; import springfox.documentation.spring.web.json.Json; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; public class CsvExportUtil { /** * CSV文件列分隔符 */ private static final String CSV_COLUMN_SEPARATOR = ","; /** * CSV文件行分隔符 */ private static final String CSV_ROW_SEPARATOR = System.lineSeparator(); /** * @param dataList 集合数据 * @param titles 表头部数据 * @param keys 表内容的键值 * @param os 输出流 */ public static void doExport(List<Map<String, Object>> dataList, String[] titles, String keys, OutputStream os) throws Exception { // 保证线程安全 StringBuffer buf = new StringBuffer(); String[] titleArr = null; String[] keyArr = null; // titleArr = titles.split(","); keyArr = keys.split(","); // 组装表头 for (String title : titles) { buf.append(title).append(CSV_COLUMN_SEPARATOR); } buf.append(CSV_ROW_SEPARATOR); // 组装数据 if (CollectionUtils.isNotEmpty(dataList)) { for (Map<String, Object> data : dataList) { for (String key : keyArr) { //这块的条件根据自己业务需要自行修改 if(key.equals("TmplateJson")|| key.equals("code")||key.equals("input") || key.equals("output") ){ if(ObjectUtil.isEmpty(data.get(key))){ buf.append(data.get(key)).append(CSV_COLUMN_SEPARATOR); }else { //这块主要是在导出csv文件时,如何数据的内容当中包含逗号时,进行!替换。否则解析时会导致数据分割报错。不是自己真是数据。 String replace = data.get(key).toString().replaceAll(",", "!"); StringBuffer append = buf.append(data.get(key).toString().replaceAll(",", "!")).append(CSV_COLUMN_SEPARATOR); } }else { if(ObjectUtil.isEmpty(data.get(key))){ buf.append("").append(CSV_COLUMN_SEPARATOR); }else { buf.append(data.get(key)).append(CSV_COLUMN_SEPARATOR); } } } buf.append(CSV_ROW_SEPARATOR); } } // 写出响应 os.write(buf.toString().getBytes("UTF-8")); os.flush(); } /** * 设置Header * * @param fileName * @param response * @throws */ public static void responseSetProperties(String fileName, HttpServletResponse response) throws UnsupportedEncodingException { // 设置文件后缀 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String fn = fileName + sdf.format(new Date()) + ".csv"; // 读取字符编码 String utf = "UTF-8"; // 设置响应 response.setContentType("application/ms-txt.numberformat:@"); response.setCharacterEncoding(utf); response.setHeader("Pragma", "public"); response.setHeader("Cache-Control", "max-age=30"); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fn, utf)); } }
4.1 serviceImpl实现类具体操作
/** * 仿真工程导出csv格式 * @param ids * @param response */ @Override public void exportCsv(String[] ids, HttpServletResponse response) throws IOException { //需要导出的数据集合 List<SupScale> dataList = null; if (ids.length == 0){ //没有传递参数,导出全部数据 dataList = this.list(); }else{ //根据id集合查询list对象信息 dataList = this.listByIds(Arrays.asList(ids)); } try { // 构造导出数据 List<Map<String, Object>> datas = new ArrayList<>(); Map<String, Object> mapInfo; //设备每列头部信息 String[] csvHeader = {"仿真项目名称", "项目描述", "访问权限", "单位", "模型类型", "文件地址", "模型模版json数据自定义", "模型模版名称", "工程状态"}; // 设置每列字段 String keys="name,Descrile,AccessAuth,City,Analogy,Path,TmplateJson,TmplateName,Status"; //头信息 List<Object> headerList = Arrays.asList(csvHeader); //最终数据 List<List<Object>> resultDataList = new ArrayList<>(); if (ObjectUtil.isNotEmpty(dataList)) { for (SupScale supScale : dataList) { // TODO: 2024/4/26 创建Map集合存储对象的信息 mapInfo = new HashMap<>(csvHeader.length); mapInfo.put("name", supScale.getName()); mapInfo.put("Descrile", supScale.getDescrile()); mapInfo.put("AccessAuth", supScale.getAccessAuth()); mapInfo.put("City", supScale.getCity()); mapInfo.put("Analogy", supScale.getModeAnalogy()); mapInfo.put("Path", supScale.getFilePath()); mapInfo.put("TmplateJson", supScale.getScaleTmplateJson()); mapInfo.put("TmplateName", supScale.getTmplateName()); mapInfo.put("Status", supScale.getScaleStatus()); datas.add(mapInfo); } // 设置导出文件前缀,可自行修改 String fName = "仿真工程_"; // 文件导出 OutputStream os = response.getOutputStream(); CsvExportUtil.responseSetProperties(fName, response); CsvExportUtil.doExport(datas, csvHeader, keys, os); os.close(); } }catch (Exception e) { log.error("导出失败" + e.getMessage(), e); } }
4.2 controller层代码详请
/** * 仿真工程导出csv格式 */ @PostMapping(value = "/exportCsv") @ApiOperation(value = "csv 导出") public void exportCsv (@RequestBody String[] ids,HttpServletResponse response) throws IOException { supScaleService.exportCsv(ids,response); }
导出csv文件的效果图以及数据格式如下:
5.csv文件的导入工具类
package org.springjmis.SupSim.utils; import cn.hutool.core.io.FileUtil; import cn.hutool.core.text.csv.*; import cn.hutool.core.util.CharsetUtil; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.web.multipart.MultipartFile; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import java.io.*; import java.net.URL; import java.nio.charset.Charset; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @Slf4j public class CsvImportUtil { //上传文件的路径 private final static URL PATH = Thread.currentThread().getContextClassLoader().getResource(""); /** * @return File 一般文件类型 * @Description 上传文件的文件类型 * @Param multipartFile **/ public static File uploadFile(MultipartFile multipartFile) { // 获 取上传 路径 String path = PATH.getPath() + multipartFile.getOriginalFilename(); try { // 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 File file = new File(path); // 此抽象路径名表示的文件或目录是否存在 if (!file.getParentFile().exists()) { // 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 file.getParentFile().mkdirs(); } // 转换为一般file 文件 multipartFile.transferTo(file); return file; } catch (IOException e) { e.printStackTrace(); return null; } } /** * @return List<List < String>> * @Description 读取CSV文件的内容(不含表头) * @Param filePath 文件存储路径,colNum 列数 **/ public static List<List<String>> readCSV(String filePath, int colNum) { BufferedReader bufferedReader = null; InputStreamReader inputStreamReader = null; FileInputStream fileInputStream = null; CSVFormat csvFileFormat = CSVFormat.DEFAULT.withQuote(null); try { fileInputStream = new FileInputStream(filePath); inputStreamReader = new InputStreamReader(fileInputStream, "utf-8"); bufferedReader = new BufferedReader(inputStreamReader); // CSVParser parser = CSVFormat.DEFAULT.parse(bufferedReader); CSVParser csvFileParser = new CSVParser(inputStreamReader, csvFileFormat); // 表内容集合,外层 List为行的集合,内层 List为字段集合 List<List<String>> values = new ArrayList<>(); int rowIndex = 0; // 读取文件每行内容 for (CSVRecord record : csvFileParser.getRecords()) { // 跳过表头 if (rowIndex == 0) { rowIndex++; continue; } // 判断下角标是否越界 if (colNum > record.size()) { // 返回空集合 return values; } // 每行的内容 List<String> value = new ArrayList<>(); for (int i = 0; i < colNum; i++) { value.add(record.get(i)); } values.add(value); rowIndex++; } return values; } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 if (bufferedReader != null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } if (inputStreamReader != null) { try { inputStreamReader.close(); } catch (IOException e) { e.printStackTrace(); } } if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } // 读取csv中的数据 public static List<Map<String,Object>> csvImports(MultipartFile file) throws IOException { //2. 进行配置 CsvReadConfig csvReadConfig=new CsvReadConfig(); // 是否跳过空白行 csvReadConfig.setSkipEmptyRows(true); // 是否设置首行为标题行 csvReadConfig.setContainsHeader(true); //构建 CsvReader 对象 CsvReader csvReader = CsvUtil.getReader(csvReadConfig); // 这里转了下 可能会产生临时文件,临时文件目录可以设置,也可以立马删除 CsvData read = csvReader.read(multipartFile2File(file), CharsetUtil.CHARSET_UTF_8); //CsvData read = csvReader.read(FileUtil.file(file.getOriginalFilename()), Charset.forName("utf-8")); List<Map<String,Object>> mapList = new ArrayList<>(); List<String> header = read.getHeader(); // TODO: 2024/4/26 获取所有的标题头部信息 List<CsvRow> rows = read.getRows();// TODO: 2024/4/26 获取csv文件每行数据 for (CsvRow row : rows) { Map<String,Object> map = new HashMap<>(); for (int i = 0; i < row.size(); i++) { map.put(header.get(i),row.get(i)); } mapList.add(map); } return mapList; } /** * multipartFile转File **/ public static File multipartFile2File(MultipartFile multipartFile){ File file = null; if (multipartFile != null){ try { file=File.createTempFile("tmp", null); multipartFile.transferTo(file); System.gc(); file.deleteOnExit(); }catch (Exception e){ e.printStackTrace(); log.warn("multipartFile转File发生异常:"+e); } } return file; } /** * 读取CSV格式的文档数据 * @param filePath CSV格式的文件路劲 * @return dataList csv数据读取放入二维list中。 */ public static List<List<String>> readCSVFileData(String filePath){ BufferedReader reader=null; List<List<String>> dataList=new ArrayList<>(); try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8")); }catch(FileNotFoundException | UnsupportedEncodingException e){ e.printStackTrace(); } try{ String line=null; int lineNum =0; while ((line=reader.readLine())!=null){ if (lineNum != 0) { //(1)内容不存在逗号 // String aa[]=line.split(","); // List<String> cellList= Arrays.asList(aa); // //System.out.println(cellList); // dataList.add(cellList); //(1)内容可能存在逗号,且存在“”英文双引号 String str; line += ","; Pattern pCells = Pattern.compile("(\"[^\"]*(\"{2})*[^\"]*\")*[^,]*,"); Matcher mCells = pCells.matcher(line); List<String> cells = new LinkedList();//每行记录一个list //读取每个单元格 while (mCells.find()) { str = mCells.group(); str = str.replaceAll("(?sm)\"?([^\"]*(\"{2})*[^\"]*)\"?.*,", "$1"); str = str.replaceAll("(?sm)(\"(\"))", "$2"); cells.add(str); } dataList.add(cells); } lineNum++; } } catch (Exception e) { e.printStackTrace(); } finally { try { if (reader != null) { //释放资源 reader.close(); } } catch (IOException e) { e.printStackTrace(); } } return dataList; } }
上面导入工具类当中,两个方法均可以使用,一种是将数据转换成List<List<String>> 格式字符串集合的形式,另一种是将数据转换成List<Map>的形式,主要通过Map集合存储数据。
5.1 serviceImpl实现类具体操作
/** * 仿真工程导入csv格式 * @param file * @return */ @Override public void csvImport(MultipartFile file) throws IOException { // TODO: 2024/4/23 将文件内容解析,存入List容器,List<Map>为每一行内容的集合 List<Map<String,Object>> mapList= CsvImportUtil.csvImports(file); // TODO: 2024/4/26 存储模型工程数据集合,方便统一入库 List<SupScale> supScales=new ArrayList<>(); for(Map<String,Object> map:mapList){ // TODO: 2024/4/26 创建工程的实体对象 SupScale supScale=new SupScale(); supScale.setName(map.get("仿真项目名称").toString()); supScale.setDescrile(map.get("项目描述").toString()); supScale.setAccessAuth(map.get("访问权限").toString()); supScale.setCity(map.get("单位").toString()); supScale.setModeAnalogy(map.get("模型类型").toString()); supScale.setFilePath(map.get("文件地址").toString()); String su = map.get("模型模版json数据自定义").toString(); //这块是将之前用叹号代替逗号的数据进行还原,保证数据完整性和一致性。 String s = su.replaceAll("!", ","); supScale.setScaleTmplateJson(s); supScale.setTmplateName(map.get("模型模版名称").toString()); supScale.setScaleStatus(map.get("工程状态").toString()); supScales.add(supScale); } this.saveBatch(supScales); }
5.2 controller层代码详请
/** * 仿真工程导入csv格式 * @param file * @return */ @ApiOperation("csv 导入") @PostMapping(value = "/csvImport") public R csvImport(@RequestParam("file") MultipartFile file) { try { supScaleService.csvImport(file); return R.success("导入成功"); }catch (Exception e){ return R.fail(400,"文件导入失败"); } }
关于java导入和导出csv文件的内容我们就结束了,希望此代码能帮助各位友友解决工作当中的难题。
总结
到此这篇关于Java导入导出csv格式文件完整版的文章就介绍到这了,更多相关Java导入导出csv格式文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!