对于最新的稳定版本,请使用 Spring Integration 6.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Integration 6.3.1Spring中文文档

A 可用于使用文件系统中的文件。 这是从文件系统目录创建消息的实现。 以下示例演示如何配置:FileReadingMessageSourceMessageSourceFileReadingMessageSourceSpring中文文档

<bean id="pollableFileSource"
    class="org.springframework.integration.file.FileReadingMessageSource"
    p:directory="${input.directory}"/>

若要防止为某些文件创建消息,可以提供 . 默认情况下,我们使用以下筛选器:FileListFilterSpring中文文档

确保不处理隐藏文件。 请注意,hidden 的确切定义取决于系统。 例如,在基于 UNIX 的系统上,以句点字符开头的文件被视为隐藏。 另一方面,Microsoft Windows具有专用的文件属性来指示隐藏文件。IgnoreHiddenFileListFilterSpring中文文档

版本 4.2 引入了 . 在以前的版本中,包含隐藏文件。 在默认配置下,首先触发 ,然后触发 .IgnoreHiddenFileListFilterIgnoreHiddenFileListFilterAcceptOnceFileListFilterSpring中文文档

确保仅从目录中选取一次文件。AcceptOnceFileListFilterSpring中文文档

将其状态存储在内存中。 如果您希望状态在系统重新启动后继续有效,则可以使用 . 此筛选器将接受的文件名存储在实现中(请参阅元数据存储)。 此筛选器与文件名和修改时间匹配。AcceptOnceFileListFilterFileSystemPersistentAcceptOnceFileListFilterMetadataStoreSpring中文文档

从版本 4.0 开始,此筛选器需要 . 当与共享数据存储(例如 )一起使用时,它允许在多个应用程序实例之间共享筛选器密钥,或者在多个服务器使用的网络文件共享之间共享筛选器密钥。ConcurrentMetadataStoreRedisRedisMetadataStoreSpring中文文档

从版本 4.1.5 开始,此筛选器具有一个新属性 (),这会导致它在每次更新时刷新元数据存储(如果存储实现了 )。flushOnUpdateFlushableSpring中文文档

持久性文件列表筛选器现在具有 boolean 属性。 将此属性设置为 也会设置 ,这意味着出站网关 ( 和 ) 上的递归操作现在每次都将始终遍历整个目录树。 这是为了解决未检测到目录树深处的更改的问题。 此外,还会导致文件的完整路径用作元数据存储键;这解决了一个问题,即如果具有相同名称的文件在不同目录中多次出现,则筛选器无法正常工作。 重要说明:这意味着在顶级目录下的文件中找不到持久性元数据存储中的现有键。 因此,默认情况下,该属性是;这在将来的版本中可能会发生变化。forRecursiontruealwaysAcceptDirectorieslsmgetforRecursion=truefalseSpring中文文档

以下示例使用筛选器配置 a:FileReadingMessageSourceSpring中文文档

<bean id="pollableFileSource"
    class="org.springframework.integration.file.FileReadingMessageSource"
    p:inputDirectory="${input.directory}"
    p:filter-ref="customFilterBean"/>

读取文件的一个常见问题是,文件可能在准备就绪之前就被检测到(也就是说,其他一些进程可能仍在写入文件)。 默认值不会阻止此操作。 在大多数情况下,如果文件写入过程在准备好读取每个文件后立即重命名每个文件,则可以防止这种情况。 仅接受就绪文件(可能基于已知后缀)的 or 过滤器,使用默认 组成,允许这种情况。 启用合成,如以下示例所示:AcceptOnceFileListFilterfilename-patternfilename-regexAcceptOnceFileListFilterCompositeFileListFilterSpring中文文档

<bean id="pollableFileSource"
    class="org.springframework.integration.file.FileReadingMessageSource"
    p:inputDirectory="${input.directory}"
    p:filter-ref="compositeFilter"/>

<bean id="compositeFilter"
    class="org.springframework.integration.file.filters.CompositeFileListFilter">
    <constructor-arg>
        <list>
            <bean class="o.s.i.file.filters.AcceptOnceFileListFilter"/>
            <bean class="o.s.i.file.filters.RegexPatternFileListFilter">
                <constructor-arg value="^test.*$"/>
            </bean>
        </list>
    </constructor-arg>
</bean>

