Spring boot 如何为每个区块创建多个文件(csv)?
嗨,我是SpringBatch的新手,我想为每个处理的区块创建多个文件(csv)。文件名类似于timestamp.csv。 知道我该怎么做吗?基本上,它是将一个大文件拆分为更小的文件Spring boot 如何为每个区块创建多个文件(csv)?,spring-boot,spring-batch,Spring Boot,Spring Batch,嗨,我是SpringBatch的新手,我想为每个处理的区块创建多个文件(csv)。文件名类似于timestamp.csv。 知道我该怎么做吗?基本上,它是将一个大文件拆分为更小的文件 谢谢大家! CSV文件基本上是文本文件,末尾有一个新行字符 因此,就将一个大的CSV文件拆分为较小的文件而言,您只需在Java中逐行读取大文件,,当您的读取行数达到阈值计数/每个小文件的最大计数(10、100、1000等),您就可以根据需要创建一个具有命名约定的新文件,并在其中转储数据 BufferedReade
谢谢大家! CSV文件基本上是文本文件,末尾有一个新行字符 因此,就将一个大的CSV文件拆分为较小的文件而言,您只需在Java中逐行读取大文件,,当您的读取行数达到阈值计数/每个小文件的最大计数(10、100、1000等),您就可以根据需要创建一个具有命名约定的新文件,并在其中转储数据
BufferedReader
是逐行读取文本文件的主类
实现此逻辑与Spring批处理无关,但可以使用Java或操作系统级命令
因此,您有两个不同的逻辑部分,逐行读取大文件并创建csv…您可以将这两个部分作为单独的组件开发,并根据您的业务需求在适当的位置将其插入Spring Batch Framework
有一个java库可以轻松地处理CSV文件&您可能会喜欢使用它,这取决于所涉及的复杂性
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>4.6</version>
</dependency>
com.opencsv
opencsv
4.6
在spring batch中使用分区器
,有关实施细节,请查看
查看API文档我会使用命令行实用程序,比如
split
命令(或等效命令),或者尝试使用纯Java来实现(请参阅)
但是,如果您真的想使用Spring批处理,那么您可以使用以下方法:
import java.time.LocalDateTime;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.mapping.PassThroughLineMapper;
import org.springframework.batch.item.file.transform.PassThroughLineAggregator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
@Configuration
@EnableBatchProcessing
public class MyJob {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
public MyJob(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory;
}
@Bean
public FlatFileItemReader<String> itemReader() {
return new FlatFileItemReaderBuilder<String>()
.name("flatFileReader")
.resource(new FileSystemResource("foos.txt"))
.lineMapper(new PassThroughLineMapper())
.build();
}
@Bean
public ItemWriter<String> itemWriter() {
final FlatFileItemWriter<String> writer = new FlatFileItemWriter<>();
writer.setLineAggregator(new PassThroughLineAggregator<>());
writer.setName("chunkFileItemWriter");
return items -> {
writer.setResource(new FileSystemResource("foos" + getTimestamp() + ".txt"));
writer.open(new ExecutionContext());
writer.write(items);
writer.close();
};
}
private String getTimestamp() {
// TODO tested on unix/linux systems, update as needed to not contain illegal characters for a file name on MS windows
return LocalDateTime.now().toString();
}
@Bean
public Step step() {
return stepBuilderFactory.get("step")
.<String, String>chunk(3)
.reader(itemReader())
.writer(itemWriter())
.build();
}
@Bean
public Job job() {
return jobBuilderFactory.get("job")
.start(step())
.build();
}
public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
Job job = context.getBean(Job.class);
jobLauncher.run(job, new JobParameters());
}
}
该示例将每个区块写入一个带有时间戳的单独文件中:
文件1foos2019-11-28009:23:47.769.txt
:
foo1
foo2
foo3
foo4
foo5
foo6
文件2foos2019-11-28009:23:47.779.txt
:
foo1
foo2
foo3
foo4
foo5
foo6
顺便说一句,我认为最好使用数字而不是时间戳
注意:对于这样的用例,我不太关心可重启性。因此,在启动区块(读->处理->写)逻辑之前,您希望将大文件拆分为小文件&一个小文件被输入step的区块处理器?Hi@SabirKhan,1个文件(实际上是xlsx),包含大约600k-800k条记录。由于文件太大,无法处理,我必须首先将其拆分为包含100k条记录的csv文件(块)。因此,这部分问题被问及(文件拆分)看起来更像是一个作业预处理,而不是实际的作业逻辑,所以您可以很好地在中编写自定义拆分器逻辑,然后在所有文件上为拆分文件的目录设置实际作业。为了高效执行,分区在这里似乎是一个用例。您好,只是一个简单的问题-传递给call-
writer.open(new ExecutionContext())
的ExecutionContext
的目的是什么?是否可以重复使用或每次都需要创建新的?此外,Windows上通过调用创建的文件名存在问题-LocalDateTime.now()
,因为它包含不允许的字符冒号,java.nio.file.InvalidPathException:索引处的非法字符
可以重用执行上下文。对于时间戳,是的,如果它在windows上导致文件名包含非法字符的值,则可能会出现问题(在发布答案之前,我在mac os上对其进行了测试)。我相应地更新了答案,并将此详细信息留给了用户。Hi@MahmoudBenHassine我得到的错误原因是:java.lang.IllegalArgumentException:必须设置资源。实际上现在我只是在这里用ClassifierCompositeItemWriter做了一些解决方案,@Lp.Don不,你不应该,确保你的资源不是null
。我用一个完整的例子更新了答案。这种步骤可以用作分区步骤的准备任务(与使用带有非可移植操作系统特定命令的SystemCommandTasklet
相比,这实际上是一种可移植的文件拆分方法)@Sabir Khan我应该提到,如果你想重新使用执行上下文,你需要在每次迭代后清除它,但这不是什么大问题,它无论如何都会被GCed。