该版本仍在开发中,尚未被视为稳定。对于最新的稳定版本,请使用 Spring Integration 7.0.0spring-doc.cadn.net.cn

写入文件

要向文件系统写消息,可以使用文件写入消息处理器. 该类可处理以下有效载荷类型:spring-doc.cadn.net.cn

对于字符串有效载荷,你可以配置编码和字符集。spring-doc.cadn.net.cn

为了简化作,你可以配置文件写入消息处理器作为出站通道适配器或出站网关的一部分,通过使用XML命名空间实现。spring-doc.cadn.net.cn

从4.3版本开始,你可以指定写入文件时使用的缓冲区大小。spring-doc.cadn.net.cn

从5.1版本开始,你可以提供BiConsumer<File, Message<?>> newFile回调如果你用FileExistsMode.APPENDFileExistsMode.APPEND_NO_FLUSH并且需要创建一个新文件。这个回调接收一个新创建的文件及其触发的消息。例如,这个回调可以用来写出在消息头中定义的CSV头。spring-doc.cadn.net.cn

生成文件名

在最简单的形式中,文件写入消息处理器只需一个目的目录来写入文件。要写入的文件名称由处理器的文件名生成器. 默认实现会寻找键与定义为FileHeaders.FILENAME.spring-doc.cadn.net.cn

或者,你也可以指定一个表达式,针对消息进行评估以生成文件名——例如,headers['myCustomHeader'] + '.something'. 该表达式必须取值为字符串. 为了方便起见,DefaultFileNameGenerator还提供setHeaderName方法,允许你明确指定将用作文件名的消息头。spring-doc.cadn.net.cn

一旦设置好,DefaultFileNameGenerator采用以下解析步骤来确定给定消息负载的文件名:spring-doc.cadn.net.cn

  1. 对消息进行表达式的评估,如果结果非空,则字符串,使用它作为文件名。spring-doc.cadn.net.cn

  2. 否则,如果有效载荷是java.io.file,使用文件对象的文件名。spring-doc.cadn.net.cn

  3. 否则,使用带有 . 的消息 ID 。味精作为文件名。spring-doc.cadn.net.cn

当你使用 XML 命名空间支持时,文件出站通道适配器和文件出站网关都支持以下互斥的配置属性:spring-doc.cadn.net.cn

写入文件时,会使用临时文件后缀(默认为。写作). 在写入文件时,它会被附加到文件名上。要自定义后缀,你可以设置临时文件后缀在文件外站通道适配器和文件外站网关上都设置了属性。spring-doc.cadn.net.cn

当使用附加文件模式临时文件后缀属性被忽略,因为数据直接附加到文件中。

从4.2.5版本开始,生成的文件名(由于文件名生成器文件名生成器表达式evaluation)可以表示子路径和目标文件名。它被用作 的第二个构造子参数文件(文件父,字符串子)如故。 然而,过去我们并没有创造(MKDIRS()) 用于子路径的目录,仅假设文件名。这种方法适用于需要恢复文件系统树以匹配源目录的情况——例如解压归档并按原始顺序保存目标目录中的所有文件。spring-doc.cadn.net.cn

指定输出目录

文件出站通道适配器和文件出站网关都提供了两个互斥的配置属性来指定输出目录:spring-doc.cadn.net.cn

Spring Integration 2.2 引入了目录表达式属性。

使用目录属性

当你使用目录属性,输出目录被设置为固定值,当文件写入消息处理器已初始化。如果您未指定该属性,必须使用目录表达式属性。spring-doc.cadn.net.cn

使用目录表达式属性

如果你想获得完整的 SpEL 支持,可以使用目录表达式属性。 该属性接受针对每个正在处理消息的 SpEL 表达式进行评估。因此,当你动态指定输出文件目录时,你可以完全访问消息的有效载荷及其头部。spring-doc.cadn.net.cn

SpEL表达式必须解析为字符串,java.io.fileorg.springframework.core.io.Resource. (后者被评估为文件总之。) 此外,得到的字符串文件必须指向目录。 如果你没有具体说明目录表达式属性,然后你必须设置目录属性。spring-doc.cadn.net.cn

使用自动创建目录属性

默认情况下,如果目标目录不存在,相应的目标目录和不存在的父目录会自动被创建。 为了防止这种情况,你可以设置自动创建目录归属为false. 这一属性适用于目录以及目录表达式属性。spring-doc.cadn.net.cn

当使用目录属性和自动创建目录false,从Spring Integration 2.2开始,进行了以下改动:spring-doc.cadn.net.cn

现在,不再在适配器初始化时检查目标目录的存在,而是对每个正在处理的消息进行检查。spring-doc.cadn.net.cn

