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

仓库

https://gitee.com/Lin_DH/system

介绍

简介

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

优点

简单性:Csv 文件易于创建和编辑,可以使用任何文本编辑器打开和修改。

兼容性:格式简单且广泛支持,可以轻松地在不同软件和应用程序之间交换数据。

灵活性:通常用逗号作为分隔符,也可以根据需求选择其他字符作为分隔符。

实现代码

通用代码

第一步:导入依赖

pom.xml

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

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

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

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

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

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

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

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

java 复制代码
    @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

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文件

项目结构图

相关推荐
m51271 分钟前
LinuxC语言
java·服务器·前端
IU宝6 分钟前
C/C++内存管理
java·c语言·c++
瓜牛_gn7 分钟前
依赖注入注解
java·后端·spring
hakesashou8 分钟前
Python中常用的函数介绍
java·网络·python
佚先森17 分钟前
2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程
java·html
Estar.Lee24 分钟前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪25 分钟前
Django:从入门到精通
后端·python·django
一个小坑货26 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet2730 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust
古月居GYH30 分钟前
在C++上实现反射用法
java·开发语言·c++