如果无法使用临时名称创建文件并重命名为最终名称,则 Spring Integration 提供了另一种选择。 版本 4.2 添加了 . 可以使用属性配置此筛选器,以便筛选器仅传递早于此值的文件。 期限默认为 60 秒,但您应该选择一个足够大的期限,以避免过早拿起文件(例如,由于网络故障)。 以下示例演示如何配置:LastModifiedFileListFilterageLastModifiedFileListFilterSpring中文文档

<bean id="filter" class="org.springframework.integration.file.filters.LastModifiedFileListFilter">
    <property name="age" value="120" />
</bean>

从版本 4.3.7 开始,引入了 a( 的扩展),以允许后续筛选器只能看到上一个筛选器的结果。 (使用 ,所有筛选器都可以看到所有文件,但它只传递已通过所有筛选器的文件)。 需要新行为的一个示例是 和 的组合,当我们不希望在经过一段时间之前接受该文件时。 使用 ,因为 在第一次传递时会看到所有文件,因此当另一个过滤器这样做时,它不会稍后传递它。 当模式筛选器与自定义筛选器结合使用时,该方法非常有用,该筛选器查找辅助文件以指示文件传输已完成。 模式过滤器可能只传递主文件(例如 ),但“完成”过滤器需要查看(例如)是否存在。ChainFileListFilterCompositeFileListFilterCompositeFileListFilterLastModifiedFileListFilterAcceptOnceFileListFilterCompositeFileListFilterAcceptOnceFileListFilterCompositeFileListFiltersomething.txtsomething.doneSpring中文文档

假设我们有文件、 和 。a.txta.doneb.txtSpring中文文档

模式过滤器仅通过 和 ,而“完成”过滤器可查看所有三个文件并仅通过 。 复合过滤器的最终结果是仅释放。a.txtb.txta.txta.txtSpring中文文档

使用 ,如果链中的任何过滤器返回空列表,则不会调用其余过滤器。ChainFileListFilter

V5.0 引入了对文件执行 SpEL 表达式作为上下文评估根对象。 为此,所有用于文件处理的 XML 组件(本地和远程)以及现有属性都提供了该选项,如以下示例所示:ExpressionFileListFilterfilterfilter-expressionSpring中文文档

<int-file:inbound-channel-adapter
        directory="${inputdir}"
        filter-expression="name matches '.text'"
        auto-startup="false"/>

版本 5.0.5 引入了对被拒绝的文件感兴趣的实现。 为此,应通过 提供此类筛选器实现的回调。 在框架中,此功能与 结合使用。 与常规文件不同,它根据目标文件系统上的事件提供用于处理的文件。 在轮询包含这些文件的内部队列时,可能会丢弃它们,因为它们相对于其配置的 . 因此,我们丢失了文件以备将来可能的考虑。 丢弃回调钩子允许我们将文件保留在内部队列中,以便可以在后续轮询中对其进行检查。 还实现并填充其所有委托的丢弃回调。DiscardAwareFileListFilteraddDiscardCallback(Consumer<File>)FileReadingMessageSource.WatchServiceDirectoryScannerLastModifiedFileListFilterDirectoryScannerWatchServiceLastModifiedFileListFilterageageCompositeFileListFilterDiscardAwareFileListFilterDiscardAwareFileListFilterSpring中文文档

由于文件与所有委托匹配,因此可以对同一文件调用多次。CompositeFileListFilterdiscardCallback

从版本 5.1 开始,不会检查目录是否存在,也不会创建它,直到调用它(通常通过包装)。 以前,没有简单的方法可以防止在引用目录时(例如,从测试中引用)或稍后应用权限时出现操作系统权限错误。FileReadingMessageSourcestart()SourcePollingChannelAdapterSpring中文文档

版本 4.2 引入了 . 在以前的版本中,包含隐藏文件。 在默认配置下,首先触发 ,然后触发 .IgnoreHiddenFileListFilterIgnoreHiddenFileListFilterAcceptOnceFileListFilterSpring中文文档

将其状态存储在内存中。 如果您希望状态在系统重新启动后继续有效,则可以使用 . 此筛选器将接受的文件名存储在实现中(请参阅元数据存储)。 此筛选器与文件名和修改时间匹配。AcceptOnceFileListFilterFileSystemPersistentAcceptOnceFileListFilterMetadataStoreSpring中文文档

