Java導(dǎo)入導(dǎo)出csv格式文件完整版詳解(附代碼)
1.首先介紹下什么是csv文件?
CSV(Comma-Separated Values,逗號(hào)分隔的值)是一種簡(jiǎn)單、實(shí)用的文件格式,用于存儲(chǔ)和表示包括文本、數(shù)值等各種類型的數(shù)據(jù)。CSV 文件通常以 .csv
作為文件擴(kuò)展名。這種文件格式的一個(gè)顯著特點(diǎn)是:文件內(nèi)的數(shù)據(jù)以逗號(hào) ,
分隔,呈現(xiàn)一個(gè)表格形式。CSV 文件已廣泛應(yīng)用于存儲(chǔ)、傳輸和編輯數(shù)據(jù)。
2.csv文件結(jié)構(gòu)是什么?
CSV 文件的結(jié)構(gòu)相對(duì)簡(jiǎn)單,通常由以下組成:
- 每行表示一條記錄:CSV 文件中的每一行代表一條記錄,相當(dāng)于數(shù)據(jù)庫(kù)中的一行數(shù)據(jù)。
- 逗號(hào)分隔:每行數(shù)據(jù)中,使用逗號(hào)
,
進(jìn)行數(shù)據(jù)分隔,代表不同的數(shù)據(jù)。 - 引號(hào)包圍:當(dāng)數(shù)據(jù)單元格中的內(nèi)容含有逗號(hào)時(shí),為避免混淆,需要引號(hào) (單引號(hào)
'
或雙引號(hào)"
)將這個(gè)數(shù)據(jù)包圍起來,防止誤認(rèn)為是兩個(gè)不同數(shù)據(jù)。
例如:
姓名,年齡,性別
張三,25,男
李四,28,男
王五,22,女
上面的例子中,CSV 文件包含三列(姓名、年齡和性別),每行都由逗號(hào) ,
分隔的三個(gè)數(shù)據(jù)項(xiàng)組成。
3.csv文件在導(dǎo)入和導(dǎo)出過程中需要注意什么呢?
在導(dǎo)出csv文件時(shí),通常都是通過文件流的格式進(jìn)行導(dǎo)出。所以我們一定要注意文件逗號(hào)的處理以及換行。話不多說,上代碼。
4.csv文件的導(dǎo)出工具類
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 集合數(shù)據(jù) * @param titles 表頭部數(shù)據(jù) * @param keys 表內(nèi)容的鍵值 * @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); // 組裝數(shù)據(jù) if (CollectionUtils.isNotEmpty(dataList)) { for (Map<String, Object> data : dataList) { for (String key : keyArr) { //這塊的條件根據(jù)自己業(yè)務(wù)需要自行修改 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 { //這塊主要是在導(dǎo)出csv文件時(shí),如何數(shù)據(jù)的內(nèi)容當(dāng)中包含逗號(hào)時(shí),進(jìn)行!替換。否則解析時(shí)會(huì)導(dǎo)致數(shù)據(jù)分割報(bào)錯(cuò)。不是自己真是數(shù)據(jù)。 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); } } // 寫出響應(yīng) os.write(buf.toString().getBytes("UTF-8")); os.flush(); } /** * 設(shè)置Header * * @param fileName * @param response * @throws */ public static void responseSetProperties(String fileName, HttpServletResponse response) throws UnsupportedEncodingException { // 設(shè)置文件后綴 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String fn = fileName + sdf.format(new Date()) + ".csv"; // 讀取字符編碼 String utf = "UTF-8"; // 設(shè)置響應(yīng) 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實(shí)現(xiàn)類具體操作
/** * 仿真工程導(dǎo)出csv格式 * @param ids * @param response */ @Override public void exportCsv(String[] ids, HttpServletResponse response) throws IOException { //需要導(dǎo)出的數(shù)據(jù)集合 List<SupScale> dataList = null; if (ids.length == 0){ //沒有傳遞參數(shù),導(dǎo)出全部數(shù)據(jù) dataList = this.list(); }else{ //根據(jù)id集合查詢list對(duì)象信息 dataList = this.listByIds(Arrays.asList(ids)); } try { // 構(gòu)造導(dǎo)出數(shù)據(jù) List<Map<String, Object>> datas = new ArrayList<>(); Map<String, Object> mapInfo; //設(shè)備每列頭部信息 String[] csvHeader = {"仿真項(xiàng)目名稱", "項(xiàng)目描述", "訪問權(quán)限", "單位", "模型類型", "文件地址", "模型模版json數(shù)據(jù)自定義", "模型模版名稱", "工程狀態(tài)"}; // 設(shè)置每列字段 String keys="name,Descrile,AccessAuth,City,Analogy,Path,TmplateJson,TmplateName,Status"; //頭信息 List<Object> headerList = Arrays.asList(csvHeader); //最終數(shù)據(jù) List<List<Object>> resultDataList = new ArrayList<>(); if (ObjectUtil.isNotEmpty(dataList)) { for (SupScale supScale : dataList) { // TODO: 2024/4/26 創(chuàng)建Map集合存儲(chǔ)對(duì)象的信息 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); } // 設(shè)置導(dǎo)出文件前綴,可自行修改 String fName = "仿真工程_"; // 文件導(dǎo)出 OutputStream os = response.getOutputStream(); CsvExportUtil.responseSetProperties(fName, response); CsvExportUtil.doExport(datas, csvHeader, keys, os); os.close(); } }catch (Exception e) { log.error("導(dǎo)出失敗" + e.getMessage(), e); } }
4.2 controller層代碼詳請(qǐng)
/** * 仿真工程導(dǎo)出csv格式 */ @PostMapping(value = "/exportCsv") @ApiOperation(value = "csv 導(dǎo)出") public void exportCsv (@RequestBody String[] ids,HttpServletResponse response) throws IOException { supScaleService.exportCsv(ids,response); }
導(dǎo)出csv文件的效果圖以及數(shù)據(jù)格式如下:
5.csv文件的導(dǎo)入工具類
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 { // 通過將給定的路徑名字符串轉(zhuǎn)換為抽象路徑名來創(chuàng)建新的 File實(shí)例 File file = new File(path); // 此抽象路徑名表示的文件或目錄是否存在 if (!file.getParentFile().exists()) { // 創(chuàng)建由此抽象路徑名命名的目錄,包括任何必需但不存在的父目錄 file.getParentFile().mkdirs(); } // 轉(zhuǎn)換為一般file 文件 multipartFile.transferTo(file); return file; } catch (IOException e) { e.printStackTrace(); return null; } } /** * @return List<List < String>> * @Description 讀取CSV文件的內(nèi)容(不含表頭) * @Param filePath 文件存儲(chǔ)路徑,colNum 列數(shù) **/ 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); // 表內(nèi)容集合,外層 List為行的集合,內(nèi)層 List為字段集合 List<List<String>> values = new ArrayList<>(); int rowIndex = 0; // 讀取文件每行內(nèi)容 for (CSVRecord record : csvFileParser.getRecords()) { // 跳過表頭 if (rowIndex == 0) { rowIndex++; continue; } // 判斷下角標(biāo)是否越界 if (colNum > record.size()) { // 返回空集合 return values; } // 每行的內(nèi)容 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 { //關(guān)閉流 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中的數(shù)據(jù) public static List<Map<String,Object>> csvImports(MultipartFile file) throws IOException { //2. 進(jìn)行配置 CsvReadConfig csvReadConfig=new CsvReadConfig(); // 是否跳過空白行 csvReadConfig.setSkipEmptyRows(true); // 是否設(shè)置首行為標(biāo)題行 csvReadConfig.setContainsHeader(true); //構(gòu)建 CsvReader 對(duì)象 CsvReader csvReader = CsvUtil.getReader(csvReadConfig); // 這里轉(zhuǎn)了下 可能會(huì)產(chǎn)生臨時(shí)文件,臨時(shí)文件目錄可以設(shè)置,也可以立馬刪除 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 獲取所有的標(biāo)題頭部信息 List<CsvRow> rows = read.getRows();// TODO: 2024/4/26 獲取csv文件每行數(shù)據(jù) 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轉(zhuǎn)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轉(zhuǎn)File發(fā)生異常:"+e); } } return file; } /** * 讀取CSV格式的文檔數(shù)據(jù) * @param filePath CSV格式的文件路勁 * @return dataList csv數(shù)據(jù)讀取放入二維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)內(nèi)容不存在逗號(hào) // String aa[]=line.split(","); // List<String> cellList= Arrays.asList(aa); // //System.out.println(cellList); // dataList.add(cellList); //(1)內(nèi)容可能存在逗號(hào),且存在“”英文雙引號(hào) String str; line += ","; Pattern pCells = Pattern.compile("(\"[^\"]*(\"{2})*[^\"]*\")*[^,]*,"); Matcher mCells = pCells.matcher(line); List<String> cells = new LinkedList();//每行記錄一個(gè)list //讀取每個(gè)單元格 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; } }
上面導(dǎo)入工具類當(dāng)中,兩個(gè)方法均可以使用,一種是將數(shù)據(jù)轉(zhuǎn)換成List<List<String>> 格式字符串集合的形式,另一種是將數(shù)據(jù)轉(zhuǎn)換成List<Map>的形式,主要通過Map集合存儲(chǔ)數(shù)據(jù)。
5.1 serviceImpl實(shí)現(xiàn)類具體操作
/** * 仿真工程導(dǎo)入csv格式 * @param file * @return */ @Override public void csvImport(MultipartFile file) throws IOException { // TODO: 2024/4/23 將文件內(nèi)容解析,存入List容器,List<Map>為每一行內(nèi)容的集合 List<Map<String,Object>> mapList= CsvImportUtil.csvImports(file); // TODO: 2024/4/26 存儲(chǔ)模型工程數(shù)據(jù)集合,方便統(tǒng)一入庫(kù) List<SupScale> supScales=new ArrayList<>(); for(Map<String,Object> map:mapList){ // TODO: 2024/4/26 創(chuàng)建工程的實(shí)體對(duì)象 SupScale supScale=new SupScale(); supScale.setName(map.get("仿真項(xiàng)目名稱").toString()); supScale.setDescrile(map.get("項(xiàng)目描述").toString()); supScale.setAccessAuth(map.get("訪問權(quán)限").toString()); supScale.setCity(map.get("單位").toString()); supScale.setModeAnalogy(map.get("模型類型").toString()); supScale.setFilePath(map.get("文件地址").toString()); String su = map.get("模型模版json數(shù)據(jù)自定義").toString(); //這塊是將之前用嘆號(hào)代替逗號(hào)的數(shù)據(jù)進(jìn)行還原,保證數(shù)據(jù)完整性和一致性。 String s = su.replaceAll("!", ","); supScale.setScaleTmplateJson(s); supScale.setTmplateName(map.get("模型模版名稱").toString()); supScale.setScaleStatus(map.get("工程狀態(tài)").toString()); supScales.add(supScale); } this.saveBatch(supScales); }
5.2 controller層代碼詳請(qǐng)
/** * 仿真工程導(dǎo)入csv格式 * @param file * @return */ @ApiOperation("csv 導(dǎo)入") @PostMapping(value = "/csvImport") public R csvImport(@RequestParam("file") MultipartFile file) { try { supScaleService.csvImport(file); return R.success("導(dǎo)入成功"); }catch (Exception e){ return R.fail(400,"文件導(dǎo)入失敗"); } }
關(guān)于java導(dǎo)入和導(dǎo)出csv文件的內(nèi)容我們就結(jié)束了,希望此代碼能幫助各位友友解決工作當(dāng)中的難題。
總結(jié)
到此這篇關(guān)于Java導(dǎo)入導(dǎo)出csv格式文件完整版的文章就介紹到這了,更多相關(guān)Java導(dǎo)入導(dǎo)出csv格式文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決DataInputStream?read不等于-1,socket文件傳輸只能傳輸一個(gè)文件無法傳輸多個(gè)問題
這篇文章主要介紹了解決DataInputStream?read不等于-1,socket文件傳輸只能傳輸一個(gè)文件無法傳輸多個(gè)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08Java結(jié)構(gòu)性設(shè)計(jì)模式中的裝飾器模式介紹使用
裝飾器模式又名包裝(Wrapper)模式。裝飾器模式以對(duì)客戶端透明的方式拓展對(duì)象的功能,是繼承關(guān)系的一種替代方案,本篇文章以虹貓藍(lán)兔生動(dòng)形象的為你帶來詳細(xì)講解2022-09-09詳解SpringBoot的三種緩存技術(shù)(Spring Cache、Layering Cache 框架、Alibaba J
這篇文章主要介紹了SpringBoot的三種緩存技術(shù),幫助大家更好的理解和學(xué)習(xí)springboot框架,感興趣的朋友可以了解下2020-10-10SpringBoot實(shí)現(xiàn)數(shù)據(jù)預(yù)熱的方式小結(jié)
這里用到的數(shù)據(jù)預(yù)熱,就是在項(xiàng)目啟動(dòng)時(shí)將一些數(shù)據(jù)量較大的數(shù)據(jù)加載到緩存中(筆者這里用的Redis),那么在項(xiàng)目啟動(dòng)有哪些方式可以實(shí)現(xiàn)數(shù)據(jù)預(yù)熱呢,本文就來給大家講講幾種實(shí)現(xiàn)數(shù)據(jù)預(yù)熱的方式,需要的朋友可以參考下2023-09-09mybatis類型轉(zhuǎn)換器如何實(shí)現(xiàn)數(shù)據(jù)加解密
這篇文章主要介紹了mybatis類型轉(zhuǎn)換器如何實(shí)現(xiàn)數(shù)據(jù)加解密,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09解決SpringBoot搭建MyBatisPlus中selectList遇到LambdaQueryWrapper報(bào)錯(cuò)問題
這篇文章主要介紹了解決SpringBoot搭建MyBatisPlus中selectList遇到LambdaQueryWrapper報(bào)錯(cuò)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java正則驗(yàn)證正整數(shù)的方法分析【測(cè)試可用】
這篇文章主要介紹了Java正則驗(yàn)證正整數(shù)的方法,結(jié)合實(shí)例形式對(duì)比分析了java針對(duì)正整數(shù)的驗(yàn)證方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-08-08