此外,如果自动创建目录true在处理消息之间删除目录后,每条待处理消息都会重新创建该目录。spring-doc.cadn.net.cn

处理现有的目的地文件

当你写入文件且目标文件已经存在时,默认行为是覆盖该目标文件。 你可以通过设置模式在相关文件的 Outbound 组件上设置属性。 存在以下选项:spring-doc.cadn.net.cn

Spring Integration 2.2 引入了模式属性和附加,失败忽视选项。
取代

如果目标文件已经存在,则会被覆盖。 如果模式属性未指定,这是写入文件时的默认行为。spring-doc.cadn.net.cn

REPLACE_IF_MODIFIED

如果目标文件已经存在,只有当最后修改的时间戳与源文件的时间戳不同时,才会被覆盖。 为文件有效载荷,有效载荷最后修改时间与现有文件进行比较。 对于其他有效载荷,FileHeaders.SET_MODIFIED (file_setModified)头部与现有文件进行比较。 如果头部缺失或有非,文件总是被替换。spring-doc.cadn.net.cn

附加

这个模式允许你在现有文件上添加消息内容,而不是每次都创建新文件。 注意,该属性与临时文件后缀属性,因为当适配器将内容附加到现有文件时,适配器不再使用临时文件。 每条消息后,文件就会关闭。spring-doc.cadn.net.cn

APPEND_NO_FLUSH

这个选项的语义与附加但数据不会被刷新,文件也不会在每次消息后关闭。 这可以在故障时数据丢失的风险下提供显著性能。 看使用时冲洗文件APPEND_NO_FLUSH更多信息请见。spring-doc.cadn.net.cn

失败

如果目标文件存在,则消息处理异常被抛出。spring-doc.cadn.net.cn

忽视

如果目标文件存在,消息有效载荷会被静默忽略。spring-doc.cadn.net.cn

使用临时文件后缀时(默认为。写作),忽视如果最终文件名或临时文件名存在,则该选项适用。

使用时冲洗文件APPEND_NO_FLUSH

APPEND_NO_FLUSH模式在4.3版本中加入。 使用它可以提升性能,因为文件不会在每次消息后关闭。 然而,一旦发生故障,这可能导致数据丢失。spring-doc.cadn.net.cn

Spring Integration提供了几种冲洗策略以减轻数据丢失:spring-doc.cadn.net.cn

  • 同花区间. 如果文件在这段时间内未被写入,则会自动清除。 这是近似值,可能为1.33倍这次(平均值为1.167x).spring-doc.cadn.net.cn

  • 向消息处理程序发送包含正则表达式的消息触发方法。 具有绝对路径名且与模式匹配的文件会被清除。spring-doc.cadn.net.cn

  • 为作者提供自定义MessageFlush谓词实现以修改发送消息时所采取的作触发方法。spring-doc.cadn.net.cn

  • 召唤其中一位控者flushIfNeeded通过传递自定义方法来实现FileWritingMessageHandler.FlushPredicateFileWritingMessageHandler.MessageFlushPredicate实现。spring-doc.cadn.net.cn

每个打开的文件调用谓词。 有关这些接口的更多信息,请参见 Javadoc。 注意,自5.0版本起,谓词方法提供了另一个参数:当前文件首次写入的时间(如果新文件或之前关闭)。spring-doc.cadn.net.cn

使用同花区间,区间从最后一次写入开始。 只有当文件在该间隔内处于空闲状态时才会被冲洗。 从4.3.7版本开始,新增了一个属性(冲洗无动时)可以设置为false,意味着该间隔从对之前冲洗(或新)文件的首次写入开始。spring-doc.cadn.net.cn

文件时间戳

默认情况下,目标文件的最后修改时间戳是文件创建的时间(但原地重命名保留当前时间戳)。 从4.3版本开始,你现在可以配置保留时间戳(或setPreserveTimestamp(true)当使用 Java 配置时)。 为文件有效载荷,这会将时间戳从入站文件传输到出站文件(无论是否需要副本)。 对于其他有效载荷,如果FileHeaders.SET_MODIFIED头部 (file_setModified)存在,用于设置目标文件的最后修改只要头部是.spring-doc.cadn.net.cn

文件权限

从版本5.0开始,当文件写入支持 Posix 权限的文件系统时,你可以在出站通道适配器或网关上指定这些权限。 该性质是一个整数,通常以熟悉的八进制格式提供——例如,0640意味着拥有者拥有读写权限,组拥有只读权限,其他人则无访问权限。spring-doc.cadn.net.cn

文件出站通道适配器

以下示例配置了一个文件出站通道适配器:spring-doc.cadn.net.cn