从版本 4.0 开始,此筛选器需要 . 当与共享数据存储(例如 )一起使用时,它允许在多个应用程序实例之间共享筛选器密钥,或者在多个服务器使用的网络文件共享之间共享筛选器密钥。ConcurrentMetadataStoreRedisRedisMetadataStoreSpring中文文档

从版本 4.1.5 开始,此筛选器具有一个新属性 (),这会导致它在每次更新时刷新元数据存储(如果存储实现了 )。flushOnUpdateFlushableSpring中文文档

使用 ,如果链中的任何过滤器返回空列表,则不会调用其余过滤器。ChainFileListFilter
由于文件与所有委托匹配,因此可以对同一文件调用多次。CompositeFileListFilterdiscardCallback

邮件头

从版本 5.0 开始,(除了作为轮询)将以下标头填充到出站:FileReadingMessageSourcepayloadFileMessageSpring中文文档

  • FileHeaders.FILENAME:要发送的文件。 可用于后续重命名或复制逻辑。File.getName()Spring中文文档

  • FileHeaders.ORIGINAL_FILE:对象本身。 通常,当我们丢失原始对象时,此标头由框架组件(例如拆分器转换器)自动填充。 但是,为了与任何其他自定义用例保持一致和方便,此标头可用于访问原始文件。FileFileSpring中文文档

  • FileHeaders.RELATIVE_PATH:引入的新标头,用于表示文件路径相对于扫描根目录的部分。 当需要在其他位置还原源目录层次结构时,此标头可能很有用。 为此,可以将(请参阅“生成文件名)配置为使用此标头。DefaultFileNameGeneratorSpring中文文档

目录扫描和轮询

不会立即为目录中的文件生成消息。 它对 返回的“合格文件”使用内部队列。 该选项用于确保在每次轮询时使用最新的输入目录内容刷新内部队列。 默认情况下 (),在再次扫描目录之前清空其队列。 此默认行为对于减少对目录中大量文件的扫描特别有用。 但是,在需要自定义排序的情况下,请务必考虑将此标志设置为 的效果。 文件的处理顺序可能与预期不同。 默认情况下,队列中的文件按其自然 () 顺序进行处理。 扫描添加的新文件,即使队列已有文件,也会插入到适当的位置以保持该自然顺序。 若要自定义顺序,可以接受 a 作为构造函数参数。 内部 () 使用它来根据业务需求对其内容进行重新排序。 因此,要按特定顺序处理文件,应提供自定义生成的列表的比较器,而不是对自定义生成的列表进行排序。FileReadingMessageSourcescannerscanEachPollscanEachPoll = falseFileReadingMessageSourcetruepathFileReadingMessageSourceComparator<File>PriorityBlockingQueueFileReadingMessageSourceDirectoryScannerSpring中文文档

引入了 V5.0 以执行文件树访问。 实现基于功能。 根目录 () 参数从结果中排除。 所有其他子目录的包含和排除都基于目标实现。 例如,默认情况下会筛选出目录。 有关更多信息,请参见 AbstractDirectoryAwareFileListFilter 及其实现。RecursiveDirectoryScannerFiles.walk(Path start, int maxDepth, FileVisitOption…​ options)DirectoryScanner.listFiles(File)FileListFilterSimplePatternFileListFilterSpring中文文档

从 5.5 版开始,Java DSL 有一个方便的选项,可以在目标中使用 a 而不是默认的。FileInboundChannelAdapterSpecrecursive(boolean)RecursiveDirectoryScannerFileReadingMessageSource
从 5.5 版开始,Java DSL 有一个方便的选项,可以在目标中使用 a 而不是默认的。FileInboundChannelAdapterSpecrecursive(boolean)RecursiveDirectoryScannerFileReadingMessageSource

命名空间支持

通过使用特定于文件的命名空间,可以简化文件读取的配置。 为此,请使用以下模板:Spring中文文档

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-file="http://www.springframework.org/schema/integration/file"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/file
    https://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
</beans>

在此命名空间中,可以减少 并将其包装在入站通道适配器中,如下所示:FileReadingMessageSourceSpring中文文档

<int-file:inbound-channel-adapter id="filesIn1"
    directory="file:${input.directory}" prevent-duplicates="true" ignore-hidden="true"/>

<int-file:inbound-channel-adapter id="filesIn2"
    directory="file:${input.directory}"
    filter="customFilterBean" />

