当前位置: 首页 > 知识库问答 >
问题:

FlatFileItemWriter应写入与输入文件同名的输出文件

晁开宇
2023-03-14

阅读器bean

<bean id="multiResourceReader" class="org.springframework.batch.item.file.MultiResourceItemReader" scope="step">
  <property name="resources" value="file:#{jobParameters['cwd']}/#{jobParameters['inputFolder']}/file-*.txt" />
  <property name="delegate" ref="itemReader" />
</bean>
<bean id="itemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">

        <property name="lineMapper">
          <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">

            <property name="lineTokenizer">
              <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
                <property name="delimiter" value="," />
                <property name="names" value="paramA,paramB" />
              </bean>
            </property>
            <property name="fieldSetMapper" >
              <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
                <property name="targetType" value="a.b.c.d.MyObject" />
              </bean>
            </property>
          </bean>
        </property>
    </bean>

我尝试这样做:在FlatFileItemReader的实现中,将输入文件路径作为string属性添加到ExecutionContext的映射中。在我的FlatFileItemWriter实现中-重写setResource,并实际从ExecutionContext的值中创建一个Resource对象。这样行吗?

共有1个答案

童子明
2023-03-14

我认为这个解决方案可能会产生错误的输出,因为MultiResourceItemReader从第一个文件读取一堆项,如果这个集合与您的完成策略不匹配,它会从下一个资源读取;在这种情况下,您将拥有来自两个不同源的项,而ItemReaderbean未同步写入正确的输出资源。
您可以尝试解决让a.B.C.D.MyObject实现org.springframework.batch.item.ResourceAware并编写一个自定义的多资源项编写器,以及在资源更改时管理资源的打开/关闭(该解决方案并不简单,因为当然需要状态保存才能重新启动)。

class MyItemWriter implements ItemWriter<MyObject>,ItemStream {
  private FlatFileItemWriter delegate;
  private String resourceName;

  public open(ExecutionContext e) {
    resourceName = e.getString("resourceName");
    delegate.open(e);
  }
  public close(ExecutionContext e) {
    e.putString("resourceName",resourceName);
    delegate.close(e);
  }
  public update(ExecutionContext e) {
    e.putString("resourceName",resourceName);
    delegate.update(e);
  }
  public void write(List<? extends MyObject> items) {
    for(final MyObject o : items) {
      String itemResource = o.getResource().toString();
      if(!itemResource.equals(resourceName)) {
        closeDelegate();
        openDelegate();
      }
      writeToDelegate(o);
    }
  }
}

当然,这是没有经过测试的,它只是一个想法。
另一种方法是很不同的,用这样的方法模拟一个使用决定器的循环:

  1. 创建一个bean(例如,称为resourceholder),用于存储文件中的资源:#{jobparameters['cwd']}/#{jobparameters['input folder']}/file-*.txt;这个bean不存储到执行上下文中,而是在每个作业启动时用JobExecutionListener填充,因为执行上下文应该保持较小。
  2. 使用判定器来模拟循环!从决策器存储到执行上下文中的当前资源的名称;您还需要存储当前资源的索引,以便移动到下一个资源或停止作业
  3. 读取/处理/写入步骤使用一个文件,该文件存储在Decider的执行上下文中
<batch:job id="job">
  <batch:decision decider="decider" id="decider">
    <batch:next on="CONTINUABLE" to="readWriteStep" />
    <batch:end on="FINISHED" />
  </batch:decision>
  <batch:step id="readWriteStep" reader="itemReader" write="itemWriter" next="decider" />
  <batch:listeners>
    <batch:listener class="ResourceHolderFiller">
      <property name="resources" value="file:#{jobParameters['cwd']}/#{jobParameters['inputFolder']}/file-*.txt" />
      <property name="resourceHolderBean" ref="resourceHolderBean" />
    </batch:listener>
  </batch:listeners>
</batch:job>
<bean id="resourceHolder" class="ResourceHolderBean" />
<bean id="decider" class="MyDecider">
  <property name="resourceHolderBean" ref="resourceHolderBean" />