<int-file:outbound-channel-adapter id="filesOut" directory="${input.directory.property}"/>

基于命名空间的配置还支持删除源文件属性。 如果设置为true写入目标后,它会触发原始源文件的删除。 该标志的默认值为false. 以下示例展示了如何将其设置为true:spring-doc.cadn.net.cn

<int-file:outbound-channel-adapter id="filesOut"
    directory="${output.directory}"
    delete-source-files="true"/>
删除源文件属性只有在入站消息具有文件有效载荷或如果FileHeaders.ORIGINAL_FILE头部值包含源代码文件实例或字符串表示原始文件路径。

从4.2版本开始,文件写入消息处理器支撑一个加新行选择。 如果设置为true,写入消息后,文件会附加一行新行。 默认属性值为false. 以下示例展示了如何使用加新行选择:spring-doc.cadn.net.cn

<int-file:outbound-channel-adapter id="newlineAdapter"
	append-new-line="true"
    directory="${output.directory}"/>

出站网关

如果你想继续基于书面文件处理消息,可以使用出站网关相反。 它的作用类似于出站通道适配器. 然而,写入文件后,它还会将其作为消息的有效载荷发送到回复信道。spring-doc.cadn.net.cn

以下示例配置了一个出站网关:spring-doc.cadn.net.cn

<int-file:outbound-gateway id="mover" request-channel="moveInput"
    reply-channel="output"
    directory="${output.directory}"
    mode="REPLACE" delete-source-files="true"/>

如前所述,你还可以指定模式属性,定义了如何处理目标文件已存在的情况的行为。 详情请参见处理现有目的地文件。 通常,使用文件出站网关时,结果文件会作为消息有效载荷返回到回复通道。spring-doc.cadn.net.cn

这在指定忽视模式。 此时返回的是预先存在的目标文件。 如果请求消息的有效载荷是一个文件,你仍然可以通过消息头访问该原始文件。 看FileHeaders.ORIGINAL_FILEspring-doc.cadn.net.cn

“出站网关”在你想先移动文件然后通过处理流水线的情况下表现良好。 在这种情况下,你可以连接文件命名空间的入站信道适配器元素映射到出站网关然后连接那个网关回复信道到管道的起点。

如果你有更复杂的需求,或者需要支持额外的有效载荷类型作为输入转换为文件内容,你可以扩展文件写入消息处理器,但更好的选择是依赖转换器.spring-doc.cadn.net.cn

使用 Java 配置配置

以下 Spring Boot 应用程序展示了如何用 Java 配置配置入站适配器的示例:spring-doc.cadn.net.cn

@SpringBootApplication
@IntegrationComponentScan
public class FileWritingJavaApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context =
                      new SpringApplicationBuilder(FileWritingJavaApplication.class)
                              .web(false)
                              .run(args);
             MyGateway gateway = context.getBean(MyGateway.class);
             gateway.writeToFile("foo.txt", new File(tmpDir.getRoot(), "fileWritingFlow"), "foo");
    }

    @Bean
    @ServiceActivator(inputChannel = "writeToFileChannel")
    public MessageHandler fileWritingMessageHandler() {
         Expression directoryExpression = new SpelExpressionParser().parseExpression("headers.directory");
         FileWritingMessageHandler handler = new FileWritingMessageHandler(directoryExpression);
         handler.setFileExistsMode(FileExistsMode.APPEND);
         return handler;
    }

    @MessagingGateway(defaultRequestChannel = "writeToFileChannel")
    public interface MyGateway {

        void writeToFile(@Header(FileHeaders.FILENAME) String fileName,
                       @Header(FileHeaders.FILENAME) File directory, String data);

    }
}

使用 Java DSL 配置

以下 Spring Boot 应用程序展示了如何用 Java DSL 配置入站适配器的示例:spring-doc.cadn.net.cn

@SpringBootApplication
public class FileWritingJavaApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context =
                 new SpringApplicationBuilder(FileWritingJavaApplication.class)
                         .web(false)
                         .run(args);
        MessageChannel fileWritingInput = context.getBean("fileWritingInput", MessageChannel.class);
        fileWritingInput.send(new GenericMessage<>("foo"));
    }

    @Bean
   	public IntegrationFlow fileWritingFlow() {
   	    return IntegrationFlow.from("fileWritingInput")
   		        .enrichHeaders(h -> h.header(FileHeaders.FILENAME, "foo.txt")
   		                  .header("directory", new File(tmpDir.getRoot(), "fileWritingFlow")))
   	            .handle(Files.outboundGateway(m -> m.getHeaders().get("directory")))
   	            .channel(MessageChannels.queue("fileWritingResultChannel"))
   	            .get();
    }

}