<int-file:inbound-channel-adapter id="filesIn3"
    directory="file:${input.directory}"
    filename-pattern="test*" />

<int-file:inbound-channel-adapter id="filesIn4"
    directory="file:${input.directory}"
    filename-regex="test[0-9]+\.txt" />

第一个通道适配器示例依赖于默认实现:FileListFilterSpring中文文档

因此,您也可以省略 和 属性,因为它们默认为。prevent-duplicatesignore-hiddentrueSpring中文文档

Spring Integration 4.2 引入了该属性。 在以前的版本中,包含隐藏文件。ignore-hiddenSpring中文文档

第二个通道适配器示例使用自定义筛选器,第三个使用属性添加基于筛选器,第四个使用该属性将基于正则表达式模式的筛选器添加到 . 和 属性与常规引用属性互斥。 但是,您可以使用该属性来引用组合任意数量的筛选器的实例,包括一个或多个基于模式的筛选器,以满足您的特定需求。filename-patternAntPathMatcherfilename-regexFileReadingMessageSourcefilename-patternfilename-regexfilterfilterCompositeFileListFilterSpring中文文档

当多个进程从同一目录读取时,您可能希望锁定文件以防止同时选取它们。 为此,您可以使用 . 有一个基于 的实现可用,但也可以实现您自己的锁定方案。 储物柜可以按如下方式注入:FileLockerjava.nionioSpring中文文档

<int-file:inbound-channel-adapter id="filesIn"
    directory="file:${input.directory}" prevent-duplicates="true">
    <int-file:nio-locker/>
</int-file:inbound-channel-adapter>

您可以按如下方式配置自定义储物柜:Spring中文文档

<int-file:inbound-channel-adapter id="filesIn"
    directory="file:${input.directory}" prevent-duplicates="true">
    <int-file:locker ref="customLocker"/>
</int-file:inbound-channel-adapter>
当文件入站适配器配置了锁时,它负责在允许接收文件之前获取锁。 它不承担解锁文件的责任。 如果您已处理文件并使锁挂起,则存在内存泄漏。 如果这是一个问题,你应该在适当的时间给自己打电话。FileLocker.unlock(File file)

当筛选和锁定文件还不够时,您可能需要完全控制文件的列出方式。 若要实现此类要求,可以使用 的实现。 此扫描仪可让您准确确定每个投票中列出的文件。 这也是 Spring Integration 在内部用于连接实例和 . 您可以将自定义项注入到 on 属性中,如以下示例所示:DirectoryScannerFileListFilterFileLockerFileReadingMessageSourceDirectoryScanner<int-file:inbound-channel-adapter/>scannerSpring中文文档

<int-file:inbound-channel-adapter id="filesIn" directory="file:${input.directory}"
     scanner="customDirectoryScanner"/>

这样做可以让您完全自由地选择订购、列表和锁定策略。Spring中文文档

同样重要的是要了解筛选器(包括 、 、 等)和实例实际上由 . 适配器上设置的这些属性中的任何一个随后都会注入到内部 . 对于外部 ,所有过滤器和储物柜属性都禁止在 上。 必须在该自定义上指定它们(如果需要)。 换言之,如果将 a 注入 ,则应提供 和 on that ,而不是 。patternsregexprevent-duplicateslockerscannerscannerscannerFileReadingMessageSourceDirectoryScannerscannerFileReadingMessageSourcefilterlockerscannerFileReadingMessageSourceSpring中文文档

默认情况下,使用 an 和 . 为了防止使用它们,您可以配置自己的过滤器(例如 ),甚至将其设置为 。DefaultDirectoryScannerIgnoreHiddenFileListFilterAcceptOnceFileListFilterAcceptAllFileListFilternull

Spring Integration 4.2 引入了该属性。 在以前的版本中,包含隐藏文件。ignore-hiddenSpring中文文档

当文件入站适配器配置了锁时,它负责在允许接收文件之前获取锁。 它不承担解锁文件的责任。 如果您已处理文件并使锁挂起,则存在内存泄漏。 如果这是一个问题,你应该在适当的时间给自己打电话。FileLocker.unlock(File file)
默认情况下,使用 an 和 . 为了防止使用它们,您可以配置自己的过滤器(例如 ),甚至将其设置为 。DefaultDirectoryScannerIgnoreHiddenFileListFilterAcceptOnceFileListFilterAcceptAllFileListFilternull

