【问题标题】:How to create multiple files (csv) per chunk?如何为每个块创建多个文件(csv)?
【发布时间】:2019-11-26 08:28:39
【问题描述】:

您好,我是 Spring Batch 的新手,我想为每个处理的块创建多个文件 (csv)。 FileName 类似于 timestamp.csv。 知道我该怎么做吗?基本上它是将一个大文件拆分为较小的文件。

谢谢!

【问题讨论】:

  • 所以您希望在启动块(读取->处理->写入)逻辑之前将大文件拆分成较小的文件,并将一个小文件输入到步骤的块处理器?
  • 嗨 @SabirKhan,1 个文件(实际上是 xlsx)包含大约 600k-800k 记录。由于文件太大而无法处理,我必须先将其拆分为包含 10 万条记录(块)的 csv 文件。
  • 所以这部分被问到的问题(文件拆分)在我看来更像是 作业预处理,而不是实际的作业逻辑,因此您可以很好地编写自定义拆分器逻辑JobExecutionListenerSupport.beforeJob & 然后在所有文件上为拆分文件的目录设置实际作业。为了高效执行 - 分区似乎是一个用例。

标签: spring-boot spring-batch


【解决方案1】:

CSV 文件基本上是末尾带有换行符的文本文件。

因此,就将大 CSV 文件拆分为较小的文件而言,您只需在 Java 中 逐行 读取大文件,并且当您的读取行数达到阈值计数/最大计数时小文件(10、100、1000 等),您可以根据需要创建一个具有命名约定的新文件并在那里转储数据。

How to read a large text file line by line using Java?

BufferedReader 是逐行读取文本文件的主类。

实现此逻辑与 Spring Batch 无关,但可以使用 Java 或使用 OS 级别的命令。

因此,您有两个不同的逻辑部分,逐行读取大文件并创建 csv ...您可以将这两个部分开发为单独的组件,并根据您的业务需求将其插入 Spring Batch Framework 的适当位置。

有一个 java 库可以轻松处理 CSV 文件,您可能会喜欢使用它 - 取决于所涉及的复杂性。

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

【讨论】:

    【解决方案2】:

    我会使用类似split 命令(或等效命令)之类的命令行实用程序,或者尝试使用纯Java 来执行此操作(请参阅Java - Read file and split into multiple files)。

    但如果你真的想用 Spring Batch 来做,那么你可以使用类似的东西:

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

    文件foos.txt如下:

    foo1
    foo2
    foo3
    foo4
    foo5
    foo6
    

    该示例会将每个块写入带有时间戳的单独文件中:

    文件1foos2019-11-28T09:23:47.769.txt:

    foo1
    foo2
    foo3
    

    文件2foos2019-11-28T09:23:47.779.txt:

    foo4
    foo5
    foo6
    

    我认为最好使用数字而不是时间戳 BTW。

    注意:对于这种用例,我不会太在意可重启性。

    【讨论】:

    • 嗨,只是一个简单的问题 - 传递给调用的 ExecutionContext 的目的是什么 - writer.open(new ExecutionContext()) ?这可以重复使用还是每次都需要创建新的?此外,Windows 上通过调用创建的文件名存在问题 - LocalDateTime.now() 因为它包含不允许的字符冒号,java.nio.file.InvalidPathException: Illegal char &lt;:&gt; at index
    • 执行上下文可以重复使用。对于时间戳,是的,如果它导致 Windows 上的文件名包含非法字符的值,这可能是一个问题(我在发布答案之前在 mac os 上对其进行了测试)。我相应地更新了答案,并将这个细节留给了用户。
    • 嗨@MahmoudBenHassine 我收到错误原因:java.lang.IllegalArgumentException:必须设置资源。实际上现在我只是从这里stackoverflow.com/questions/15974458/… 使用 ClassifierCompositeItemWriter 做了一些解决方案,
    • @Lp.Don 不,你不应该这样做,确保你的资源不是null。我用一个完整的例子更新了答案。这种步骤可以用作分区步骤的准备任务(与使用带有非便携式操作系统特定命令的SystemCommandTasklet 相比,这实际上是一种拆分文件的便携式方式)。 @Sabir Khan 我应该提一下,如果你想重用执行上下文,你需要在每次迭代后清除它,但这没什么大不了的,反正它会被 GCed。
    【解决方案3】:

    在spring批处理中使用Partitioner实现细节请查看

    1. this article
    2. or this

    并查看 API 文档here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-16
      • 2020-06-18
      • 1970-01-01
      • 1970-01-01
      • 2021-07-17
      • 1970-01-01
      • 2021-02-07
      • 2020-12-21
      相关资源
      最近更新 更多