仓库
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();
}