【SpringBoot】22 Txt、Csv文件的读取和写入

仓库

https://gitee.com/Lin_DH/system

介绍

简介

CSV(逗号分隔值,Comma-Separated Values,又称字符分隔值),文件以纯文本形式存储表格数据。

优点

简单性:Csv 文件易于创建和编辑,可以使用任何文本编辑器打开和修改。
兼容性:格式简单且广泛支持,可以轻松地在不同软件和应用程序之间交换数据。
灵活性:通常用逗号作为分隔符,也可以根据需求选择其他字符作为分隔符。

实现代码

通用代码

第一步:导入依赖

pom.xml

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.70</version>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.10</version>
</dependency>

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.7.1</version>
</dependency>

第二步:配置请求模板

RestTemplateConfig.java

package com.lm.system.config;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.TimeUnit;

/**
 * @author DUHAOLIN
 * @date 2024/11/8
 */
public class RestTemplateConfig {

    private static final RestTemplate restTemplate;
    private static final int RETRY_COUNT = 3;
    private static final int READ_TIMEOUT = 10000;
    private static final int CONNECTION_REQUEST_TIMEOUT = 3000;

    static {
        //长链接保持时间
        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(20, TimeUnit.SECONDS);
        //最大链接数
        manager.setMaxTotal(2 * getMaxCpuCore() + 3);
        //单路由的并发数
        manager.setDefaultMaxPerRoute(2 * getMaxCpuCore());

        HttpClientBuilder clientBuilder = HttpClients.custom();
        clientBuilder.setConnectionManager(manager);

        //开启重试
        clientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(RETRY_COUNT , true));
        HttpClient httpClient = clientBuilder.build();
        //保持长链接配置,keep-alive
        clientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
        //链接超市配置
        factory.setReadTimeout(READ_TIMEOUT);
        //连接池不够用时等待时间长度设置
        factory.setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT);
        //缓冲请求数据,POST大量数据,机器内存比较大时可以设置为true
        factory.setBufferRequestBody(true);

        restTemplate = new RestTemplate();
        restTemplate.setRequestFactory(factory);
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
    }

    public static RestTemplate getRestTemplate() {
        return restTemplate;
    }

    private static int getMaxCpuCore() {
        return Runtime.getRuntime().availableProcessors();
    }

}

写入Txt

从接口获取 Json 数据,以流的形式写入数据到 Txt 文件中。
第三步:处理 API 响应数据的业务逻辑类

JsonResourceResponseExtract.java

package com.lm.system.extract;

import com.alibaba.fastjson.JSONObject;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.ResponseExtractor;
import com.alibaba.fastjson.JSONReader;

import java.io.*;

/**
 * @author DUHAOLIN
 * @date 2024/11/7
 */
public class JsonResourceResponseExtract implements ResponseExtractor<String> {

    private final File file;

    public JsonResourceResponseExtract(File file) {
        this.file = file;
    }

    @Override
    public String extractData(ClientHttpResponse response) throws IOException {
        try (InputStream is = response.getBody()) {
            try (InputStreamReader isr = new InputStreamReader(is)) {
                try (final JSONReader reader = new JSONReader (isr)) {
                    handle(reader);
                }
            }
        }

        return "OK";
    }

    private void handle(JSONReader reader) throws IOException {
        reader.startArray(); //打开数组

        try (FileWriter fileWriter = new FileWriter(file)) {
            while(reader.hasNext()) {
                reader.startObject(); //打开大括号
                JSONObject jsonObject = new JSONObject(); //存储对象

                while (reader.hasNext()) {
                    //一个属性只能获取一次,如果多次用到需要保存起来
                    String key = reader.readString();
                    if ("id".equals(key)) {
                        jsonObject.put("user_id", reader.readObject());
                    }
                    else if (key.contains("_time")) {
                        String jsonKey = key.replace("_time", "_date");
                        jsonObject.put(jsonKey, reader.readObject());
                    }
                    else {
                        jsonObject.put(key, reader.readObject());
                    }
                }

                fileWriter.write(jsonObject + "\n");
                reader.endObject(); //关闭大括号
            }
        }

        reader.endArray(); //关闭数组
    }

}

第四步:访问类和用户数据

CsvController.java

package com.lm.system.controller;

import com.lm.system.config.RestTemplateConfig;
import com.lm.system.extract.JsonResourceResponseExtract;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.io.File;