WatchServiceDirectoryScanner

将新文件添加到目录时,依赖于文件系统事件。 在初始化期间,将注册该目录以生成事件。 初始文件列表也是在初始化期间构建的。 在遍历目录树时,遇到的任何子目录也会被注册以生成事件。 在第一次轮询时,将返回遍历目录的初始文件列表。 在随后的轮询中,将返回来自新创建事件的文件。 如果添加了新的子目录,则其创建事件用于遍历新子树以查找现有文件并注册找到的任何新子目录。FileReadingMessageSource.WatchServiceDirectoryScannerSpring中文文档

当其内部事件没有像目录修改事件发生时那样迅速被程序耗尽时,存在一个问题。 如果超出队列大小,则会发出 a 以指示某些文件系统事件可能会丢失。 在这种情况下,将完全重新扫描根目录。 为避免重复,请考虑使用适当的(如 )或在处理完成后删除文件。WatchKeyqueueStandardWatchEventKinds.OVERFLOWFileListFilterAcceptOnceFileListFilter

可以通过选项启用,该选项与选项互斥。 将为提供的 .WatchServiceDirectoryScannerFileReadingMessageSource.use-watch-servicescannerFileReadingMessageSource.WatchServiceDirectoryScannerdirectorySpring中文文档

此外,现在轮询逻辑可以跟踪 和 。WatchServiceStandardWatchEventKinds.ENTRY_MODIFYStandardWatchEventKinds.ENTRY_DELETESpring中文文档

如果需要跟踪现有文件和新文件的修改,则应在 . 否则,将以相同的方式处理这些事件中的文件。ENTRY_MODIFYFileListFilterSpring中文文档

实现会拾取事件。 因此,为操作提供了他们的文件。 启用此事件后,筛选器(如 已删除文件)。 因此,如果出现同名文件,则该文件将通过筛选器并作为消息发送。ResettableFileListFilterENTRY_DELETEremove()AcceptOnceFileListFilterSpring中文文档