</bean>

class ResourceHolderBean
{
  Resource[] resource;

  public void setResource(Resource[] resource) {...}
  public Resource[] getResource() {...}
}
class MyDecider implements JobExecutionDecider {
  ResourceHolderBean resourceBean;

  public void setResourceBean(ResourceHolderBean resBean){...}

  FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
    Integer resourceIndex = jobExecution.getExecutionContext().getInt("resourceIndex");
    if(resourceIndex == null)
    {
      resourceIndex = 0;
    }
    if(resourceIndex == resourceBean.getResources().size()) {
      return new FlowExecutionStatus(RepeatStatus.FINISHED.name());
    }
    else {
      jobExecution.getExecutionContext().putInt("resourceIndex",resourceIndex);
      // This is the key your reader/writer must use as inpu/output filename
      jobExecution.getExecutionContext().putString("resourceName",resourceBean.getResources()[resourceIndex].toString());
    }
  }
}

ResourceHolderFiller是将资源注入ResourceHolderBean bean的作业侦听器。

希望能有所帮助。

 类似资料:
  • 文件 std::fs::File 本身实现了 Read 和 Write trait,所以文件的输入输出非常简单,只要得到一个 File 类型实例就可以调用读写接口进行文件输入与输出操作了。而要得到 File 就得让操作系统打开(open)或新建(create)一个文件。还是拿例子来说明 use std::io; use std::io::prelude::*; use std::fs::File;

  • 主要内容:puts 语句,实例,gets 语句,实例,putc 语句,实例,print 语句,实例,打开和关闭文件,语法,读取和写入文件,实例,实例,实例,实例,实例,重命名和删除文件,实例,实例,文件模式与所有权,实例,文件查询,实例,实例,实例,实例,实例,实例,实例,实例,Ruby 中的目录,创建文件 & 临时目录,内建函数Ruby 提供了一整套 I/O 相关的方法,在内核(Kernel)模块中实现。所有的 I/O 方法派生自 IO 类。 类 IO 提供了所有基础的方法,比如 read、

  • 目录表 文件 使用文件 储存器 储存与取储存 概括 在很多时候,你会想要让你的程序与用户(可能是你自己)交互。你会从用户那里得到输入,然后打印一些结果。我们可以分别使用raw_input和print语句来完成这些功能。对于输出,你也可以使用多种多样的str(字符串)类。例如,你能够使用rjust方法来得到一个按一定宽度右对齐的字符串。利用help(str)获得更多详情。 另一个常用的输入/输出类型

  • 问题内容: 所以我在bash上运行它: 该命令的作用是一直保持运行状态,并且每当我的设备发送某个数据时,例如说它检测到温度变化,它就会输出类似的内容 而且这只会继续运行,并且一旦有任何原因就会输出一些东西。因此,执行无止境。 没有回声运行完美,但是当我尝试使用’>’运算符时,这似乎没有写入文件。 例如 这不能正常工作,my_record_file只获取 间隔 写入的数据,但是我想立即写入。 有任何

  • 我想知道如何将输出写入与show()函数显示的输出相同的文本文件。例如,当我执行此代码时: 输出: 当我执行这段代码时: 输出: 所以,当我试图使用以下代码将此输出写入文本文件时: 文本文件中的输出与“System.out.println(p.toString());”显示的输出相同。 那么,如何将show()函数显示的相同输出写入文本文件? 完整代码:

  • 问题内容: 在我的机器上,我有一些软件可以在终端中接收命令并返回值列表。 要运行它,我必须输入以下内容: 我正在尝试将其作为python程序的一部分运行。当我运行以下命令时: 然后我得到想要返回到终端的值(其中epoch_name是文件名的变量名)。但是,当我尝试将结果写入文件时: 产生了文件123.txt,但它为空。 我知道我放错了“和/或’字符,但是我不知道它们应该去哪里。 任何帮助将不胜感激