/**
 * @author DUHAOLIN
 * @date 2024/11/7
 */
@RestController
public class CsvController {

    @Value("${server.port}")
    private int port;
    private final RestTemplate restTemplate = RestTemplateConfig.getRestTemplate();

    @GetMapping("downloadTxt")
    public void downloadTxt(@RequestParam String outputPath) {
        String apiUrl = "http://localhost:" + port + "/userData";
        String filename = getFilename("txt");
        File file = new File(outputPath + "/" + filename);
        restTemplate.execute(
                apiUrl,
                HttpMethod.GET,
                null,
                new JsonResourceResponseExtract(file)
        );
    }

     private String getFilename(String format) {
        return "userData_" + System.currentTimeMillis() + "." + format;
    }

    @GetMapping("userData")
    public String getUserData() {
        return  "[\n" +
                "    {\n" +
                "      \"id\": 1,\n" +
                "      \"name\": \"Tom\",\n" +
                "      \"age\": 18,\n" +
                "      \"gender\": \"男\",\n" +
                "      \"create_time\": \"2024-08-21 16:47:45\",\n" +
                "      \"update_time\": \"2024-08-21 16:47:45\"\n" +
                "    },\n" +
                "    {\n" +
                "      \"id\": 2,\n" +
                "      \"name\": \"Joe\",\n" +
                "      \"age\": 20,\n" +
                "      \"gender\": \"女\",\n" +
                "      \"create_time\": \"2024-08-21 16:47:58\",\n" +
                "      \"update_time\": \"2024-08-21 16:47:58\"\n" +
                "    },\n" +
                "    {\n" +
                "      \"id\": 3,\n" +
                "      \"name\": \"Jim\",\n" +
                "      \"age\": 33,\n" +
                "      \"gender\": \"女\",\n" +
                "      \"create_time\": \"2024-08-21 16:48:12\",\n" +
                "      \"update_time\": \"2024-08-21 16:48:12\"\n" +
                "    }\n" +
                "]";
    }

}

第五步:如果有全局异常捕获的话可以加上

GlobalExceptionHandler.java

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public String handler(MissingServletRequestParameterException e,HttpServletRequest request) {
        log.error("400-缺少必要查询参数,{},{}", e.getMessage(), request.getServletPath());
        e.printStackTrace();
        return ResultBody
                .build(HttpStatus.BAD_REQUEST)
                .setMsg("缺少必要查询参数," + e.getParameterName())
                .getReturn();
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({ResourceAccessException.class})
    public String handler(ResourceAccessException e,HttpServletRequest request) {
        log.error("400-该磁盘位置被拒绝访问,{},{}", e.getMessage(), request.getServletPath());
        e.printStackTrace();
        return ResultBody
                .build(HttpStatus.BAD_REQUEST)
                .setMsg("该磁盘位置被拒绝访问")
                .getReturn();
    }

写入Csv

第三步:添加不输出双引号的构造方法

CsvWriter.java

package com.lm.system.writer;

import com.opencsv.CSVWriter;

import java.io.Writer;

/**
 * @author DUHAOLIN
 * @date 2024/11/8
 */
public class CsvWriter extends CSVWriter {

    public CsvWriter(Writer writer) {
        super(writer);
    }

    /**
     * 字符串不输出双引号
     */
    public CsvWriter(Writer writer, char separator) {
        super(writer, separator, '\u0000', '\u0000', "\n");
    }

    public CsvWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) {
        super(writer, separator, quotechar, escapechar, lineEnd);
    }

}

第四步

StringArrayResponseExtract.java

package com.lm.system.extract;

import com.lm.system.writer.CsvWriter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author DUHAOLIN
 * @date 2024/11/8
 */
public class StringArrayResponseExtract {

    private final File file;
    private static final String[] HEAD = new String[] { "id", "name", "age", "gender", "create_time", "update_time" };
    private static final List<String[]> DATA = getData() ;

    public StringArrayResponseExtract(File file) {
        this.file = file;
    }

    public void handler() throws IOException {
        try (FileWriter fileWriter = new FileWriter(file)) {
//            try (CsvWriter csvWriter = new CsvWriter(fileWriter)) {
            try (CsvWriter csvWriter = new CsvWriter(fileWriter, ',')) {
                csvWriter.writeNext(HEAD);
                for (String[] data : DATA) {
                    csvWriter.writeNext(data);
                }
            }
        }
    }