为此,引入了属性 ()。 ( 是 中的公共内部枚举。 有了这样的选项,我们可以对新文件使用一个下游流逻辑,对修改后的文件使用一些其他逻辑。 以下示例演示如何为同一目录中的创建和修改事件配置不同的逻辑:watch-eventsFileReadingMessageSource.setWatchEvents(WatchEventType…​ watchEvents)WatchEventTypeFileReadingMessageSourceSpring中文文档

值得一提的是,该事件涉及被监视目录子目录的重命名操作。 更具体地说,与以前的目录名称相关的 event 位于通知新(重命名)目录的事件之前。 在某些操作系统(如 Windows)上,必须注册事件才能处理这种情况。 否则,在文件资源管理器中重命名监视的子目录可能会导致在该子目录中无法检测到新文件。ENTRY_DELETEENTRY_DELETEENTRY_CREATEENTRY_DELETESpring中文文档

<int-file:inbound-channel-adapter id="newFiles"
     directory="${input.directory}"
     use-watch-service="true"/>

<int-file:inbound-channel-adapter id="modifiedFiles"
     directory="${input.directory}"
     use-watch-service="true"
     filter="acceptAllFilter"
     watch-events="MODIFY"/> <!-- The default is CREATE. -->

从版本 6.1 开始,公开了两个新的与 -相关的选项:FileReadingMessageSourceWatchServiceSpring中文文档

  • watchMaxDepth- API 的参数;Files.walkFileTree(Path root, Set attributes, int maxDepth, FileVisitor visitor)Spring中文文档

  • watchDirPredicate- a 测试是否应遍历扫描树中的目录并将其注册到 和 配置的监视事件类型。Predicate<Path>WatchServiceSpring中文文档

当其内部事件没有像目录修改事件发生时那样迅速被程序耗尽时,存在一个问题。 如果超出队列大小,则会发出 a 以指示某些文件系统事件可能会丢失。 在这种情况下,将完全重新扫描根目录。 为避免重复,请考虑使用适当的(如 )或在处理完成后删除文件。WatchKeyqueueStandardWatchEventKinds.OVERFLOWFileListFilterAcceptOnceFileListFilter

限制内存消耗

可以使用 来限制内存中保留的文件数。 这在扫描大型目录时非常有用。 使用 XML 配置时,可以通过在入站通道适配器上设置属性来启用此功能。HeadDirectoryScannerqueue-sizeSpring中文文档

在版本 4.2 之前,此设置与任何其他筛选器的使用不兼容。 任何其他筛选器(包括 )都会覆盖用于限制大小的筛选器。prevent-duplicates="true"Spring中文文档

a 的使用与 . 由于在投票决策期间会咨询所有过滤器,因此不知道其他过滤器可能正在临时过滤文件。 即使以前被 筛选的文件现在可用,也会对其进行筛选。HeadDirectoryScannerAcceptOnceFileListFilterAcceptOnceFileListFilterHeadDirectoryScanner.HeadFilterAcceptOnceFileListFilterSpring中文文档

通常,在这种情况下,您不应使用 ,而应删除已处理的文件,以便以前筛选的文件在将来的轮询中可用。AcceptOnceFileListFilterSpring中文文档

a 的使用与 . 由于在投票决策期间会咨询所有过滤器,因此不知道其他过滤器可能正在临时过滤文件。 即使以前被 筛选的文件现在可用,也会对其进行筛选。HeadDirectoryScannerAcceptOnceFileListFilterAcceptOnceFileListFilterHeadDirectoryScanner.HeadFilterAcceptOnceFileListFilterSpring中文文档

通常,在这种情况下,您不应使用 ,而应删除已处理的文件,以便以前筛选的文件在将来的轮询中可用。AcceptOnceFileListFilterSpring中文文档

使用 Java 配置进行配置

以下 Spring Boot 应用程序显示了如何使用 Java 配置配置出站适配器的示例:Spring中文文档

@SpringBootApplication
public class FileReadingJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(FileReadingJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    public MessageChannel fileInputChannel() {
        return new DirectChannel();
    }

    @Bean
    @InboundChannelAdapter(value = "fileInputChannel", poller = @Poller(fixedDelay = "1000"))
    public MessageSource<File> fileReadingMessageSource() {
         FileReadingMessageSource source = new FileReadingMessageSource();
         source.setDirectory(new File(INBOUND_PATH));
         source.setFilter(new SimplePatternFileListFilter("*.txt"));
         return source;
    }

    @Bean
    @Transformer(inputChannel = "fileInputChannel", outputChannel = "processFileChannel")
    public FileToStringTransformer fileToStringTransformer() {
        return new FileToStringTransformer();
    }

}

使用 Java DSL 进行配置

以下 Spring Boot 应用程序显示了如何使用 Java DSL 配置出站适配器的示例:Spring中文文档

@SpringBootApplication
public class FileReadingJavaApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(FileReadingJavaApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    public IntegrationFlow fileReadingFlow() {
         return IntegrationFlow
                  .from(Files.inboundAdapter(new File(INBOUND_PATH))
                              .patternFilter("*.txt"),
                          e -> e.poller(Pollers.fixedDelay(1000)))
                  .transform(Files.toStringTransformer())
                  .channel("processFileChannel")
                  .get();
    }

}

'tail'ing 文件

另一个流行的用例是从文件的末尾(或尾部)获取“行”,在添加新行时捕获它们。 提供了两种实现。 第一个 ,使用本机命令(在具有本机命令的操作系统上)。 这通常是这些平台上最有效的实现。 对于没有命令的操作系统,第二种实现 使用 Apache 类。OSDelegatingFileTailingMessageProducertailtailApacheCommonsFileTailingMessageProducercommons-ioTailerSpring中文文档

在这两种情况下,文件系统事件(例如文件不可用和其他事件)都会使用常规的 Spring 事件发布机制作为实例发布。 此类事件的示例包括:ApplicationEventSpring中文文档

[message=tail: cannot open '/tmp/somefile' for reading:
               No such file or directory, file=/tmp/somefile]

[message=tail: '/tmp/somefile' has become accessible, file=/tmp/somefile]

[message=tail: '/tmp/somefile' has become inaccessible:
               No such file or directory, file=/tmp/somefile]

[message=tail: '/tmp/somefile' has appeared;
               following end of new file, file=/tmp/somefile]

例如,在旋转文件时,可能会发生上述示例中显示的事件序列。Spring中文文档

从版本 5.0 开始,当 . 以下示例显示了此类事件的外观:FileTailingIdleEventidleEventIntervalSpring中文文档

[message=Idle timeout, file=/tmp/somefile] [idle time=5438]
并非所有支持命令的平台都提供这些状态消息。tail

从这些终结点发出的消息具有以下标头:Spring中文文档

在 V5.0 之前的版本中,标头包含文件绝对路径的字符串表示形式。 现在,您可以通过调用原始文件头来获取该字符串表示形式。FileHeaders.FILENAMEgetAbsolutePath()

以下示例使用默认选项(“-F -n 0”,表示跟随当前末尾的文件名)创建本机适配器。Spring中文文档

<int-file:tail-inbound-channel-adapter id="native"
	channel="input"
	task-executor="exec"
	file="/tmp/foo"/>

以下示例使用“-F -n +0”选项创建一个本机适配器(表示跟随文件名,发出所有现有行)。Spring中文文档

<int-file:tail-inbound-channel-adapter id="native"
	channel="input"
	native-options="-F -n +0"
	task-executor="exec"
	file-delay=10000
	file="/tmp/foo"/>

如果命令失败(在某些平台上,丢失的文件会导致失败,即使指定了),则每 10 秒重试一次该命令。tailtail-FSpring中文文档

默认情况下,本机适配器从标准输出捕获内容,并将内容作为消息发送。 它们还可以从标准误差中捕获以引发事件。 从版本 4.3.6 开始,可以通过将 设置为 来丢弃标准错误事件,如以下示例所示:enable-status-readerfalseSpring中文文档

<int-file:tail-inbound-channel-adapter id="native"
	channel="input"
	enable-status-reader="false"
	task-executor="exec"
	file="/tmp/foo"/>

在以下示例中,设置为 ,这意味着,如果 5 秒内没有写入任何行,则每 5 秒触发一次:IdleEventInterval5000FileTailingIdleEventSpring中文文档

<int-file:tail-inbound-channel-adapter id="native"
	channel="input"
	idle-event-interval="5000"
	task-executor="exec"
	file="/tmp/somefile"/>

当您需要停止适配器时,这可能很有用。Spring中文文档

下面的示例创建一个 Apache 适配器,该适配器每两秒检查一次文件中的新行,并每十秒检查一次是否存在丢失的文件:commons-ioTailerSpring中文文档

<int-file:tail-inbound-channel-adapter id="apache"
	channel="input"
	task-executor="exec"
	file="/tmp/bar"
	delay="2000"
	end="false"             (1)
	reopen="true"           (2)
	file-delay="10000"/>
1 文件从开头 () 而不是结尾(默认值)开始尾随。end="false"
2 每个区块都会重新打开文件(默认情况下是保持文件打开状态)。
指定 或 属性会强制使用 Apache 适配器,并使该属性不可用。delayendreopencommons-ionative-options
并非所有支持命令的平台都提供这些状态消息。tail
在 V5.0 之前的版本中,标头包含文件绝对路径的字符串表示形式。 现在,您可以通过调用原始文件头来获取该字符串表示形式。FileHeaders.FILENAMEgetAbsolutePath()
1 文件从开头 () 而不是结尾(默认值)开始尾随。end="false"
2 每个区块都会重新打开文件(默认情况下是保持文件打开状态)。
指定 或 属性会强制使用 Apache 适配器,并使该属性不可用。delayendreopencommons-ionative-options

处理不完整的数据

文件传输方案中的一个常见问题是如何确定传输是否完成,以便您不会开始读取不完整的文件。 解决此问题的常用方法是使用临时名称编写文件,然后以原子方式将其重命名为最终名称。 这种技术与屏蔽临时文件不被使用者拾取的过滤器一起,提供了一个强大的解决方案。 此技术由写入文件(本地或远程)的 Spring Integration 组件使用。 默认情况下,它们会追加到文件名,并在传输完成后将其删除。.writingSpring中文文档

另一种常见的方法是编写第二个“标记”文件以指示文件传输完成。 在此方案中,不应考虑(例如)在同时存在之前可供使用。 Spring Integration 5.0 版引入了新的过滤器来支持此机制。 为文件系统 ()、FTPSFTP 提供了实现。 它们是可配置的,因此标记文件可以具有任何名称,尽管它通常与要传输的文件相关。 有关更多信息,请参见 Javadocsomefile.txtsomefile.txt.completeFileSystemMarkerFilePresentFileListFilterSpring中文文档