    private static List<String[]> getData() {
        List<String[]> list = new ArrayList<>();
        list.add(new String[] { "1", "Tom", "18", "男", "2024-08-21 16:47:45", "2024-08-21 16:47:45" });
        list.add(new String[] { "2", "Joe", "20", "女", "2024-08-21 16:47:58", "2024-08-21 16:47:58" });
        list.add(new String[] { "3", "Jim", "18", "女", "2024-08-21 16:48:12", "2024-08-21 16:48:12" });
        return list;
    }

}

第五步

CsvController.java

    @GetMapping("downloadCsv")
    public void downloadCsv(@RequestParam String outputPath) throws IOException {
        String filename = getFilename("csv");
        File file = new File(outputPath + "/" + filename);
        new StringArrayResponseExtract(file).handler();
    }
    
    private String getFilename(String format) {
        return "userData_" + System.currentTimeMillis() + "." + format;
    }

读取Txt

CsvController.java

	@GetMapping("readTxtStream") 
	public String readTxtStream(@RequestParam String path) throws IOException {
	    FileSystemResourceLoader resourceLoader = new FileSystemResourceLoader();
        InputStream is = resourceLoader.getResource(path).getInputStream();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
            //return reader.lines().collect(Collectors.joining(System.lineSeparator()));
            while (reader.read() != -1) {
                System.out.println(reader.readLine());
            }
        }
	}

    @GetMapping("readTxt")
    public String readTxt(@RequestParam String inputPath) throws IOException {
        StringBuilder sb = new StringBuilder();
        Files.lines(Paths.get(inputPath)).forEach(l -> {
            sb.append(l);
            sb.append("\n");
        });
        return sb.toString();
    }

读取Csv

CsvController.java

    @GetMapping("readCsv")
    public String readCsv(@RequestParam String inputPath) throws IOException, CsvException {
        File file = new File(inputPath);
        FileReader fileReader = new FileReader(file);
        CSVReader csvReader = new CSVReader(fileReader);
        List<String[]> list = csvReader.readAll();
        StringBuilder sb = new StringBuilder();
        for (String[] data : list) {
            sb.append(String.join(",", data));
            sb.append("\n");
        }
        return sb.toString();
    }

效果图

写入Txt文件

在这里插入图片描述

读取Txt文件

在这里插入图片描述

写入Csv文件

在这里插入图片描述

读取Csv文件

在这里插入图片描述

项目结构图

在这里插入图片描述

Spring Boot 是一个开源框架,它简化了使用 Java 开发生产级应用程序的过程。它基于 Spring 框架构建,提供了快速的初始设置配置,使得开发者能够更快地启动部署应用。Spring Boot 支持自动配置、嵌入式 Web 容器(如 Tomcat)、健康检查、 Actuator 工具等,旨在提供“开箱即用”的体验。 CSV 文件解析在 Spring Boot 中通常通过第三方库如 Apache Commons CSV 或者 openCSV 来处理。这些库提供了简单易用的方法来读取写入操作 CSV 文件,它们支持分隔符自定义、数据类型转换等功能。以下是一个简单的例子: 1. 添加依赖:在 `pom.xml` 或者 `build.gradle` 中添加 CSV 库的依赖,如 Apache Commons CSV: ```xml <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> <version>1.8</version> </dependency> ``` 2. 读取 CSV 文件: ```java import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import java.io.FileReader; import java.io.IOException; import java.util.Iterator; public class CsvReader { public static List<CSVRecord> readCsv(String filePath) throws IOException { CSVParser parser = CSVParser.parse(new FileReader(filePath), CSVFormat.DEFAULT.withFirstRecordAsHeader()); return parser.iterator(); } } ``` 3. 解析数据: ```java import org.apache.commons.csv.CSVRecord; public class CsvDataHandler { public List<MyObject> parseCsvToObjects(List<CSVRecord> records) { List<MyObject> objects = new ArrayList<>(); for (CSVRecord record : records) { MyObject obj = new MyObject(); obj.setField1(record.get("field1")); obj.setField2(record.get("field2")); // 假设 field1 field2 是列名 objects.add(obj); } return objects; } } class MyObject { private String field1; private String field2; // getters and setters } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值