(window.webpackJsonp=window.webpackJsonp||[]).push([[144],{576:function(e,t,o){"use strict";o.r(t);var n=o(56),a=Object(n.a)({},(function(){var e=this,t=e.$createElement,o=e._self._c||t;return o("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[o("h1",{attrs:{id:"sftp-adapters"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#sftp-adapters"}},[e._v("#")]),e._v(" SFTP Adapters")]),e._v(" "),o("h2",{attrs:{id:"sftp-adapters-2"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#sftp-adapters-2"}},[e._v("#")]),e._v(" SFTP Adapters")]),e._v(" "),o("p",[e._v("Spring Integration provides support for file transfer operations over SFTP.")]),e._v(" "),o("p",[e._v("The Secure File Transfer Protocol (SFTP) is a network protocol that lets you transfer files between two computers on the Internet over any reliable stream.")]),e._v(" "),o("p",[e._v("The SFTP protocol requires a secure channel, such as SSH, and visibility to a client’s identity throughout the SFTP session.")]),e._v(" "),o("p",[e._v("Spring Integration supports sending and receiving files over SFTP by providing three client side endpoints: inbound channel adapter, outbound channel adapter, and outbound gateway.\nIt also provides convenient namespace configuration to define these client components.")]),e._v(" "),o("p",[e._v("You need to include this dependency into your project:")]),e._v(" "),o("p",[e._v("Maven")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v("\n org.springframework.integration\n spring-integration-sftp\n 5.5.9\n\n")])])]),o("p",[e._v("Gradle")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('compile "org.springframework.integration:spring-integration-sftp:5.5.9"\n')])])]),o("p",[e._v("To include the SFTP namespace in your xml configuration, include the following attributes on the root element:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"\nxsi:schemaLocation="http://www.springframework.org/schema/integration/sftp\n https://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd"\n')])])]),o("h3",{attrs:{id:"sftp-session-factory"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#sftp-session-factory"}},[e._v("#")]),e._v(" SFTP Session Factory")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("As of version 3.0, sessions are no longer cached by default."),o("br"),e._v("See "),o("a",{attrs:{href:"#sftp-session-caching"}},[e._v("SFTP Session Caching")]),e._v(".")])])]),e._v(" "),o("tbody")]),e._v(" "),o("p",[e._v("Before configuring SFTP adapters, you must configure an SFTP session factory.\nYou can configure the SFTP session factory with a regular bean definition, as the following example shows:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n \n \n \n \n \n\n')])])]),o("p",[e._v("Every time an adapter requests a session object from its "),o("code",[e._v("SessionFactory")]),e._v(", a new SFTP session is created.\nUnder the covers, the SFTP Session Factory relies on the "),o("a",{attrs:{href:"http://www.jcraft.com/jsch",target:"_blank",rel:"noopener noreferrer"}},[e._v("JSch"),o("OutboundLink")],1),e._v(" library to provide the SFTP capabilities.")]),e._v(" "),o("p",[e._v("However, Spring Integration also supports the caching of SFTP sessions.\nSee "),o("a",{attrs:{href:"#sftp-session-caching"}},[e._v("SFTP Session Caching")]),e._v(" for more information.")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("JSch supports multiple channels (operations) over a connection to the server."),o("br"),e._v("By default, the Spring Integration session factory uses a separate physical connection for each channel."),o("br"),e._v("Since Spring Integration 3.0, you can configure the session factory (using a boolean constructor arg - default "),o("code",[e._v("false")]),e._v(") to use a single connection to the server and create multiple "),o("code",[e._v("JSch")]),e._v(" channels on that single connection."),o("br"),o("br"),e._v("When using this feature, you must wrap the session factory in a caching session factory, as "),o("a",{attrs:{href:"#sftp-session-caching"}},[e._v("described later")]),e._v(", so that the connection is not physically closed when an operation completes."),o("br"),o("br"),e._v("If the cache is reset, the session is disconnected only when the last channel is closed."),o("br"),o("br"),e._v("The connection is refreshed if it is found to be disconnected when a new operation obtains a session.")])])]),e._v(" "),o("tbody")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("If you experience connectivity problems and would like to trace session creation and see which sessions are polled, you may enable tracing by setting the logger to "),o("code",[e._v("TRACE")]),e._v(" level (for example, "),o("code",[e._v("log4j.category.org.springframework.integration.sftp=TRACE")]),e._v(")."),o("br"),e._v("See "),o("a",{attrs:{href:"#sftp-jsch-logging"}},[e._v("SFTP/JSCH Logging")]),e._v(".")])])]),e._v(" "),o("tbody")]),e._v(" "),o("p",[e._v("Now all you need to do is inject this SFTP session factory into your adapters.")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("A more practical way to provide values for the SFTP session factory is to use Spring’s "),o("a",{attrs:{href:"https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-placeholderconfigurer",target:"_blank",rel:"noopener noreferrer"}},[e._v("property placeholder support"),o("OutboundLink")],1),e._v(".")])])]),e._v(" "),o("tbody")]),e._v(" "),o("h4",{attrs:{id:"configuration-properties"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#configuration-properties"}},[e._v("#")]),e._v(" Configuration Properties")]),e._v(" "),o("p",[e._v("The following list describes all the properties that are exposed by the "),o("a",{attrs:{href:"https://docs.spring.io/spring-integration/api/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.html",target:"_blank",rel:"noopener noreferrer"}},[o("code",[e._v("DefaultSftpSessionFactory")]),o("OutboundLink")],1),e._v(".")]),e._v(" "),o("p",[o("code",[e._v("isSharedSession")]),e._v(" (constructor argument)::When "),o("code",[e._v("true")]),e._v(", a single connection is used, and "),o("code",[e._v("JSch Channels")]),e._v(" are multiplexed.\nIt defaults to "),o("code",[e._v("false")]),e._v(".")]),e._v(" "),o("p",[o("code",[e._v("clientVersion")]),e._v("::Lets you set the client version property.\nIt’s default depends on the underlying JSch version but it will look like: "),o("em",[e._v("SSH-2.0-JSCH-0.1.45")])]),e._v(" "),o("p",[o("code",[e._v("enableDaemonThread")]),e._v("::If "),o("code",[e._v("true")]),e._v(", all threads are daemon threads.\nIf set to "),o("code",[e._v("false")]),e._v(", normal non-daemon threads are used instead.\nThis property is set on the underlying "),o("a",{attrs:{href:"https://epaul.github.io/jsch-documentation/javadoc/com/jcraft/jsch/Session.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("session"),o("OutboundLink")],1),e._v(".\nThere, this property defaults to "),o("code",[e._v("false")]),e._v(".")]),e._v(" "),o("p",[o("code",[e._v("host")]),e._v("::The URL of the host to which you want to connect.\nRequired.")]),e._v(" "),o("p",[o("code",[e._v("hostKeyAlias")]),e._v("::Sets the host key alias, which is used when comparing the host key to the known hosts list.")]),e._v(" "),o("p",[o("code",[e._v("knownHostsResource")]),e._v("::Specifies the file resource that used for a host key repository.\nThe file has the same format as OpenSSH’s "),o("code",[e._v("known_hosts")]),e._v(" file and is required and must be pre-populated if "),o("code",[e._v("allowUnknownKeys")]),e._v(" is false.")]),e._v(" "),o("p",[o("code",[e._v("password")]),e._v("::The password to authenticate against the remote host.\nIf a password is not provided, then the "),o("code",[e._v("privateKey")]),e._v(" property is required.\nIt is not allowed if you set "),o("code",[e._v("userInfo")]),e._v(".\nThe password is obtained from that object.")]),e._v(" "),o("p",[o("code",[e._v("port")]),e._v("::The port over which the SFTP connection shall be established.\nIf not specified, this value defaults to "),o("code",[e._v("22")]),e._v(".\nIf specified, this properties must be a positive number.")]),e._v(" "),o("p",[o("code",[e._v("privateKey")]),e._v("::Lets you set a "),o("a",{attrs:{href:"https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/core/io/Resource.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("resource"),o("OutboundLink")],1),e._v(" that represents the location of the private key used for authenticating against the remote host.\nIf the "),o("code",[e._v("privateKey")]),e._v(" is not provided, then the "),o("code",[e._v("password")]),e._v(" property is required.")]),e._v(" "),o("p",[o("code",[e._v("privateKeyPassphrase")]),e._v("::The password for the private key.\nIf you set "),o("code",[e._v("userInfo")]),e._v(", "),o("code",[e._v("privateKeyPassphrase")]),e._v(" is not allowed .\nThe passphrase is obtained from that object.\nOptional.")]),e._v(" "),o("p",[o("code",[e._v("proxy")]),e._v("::Allows for specifying a JSch-based "),o("a",{attrs:{href:"https://epaul.github.com/jsch-documentation/javadoc/com/jcraft/jsch/Proxy.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("proxy"),o("OutboundLink")],1),e._v(".\nIf set, the proxy object is used to create the connection to the remote host through the proxy.\nSee "),o("a",{attrs:{href:"#sftp-proxy-factory-bean"}},[e._v("Proxy Factory Bean")]),e._v(" for a convenient way to configure the proxy.")]),e._v(" "),o("p",[o("code",[e._v("serverAliveCountMax")]),e._v("::Specifies the number of server-alive messages, which are sent without any reply from the server before disconnecting.\nIf not set, this property defaults to "),o("code",[e._v("1")]),e._v(".")]),e._v(" "),o("p",[o("code",[e._v("serverAliveInterval")]),e._v("::Sets the timeout interval (in milliseconds) before a server-alive message is sent, in case no message is received from the server.")]),e._v(" "),o("p",[o("code",[e._v("sessionConfig")]),e._v("::By using "),o("code",[e._v("Properties")]),e._v(", you can set additional configuration setting on the underlying JSch Session.")]),e._v(" "),o("p",[o("code",[e._v("socketFactory")]),e._v("::Lets you pass in a "),o("a",{attrs:{href:"https://epaul.github.com/jsch-documentation/javadoc/com/jcraft/jsch/SocketFactory.html",target:"_blank",rel:"noopener noreferrer"}},[o("code",[e._v("SocketFactory")]),o("OutboundLink")],1),e._v(".\nThe socket factory is used to create a socket to the target host.\nWhen a proxy is used, the socket factory is passed to the proxy.\nBy default, plain TCP sockets are used.")]),e._v(" "),o("p",[o("code",[e._v("timeout")]),e._v("::The timeout property is used as the socket timeout parameter, as well as the default connection timeout.\nDefaults to "),o("code",[e._v("0")]),e._v(", which means, that no timeout will occur.")]),e._v(" "),o("p",[o("code",[e._v("user")]),e._v("::The remote user to use.\nRequired.")]),e._v(" "),o("p",[o("code",[e._v("allowUnknownKeys")]),e._v("::Set to "),o("code",[e._v("true")]),e._v(" to allow connections to hosts with unknown (or changed) keys.\nIts default is 'false'.\nIt is applied only if no "),o("code",[e._v("userInfo")]),e._v(" is provided.\nIf "),o("code",[e._v("false")]),e._v(", a pre-populated "),o("code",[e._v("knownHosts")]),e._v(" file is required.")]),e._v(" "),o("p",[o("code",[e._v("userInfo")]),e._v("::Set a custom "),o("code",[e._v("UserInfo")]),e._v(" to be used during authentication.\nIn particular, "),o("code",[e._v("promptYesNo()")]),e._v(" is invoked when an unknown (or changed) host key is received.\nSee also "),o("a",{attrs:{href:"#sftp-unk-keys"}},[o("code",[e._v("allowUnknownKeys")])]),e._v(".\nWhen you provide a "),o("code",[e._v("UserInfo")]),e._v(", the "),o("code",[e._v("password")]),e._v(" and private key "),o("code",[e._v("passphrase")]),e._v(" are obtained from it, and you cannot set discrete"),o("code",[e._v("password")]),e._v(" and "),o("code",[e._v("privateKeyPassphrase")]),e._v(" properties.")]),e._v(" "),o("h3",{attrs:{id:"proxy-factory-bean"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#proxy-factory-bean"}},[e._v("#")]),e._v(" Proxy Factory Bean")]),e._v(" "),o("p",[o("code",[e._v("Jsch")]),e._v(" provides a mechanism to connect to the server over an HTTP or SOCKS proxy.\nTo use this feature, configure the "),o("code",[e._v("Proxy")]),e._v(" and provide a reference to the "),o("code",[e._v("DefaultSftpSessionFactory")]),e._v(", as discussed\nearlier.\nThree implementations are provided by "),o("code",[e._v("Jsch")]),e._v(": "),o("code",[e._v("HTTP")]),e._v(", "),o("code",[e._v("SOCKS4")]),e._v(", and "),o("code",[e._v("SOCKS5")]),e._v(".\nSpring Integration 4.3 introduced a "),o("code",[e._v("FactoryBean")]),e._v(", easing configuration of these proxies by allowing property\ninjection, as the following example shows:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n \n \n \n \n \n\n\n\n ...\n \n ...\n\n')])])]),o("h3",{attrs:{id:"delegating-session-factory"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#delegating-session-factory"}},[e._v("#")]),e._v(" Delegating Session Factory")]),e._v(" "),o("p",[e._v("Version 4.2 introduced the "),o("code",[e._v("DelegatingSessionFactory")]),e._v(", which allows the selection of the actual session factory at\nruntime.\nPrior to invoking the SFTP endpoint, you can call "),o("code",[e._v("setThreadKey()")]),e._v(" on the factory to associate a key with the current thread.\nThat key is then used to look up the actual session factory to be used.\nYou can clear the key by calling "),o("code",[e._v("clearThreadKey()")]),e._v(" after use.")]),e._v(" "),o("p",[e._v("We added convenience methods so that you can more easily do so from a message flow, as the following example shows:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n \n \n \x3c!-- delegate factories here --\x3e\n \n \n\n\n\n\n\n\n\n')])])]),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("When using session caching (see "),o("a",{attrs:{href:"#sftp-session-caching"}},[e._v("SFTP Session Caching")]),e._v("), each of the delegates should be cached."),o("br"),e._v("You cannot cache the "),o("code",[e._v("DelegatingSessionFactory")]),e._v(" itself.")])])]),e._v(" "),o("tbody")]),e._v(" "),o("p",[e._v("Starting with version 5.0.7, the "),o("code",[e._v("DelegatingSessionFactory")]),e._v(" can be used in conjunction with a "),o("code",[e._v("RotatingServerAdvice")]),e._v(" to poll multiple servers; see "),o("a",{attrs:{href:"#sftp-rotating-server-advice"}},[e._v("Inbound Channel Adapters: Polling Multiple Servers and Directories")]),e._v(".")]),e._v(" "),o("h3",{attrs:{id:"sftp-session-caching"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#sftp-session-caching"}},[e._v("#")]),e._v(" SFTP Session Caching")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("Starting with Spring Integration version 3.0, sessions are no longer cached by default."),o("br"),e._v("The "),o("code",[e._v("cache-sessions")]),e._v(" attribute is no longer supported on endpoints."),o("br"),e._v("If you wish to cache sessions, you must use a "),o("code",[e._v("CachingSessionFactory")]),e._v(" (see the next example).")])])]),e._v(" "),o("tbody")]),e._v(" "),o("p",[e._v("In versions prior to 3.0, the sessions were automatically cached by default.\nA "),o("code",[e._v("cache-sessions")]),e._v(" attribute was available for disabling the auto caching, but that solution did not provide a way to configure other session-caching attributes.\nFor example, you could not limit on the number of sessions created.\nTo support that requirement and other configuration options, we added a "),o("code",[e._v("CachingSessionFactory")]),e._v(".\nIt provides "),o("code",[e._v("sessionCacheSize")]),e._v(" and "),o("code",[e._v("sessionWaitTimeout")]),e._v(" properties.\nAs its name suggests, the "),o("code",[e._v("sessionCacheSize")]),e._v(" property controls how many active sessions the factory maintains in its cache (the default is unbounded).\nIf the "),o("code",[e._v("sessionCacheSize")]),e._v(" threshold has been reached, any attempt to acquire another session blocks until either one of the cached sessions becomes available or until the wait time for a session expires (the default wait time is "),o("code",[e._v("Integer.MAX_VALUE")]),e._v(").\nThe "),o("code",[e._v("sessionWaitTimeout")]),e._v(" property enables configuration of the wait time.")]),e._v(" "),o("p",[e._v("If you want your sessions to be cached, configure your default session factory (as "),o("a",{attrs:{href:"#sftp-session-factory"}},[e._v("described earlier")]),e._v(") and then wrap it in an instance of "),o("code",[e._v("CachingSessionFactory")]),e._v(" where you may provide those additional properties.\nThe following example shows how to do so:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n \n\n\n\n \n \n \n\n')])])]),o("p",[e._v("The preceding example creates a "),o("code",[e._v("CachingSessionFactory")]),e._v(" with its "),o("code",[e._v("sessionCacheSize")]),e._v(" set to "),o("code",[e._v("10")]),e._v(" and its "),o("code",[e._v("sessionWaitTimeout")]),e._v(" set to one second (1000 milliseconds).")]),e._v(" "),o("p",[e._v("Starting with Spring Integration version 3.0, the "),o("code",[e._v("CachingConnectionFactory")]),e._v(" provides a "),o("code",[e._v("resetCache()")]),e._v(" method.\nWhen invoked, all idle sessions are immediately closed and in-use sessions are closed when they are returned to the cache.\nWhen using "),o("code",[e._v("isSharedSession=true")]),e._v(", the channel is closed and the shared session is closed only when the last channel is closed.\nNew requests for sessions establish new sessions as necessary.")]),e._v(" "),o("p",[e._v("Starting with version 5.1, the "),o("code",[e._v("CachingSessionFactory")]),e._v(" has a new property "),o("code",[e._v("testSession")]),e._v(".\nWhen true, the session will be tested by performing a "),o("code",[e._v("stat(getHome())")]),e._v(" command to ensure it is still active; if not, it will be removed from the cache; a new session is created if no active sessions are in the cache.")]),e._v(" "),o("h3",{attrs:{id:"using-remotefiletemplate"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#using-remotefiletemplate"}},[e._v("#")]),e._v(" Using "),o("code",[e._v("RemoteFileTemplate")])]),e._v(" "),o("p",[e._v("Spring Integration version 3.0 provides a new abstraction over the "),o("code",[e._v("SftpSession")]),e._v(" object.\nThe template provides methods to send, retrieve (as an "),o("code",[e._v("InputStream")]),e._v("), remove, and rename files.\nIn addition, we provide an "),o("code",[e._v("execute")]),e._v(" method to let the caller run multiple operations on the session.\nIn all cases, the template takes care of reliably closing the session.\nFor more information, see the "),o("a",{attrs:{href:"https://docs.spring.io/spring-integration/api/org/springframework/integration/file/remote/RemoteFileTemplate.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("Javadoc for "),o("code",[e._v("RemoteFileTemplate")]),o("OutboundLink")],1),e._v(" There is a subclass for SFTP: "),o("a",{attrs:{href:"https://docs.spring.io/spring-integration/api/org/springframework/integration/sftp/session/SftpRemoteFileTemplate.html",target:"_blank",rel:"noopener noreferrer"}},[o("code",[e._v("SftpRemoteFileTemplate")]),o("OutboundLink")],1),e._v(".")]),e._v(" "),o("p",[e._v("We added additional methods in version 4.1, including "),o("code",[e._v("getClientInstance()")]),e._v(".\nIt provides access to the underlying "),o("code",[e._v("ChannelSftp")]),e._v(", which enables access to low-level APIs.")]),e._v(" "),o("p",[e._v("Version 5.0 introduced the "),o("code",[e._v("RemoteFileOperations.invoke(OperationsCallback action)")]),e._v(" method.\nThis method lets several "),o("code",[e._v("RemoteFileOperations")]),e._v(" calls be called in the scope of the same thread-bounded "),o("code",[e._v("Session")]),e._v(".\nThis is useful when you need to perform several high-level operations of the "),o("code",[e._v("RemoteFileTemplate")]),e._v(" as one unit of work.\nFor example, "),o("code",[e._v("AbstractRemoteFileOutboundGateway")]),e._v(" uses it with the "),o("code",[e._v("mput")]),e._v(" command implementation, where we perform a "),o("code",[e._v("put")]),e._v(" operation for each file in the provided directory and recursively for its sub-directories.\nSee the "),o("a",{attrs:{href:"https://docs.spring.io/spring-integration/api/org/springframework/integration/file/remote/RemoteFileTemplate.html#invoke-org.springframework.integration.file.remote.OperationsCallback-",target:"_blank",rel:"noopener noreferrer"}},[e._v("Javadoc"),o("OutboundLink")],1),e._v(" for more information.")]),e._v(" "),o("h3",{attrs:{id:"sftp-inbound-channel-adapter"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#sftp-inbound-channel-adapter"}},[e._v("#")]),e._v(" SFTP Inbound Channel Adapter")]),e._v(" "),o("p",[e._v("The SFTP inbound channel adapter is a special listener that connects to the server and listens for the remote directory events (such as a new file being created), at which point it initiates a file transfer.\nThe following example shows how to configure an SFTP inbound channel adapter:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n \n\n')])])]),o("p",[e._v("The preceding configuration example shows how to provide values for various attributes, including the following:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("local-directory")]),e._v(": The location to which files are going to be transferred")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("remote-directory")]),e._v(": The remote source directory from which files are going to be transferred")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("session-factory")]),e._v(": A reference to the bean we configured earlier")])])]),e._v(" "),o("p",[e._v("By default, the transferred file carries the same name as the original file.\nIf you want to override this behavior, you can set the "),o("code",[e._v("local-filename-generator-expression")]),e._v(" attribute, which lets you provide a SpEL expression to generate the name of the local file.\nUnlike outbound gateways and adapters, where the root object of the SpEL evaluation context is a "),o("code",[e._v("Message")]),e._v(", this inbound adapter does not yet have the message at the time of evaluation, since that is what it ultimately generates with the transferred file as its payload.\nConsequently, the root object of the SpEL evaluation context is the original name of the remote file (a "),o("code",[e._v("String")]),e._v(").")]),e._v(" "),o("p",[e._v("The inbound channel adapter first retrieves the file to a local directory and then emits each file according to the poller configuration.\nStarting with version 5.0, you can limit the number of files fetched from the SFTP server when new file retrievals are needed.\nThis can be beneficial when the target files are large or when running in a clustered system with a persistent file list filter, discussed later in this section.\nUse "),o("code",[e._v("max-fetch-size")]),e._v(" for this purpose.\nA negative value (the default) means no limit and all matching files are retrieved.\nSee "),o("a",{attrs:{href:"#sftp-max-fetch"}},[e._v("Inbound Channel Adapters: Controlling Remote File Fetching")]),e._v(" for more information.\nSince version 5.0, you can also provide a custom "),o("code",[e._v("DirectoryScanner")]),e._v(" implementation to the "),o("code",[e._v("inbound-channel-adapter")]),e._v(" by setting the "),o("code",[e._v("scanner")]),e._v(" attribute.")]),e._v(" "),o("p",[e._v("Starting with Spring Integration 3.0, you can specify the "),o("code",[e._v("preserve-timestamp")]),e._v(" attribute (the default is "),o("code",[e._v("false")]),e._v(").\nWhen "),o("code",[e._v("true")]),e._v(", the local file’s modified timestamp is set to the value retrieved from the server.\nOtherwise, it is set to the current time.")]),e._v(" "),o("p",[e._v("Starting with version 4.2, you can specify "),o("code",[e._v("remote-directory-expression")]),e._v(" instead of "),o("code",[e._v("remote-directory")]),e._v(", which lets\nyou dynamically determine the directory on each poll — for example, "),o("code",[e._v('remote-directory-expression="@myBean.determineRemoteDir()"')]),e._v(".")]),e._v(" "),o("p",[e._v("Sometimes, file filtering based on the simple pattern specified via "),o("code",[e._v("filename-pattern")]),e._v(" attribute might not suffice.\nIf this is the case, you can use the "),o("code",[e._v("filename-regex")]),e._v(" attribute to specify a regular expression (for example, "),o("code",[e._v('filename-regex=".*\\.test$"')]),e._v(").\nIf you need complete control, you can use the "),o("code",[e._v("filter")]),e._v(" attribute to provide a reference to a custom implementation of the "),o("code",[e._v("org.springframework.integration.file.filters.FileListFilter")]),e._v(", which is a strategy interface for filtering a list of files.\nThis filter determines which remote files are retrieved.\nYou can also combine a pattern-based filter with other filters (such as an "),o("code",[e._v("AcceptOnceFileListFilter")]),e._v(", to avoid synchronizing files that have previously been fetched) by using a "),o("code",[e._v("CompositeFileListFilter")]),e._v(".")]),e._v(" "),o("p",[e._v("The "),o("code",[e._v("AcceptOnceFileListFilter")]),e._v(" stores its state in memory.\nIf you wish the state to survive a system restart, consider using the "),o("code",[e._v("SftpPersistentAcceptOnceFileListFilter")]),e._v(" instead.\nThis filter stores the accepted file names in an instance of the "),o("code",[e._v("MetadataStore")]),e._v(" strategy (see "),o("RouterLink",{attrs:{to:"/en/spring-integration/meta-data-store.html#metadata-store"}},[e._v("Metadata Store")]),e._v(").\nThis filter matches on the filename and the remote modified time.")],1),e._v(" "),o("p",[e._v("Since version 4.0, this filter requires a "),o("code",[e._v("ConcurrentMetadataStore")]),e._v(".\nWhen used with a shared data store (such as "),o("code",[e._v("Redis")]),e._v(" with the "),o("code",[e._v("RedisMetadataStore")]),e._v("), this lets filter keys be shared across multiple application or server instances.")]),e._v(" "),o("p",[e._v("Starting with version 5.0, the "),o("code",[e._v("SftpPersistentAcceptOnceFileListFilter")]),e._v(" with an in-memory "),o("code",[e._v("SimpleMetadataStore")]),e._v(" is applied by default for the "),o("code",[e._v("SftpInboundFileSynchronizer")]),e._v(".\nThis filter is also applied, together with the "),o("code",[e._v("regex")]),e._v(" or "),o("code",[e._v("pattern")]),e._v(" option in the XML configuration, as well as through "),o("code",[e._v("SftpInboundChannelAdapterSpec")]),e._v(" in Java DSL.\nYou can handle any other use-cases by using "),o("code",[e._v("CompositeFileListFilter")]),e._v(" (or "),o("code",[e._v("ChainFileListFilter")]),e._v(").")]),e._v(" "),o("p",[e._v("The above discussion refers to filtering the files before retrieving them.\nOnce the files have been retrieved, an additional filter is applied to the files on the file system.\nBy default, this is an"),o("code",[e._v("AcceptOnceFileListFilter")]),e._v(", which, as discussed in this section, retains state in memory and does not consider the file’s modified time.\nUnless your application removes files after processing, the adapter re-processes the files on disk by default after an application restart.")]),e._v(" "),o("p",[e._v("Also, if you configure the "),o("code",[e._v("filter")]),e._v(" to use a "),o("code",[e._v("SftpPersistentAcceptOnceFileListFilter")]),e._v(" and the remote file timestamp changes (causing it to be re-fetched), the default local filter does not allow this new file to be processed.")]),e._v(" "),o("p",[e._v("For more information about this filter, and how it is used, see "),o("RouterLink",{attrs:{to:"/en/spring-integration/file.html#remote-persistent-flf"}},[e._v("Remote Persistent File List Filters")]),e._v(".")],1),e._v(" "),o("p",[e._v("You can use the "),o("code",[e._v("local-filter")]),e._v(" attribute to configure the behavior of the local file system filter.\nStarting with version 4.3.8, a "),o("code",[e._v("FileSystemPersistentAcceptOnceFileListFilter")]),e._v(" is configured by default.\nThis filter stores the accepted file names and modified timestamp in an instance of the "),o("code",[e._v("MetadataStore")]),e._v(" strategy (see "),o("RouterLink",{attrs:{to:"/en/spring-integration/meta-data-store.html#metadata-store"}},[e._v("Metadata Store")]),e._v(") and detects changes to the local file modified time.\nThe default "),o("code",[e._v("MetadataStore")]),e._v(" is a "),o("code",[e._v("SimpleMetadataStore")]),e._v(" that stores state in memory.")],1),e._v(" "),o("p",[e._v("Since version 4.1.5, these filters have a new property called "),o("code",[e._v("flushOnUpdate")]),e._v(", which causes them to flush the\nmetadata store on every update (if the store implements "),o("code",[e._v("Flushable")]),e._v(").")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("Further, if you use a distributed "),o("code",[e._v("MetadataStore")]),e._v(" (such as "),o("RouterLink",{attrs:{to:"/en/spring-integration/redis.html#redis-metadata-store"}},[e._v("Redis Metadata Store")]),e._v(" or "),o("RouterLink",{attrs:{to:"/en/spring-integration/gemfire.html#gemfire-metadata-store"}},[e._v("Gemfire Metadata Store")]),e._v("), you can have multiple instances of the same adapter or application and be sure that one and only one instance processes a file.")],1)])]),e._v(" "),o("tbody")]),e._v(" "),o("p",[e._v("The actual local filter is a "),o("code",[e._v("CompositeFileListFilter")]),e._v(" that contains the supplied filter and a pattern filter that prevents processing files that are in the process of being downloaded (based on the "),o("code",[e._v("temporary-file-suffix")]),e._v(").\nFiles are downloaded with this suffix (the default is "),o("code",[e._v(".writing")]),e._v("), and the files are renamed to their final names when the transfer is complete, making them 'visible' to the filter.")]),e._v(" "),o("p",[e._v("See the "),o("a",{attrs:{href:"https://github.com/spring-projects/spring-integration/tree/main/spring-integration-core/src/main/resources/org/springframework/integration/config",target:"_blank",rel:"noopener noreferrer"}},[e._v("schema"),o("OutboundLink")],1),e._v(" for more detail on these attributes.")]),e._v(" "),o("p",[e._v("SFTP inbound channel adapter is a polling consumer.\nTherefore, you must configure a poller (either a global default or a local element).\nOnce the file has been transferred to a local directory, a message with "),o("code",[e._v("java.io.File")]),e._v(" as its payload type is generated and sent to the channel identified by the "),o("code",[e._v("channel")]),e._v(" attribute.")]),e._v(" "),o("h4",{attrs:{id:"more-on-file-filtering-and-large-files"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#more-on-file-filtering-and-large-files"}},[e._v("#")]),e._v(" More on File Filtering and Large Files")]),e._v(" "),o("p",[e._v("Sometimes, a file that just appeared in the monitored (remote) directory is not complete.\nTypically such a file is written with some temporary extension (such as "),o("code",[e._v(".writing")]),e._v(" on a file named "),o("code",[e._v("something.txt.writing")]),e._v(") and then renamed after the writing process completes.\nIn most cases, developers are interested only in files that are complete and would like to filter only those files.\nTo handle these scenarios, you can use the filtering support provided by the "),o("code",[e._v("filename-pattern")]),e._v(", "),o("code",[e._v("filename-regex")]),e._v(", and "),o("code",[e._v("filter")]),e._v(" attributes.\nIf you need a custom filter implementation, you can include a reference in your adapter by setting the "),o("code",[e._v("filter")]),e._v(" attribute.\nThe following example shows how to do so:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n \n\n\n\n')])])]),o("h4",{attrs:{id:"recovering-from-failures"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#recovering-from-failures"}},[e._v("#")]),e._v(" Recovering from Failures")]),e._v(" "),o("p",[e._v("You should understand the architecture of the adapter.\nA file synchronizer fetches the files, and a "),o("code",[e._v("FileReadingMessageSource")]),e._v(" emits a message for each synchronized file.\nAs "),o("a",{attrs:{href:"#sftp-inbound"}},[e._v("discussed earlier")]),e._v(", two filters are involved.\nThe "),o("code",[e._v("filter")]),e._v(" attribute (and patterns) refers to the remote (SFTP) file list, to avoid fetching files that have already\nbeen fetched.\nthe "),o("code",[e._v("FileReadingMessageSource")]),e._v(" uses the "),o("code",[e._v("local-filter")]),e._v(" to determine which files are to be sent as messages.")]),e._v(" "),o("p",[e._v("The synchronizer lists the remote files and consults its filter.\nThe files are then transferred.\nIf an IO error occurs during file transfer, any files that have already been added to the filter are removed so that they\nare eligible to be re-fetched on the next poll.\nThis applies only if the filter implements "),o("code",[e._v("ReversibleFileListFilter")]),e._v(" (such as the "),o("code",[e._v("AcceptOnceFileListFilter")]),e._v(").")]),e._v(" "),o("p",[e._v("If, after synchronizing the files, an error occurs on the downstream flow processing a file, no automatic rollback of the filter occurs, so the failed file is not reprocessed by default.")]),e._v(" "),o("p",[e._v("If you wish to reprocess such files after a failure, you can use a configuration similar to the following to facilitate\nthe removal of the failed file from the filter:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n \n \n \n\n\n\n\n\n \n\n\n\n')])])]),o("p",[e._v("The preceding configuration works for any "),o("code",[e._v("ResettableFileListFilter")]),e._v(".")]),e._v(" "),o("p",[e._v("Starting with version 5.0, the inbound channel adapter can build sub-directories locally, according to the generated local file name.\nThat can be a remote sub-path as well.\nTo be able to read a local directory recursively for modification according to the hierarchy support, you can now supply an internal "),o("code",[e._v("FileReadingMessageSource")]),e._v(" with a new "),o("code",[e._v("RecursiveDirectoryScanner")]),e._v(" based on the "),o("code",[e._v("Files.walk()")]),e._v(" algorithm.\nSee "),o("a",{attrs:{href:"https://docs.spring.io/spring-integration/api/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizingMessageSource.html#setScanner-org.springframework.integration.file.DirectoryScanner",target:"_blank",rel:"noopener noreferrer"}},[o("code",[e._v("AbstractInboundFileSynchronizingMessageSource.setScanner()")]),o("OutboundLink")],1),e._v(" for more information.\nAlso, you can now switch the "),o("code",[e._v("AbstractInboundFileSynchronizingMessageSource")]),e._v(" to the "),o("code",[e._v("WatchService")]),e._v("-based "),o("code",[e._v("DirectoryScanner")]),e._v(" by using "),o("code",[e._v("setUseWatchService()")]),e._v(" option.\nIt is also configured for all the "),o("code",[e._v("WatchEventType")]),e._v(" instances to react for any modifications in local directory.\nThe reprocessing sample shown earlier is based on the built-in functionality of the "),o("code",[e._v("FileReadingMessageSource.WatchServiceDirectoryScanner")]),e._v(", which uses "),o("code",[e._v("ResettableFileListFilter.remove()")]),e._v(" when the file is deleted ("),o("code",[e._v("StandardWatchEventKinds.ENTRY_DELETE")]),e._v(") from the local directory.\nSee "),o("RouterLink",{attrs:{to:"/en/spring-integration/file.html#watch-service-directory-scanner"}},[o("code",[e._v("WatchServiceDirectoryScanner")])]),e._v(" for more information.")],1),e._v(" "),o("h4",{attrs:{id:"configuring-with-java-configuration"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#configuring-with-java-configuration"}},[e._v("#")]),e._v(" Configuring with Java Configuration")]),e._v(" "),o("p",[e._v("The following Spring Boot application shows an example of how to configure the inbound adapter with Java:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@SpringBootApplication\npublic class SftpJavaApplication {\n\n public static void main(String[] args) {\n new SpringApplicationBuilder(SftpJavaApplication.class)\n .web(false)\n .run(args);\n }\n\n @Bean\n public SessionFactory sftpSessionFactory() {\n DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);\n factory.setHost("localhost");\n factory.setPort(port);\n factory.setUser("foo");\n factory.setPassword("foo");\n factory.setAllowUnknownKeys(true);\n factory.setTestSession(true);\n return new CachingSessionFactory(factory);\n }\n\n @Bean\n public SftpInboundFileSynchronizer sftpInboundFileSynchronizer() {\n SftpInboundFileSynchronizer fileSynchronizer = new SftpInboundFileSynchronizer(sftpSessionFactory());\n fileSynchronizer.setDeleteRemoteFiles(false);\n fileSynchronizer.setRemoteDirectory("foo");\n fileSynchronizer.setFilter(new SftpSimplePatternFileListFilter("*.xml"));\n return fileSynchronizer;\n }\n\n @Bean\n @InboundChannelAdapter(channel = "sftpChannel", poller = @Poller(fixedDelay = "5000"))\n public MessageSource sftpMessageSource() {\n SftpInboundFileSynchronizingMessageSource source =\n new SftpInboundFileSynchronizingMessageSource(sftpInboundFileSynchronizer());\n source.setLocalDirectory(new File("sftp-inbound"));\n source.setAutoCreateLocalDirectory(true);\n source.setLocalFilter(new AcceptOnceFileListFilter());\n source.setMaxFetchSize(1);\n return source;\n }\n\n @Bean\n @ServiceActivator(inputChannel = "sftpChannel")\n public MessageHandler handler() {\n return new MessageHandler() {\n\n @Override\n public void handleMessage(Message message) throws MessagingException {\n System.out.println(message.getPayload());\n }\n\n };\n }\n\n}\n')])])]),o("h4",{attrs:{id:"configuring-with-the-java-dsl"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#configuring-with-the-java-dsl"}},[e._v("#")]),e._v(" Configuring with the Java DSL")]),e._v(" "),o("p",[e._v("The following Spring Boot application shows an example of how to configure the inbound adapter with the Java DSL:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@SpringBootApplication\npublic class SftpJavaApplication {\n\n public static void main(String[] args) {\n new SpringApplicationBuilder(SftpJavaApplication.class)\n .web(false)\n .run(args);\n }\n\n @Bean\n public IntegrationFlow sftpInboundFlow() {\n return IntegrationFlows\n .from(Sftp.inboundAdapter(this.sftpSessionFactory)\n .preserveTimestamp(true)\n .remoteDirectory("foo")\n .regexFilter(".*\\\\.txt$")\n .localFilenameExpression("#this.toUpperCase() + \'.a\'")\n .localDirectory(new File("sftp-inbound")),\n e -> e.id("sftpInboundAdapter")\n .autoStartup(true)\n .poller(Pollers.fixedDelay(5000)))\n .handle(m -> System.out.println(m.getPayload()))\n .get();\n }\n}\n')])])]),o("h4",{attrs:{id:"dealing-with-incomplete-data"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#dealing-with-incomplete-data"}},[e._v("#")]),e._v(" Dealing With Incomplete Data")]),e._v(" "),o("p",[e._v("See "),o("RouterLink",{attrs:{to:"/en/spring-integration/file.html#file-incomplete"}},[e._v("Dealing With Incomplete Data")]),e._v(".")],1),e._v(" "),o("p",[e._v("The "),o("code",[e._v("SftpSystemMarkerFilePresentFileListFilter")]),e._v(" is provided to filter remote files that don’t have the corresponding marker file on the remote system.\nSee the "),o("a",{attrs:{href:"https://docs.spring.io/spring-integration/api/org/springframework/integration/sftp/filters/SftpSystemMarkerFilePresentFileListFilter.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("Javadoc"),o("OutboundLink")],1),e._v(" for configuration information.")]),e._v(" "),o("h3",{attrs:{id:"sftp-streaming-inbound-channel-adapter"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#sftp-streaming-inbound-channel-adapter"}},[e._v("#")]),e._v(" SFTP Streaming Inbound Channel Adapter")]),e._v(" "),o("p",[e._v("Version 4.3 introduced the streaming inbound channel adapter.\nThis adapter produces message with payloads of type "),o("code",[e._v("InputStream")]),e._v(", letting you fetch files without writing to the local file system.\nSince the session remains open, the consuming application is responsible for closing the session when the file has been consumed.\nThe session is provided in the "),o("code",[e._v("closeableResource")]),e._v(" header ("),o("code",[e._v("IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE")]),e._v(").\nStandard framework components, such as the "),o("code",[e._v("FileSplitter")]),e._v(" and "),o("code",[e._v("StreamTransformer")]),e._v(", automatically close the session.\nSee "),o("RouterLink",{attrs:{to:"/en/spring-integration/file.html#file-splitter"}},[e._v("File Splitter")]),e._v(" and "),o("RouterLink",{attrs:{to:"/en/spring-integration/transformer.html#stream-transformer"}},[e._v("Stream Transformer")]),e._v(" for more information about these components.\nThe following example shows how to configure an SFTP streaming inbound channel adapter:")],1),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n \n\n')])])]),o("p",[e._v("You can use only one of "),o("code",[e._v("filename-pattern")]),e._v(", "),o("code",[e._v("filename-regex")]),e._v(", "),o("code",[e._v("filter")]),e._v(", or "),o("code",[e._v("filter-expression")]),e._v(".")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("Starting with version 5.0, by default, the "),o("code",[e._v("SftpStreamingMessageSource")]),e._v(" adapter prevents duplicates for remote files by using "),o("code",[e._v("SftpPersistentAcceptOnceFileListFilter")]),e._v(" based on the in-memory "),o("code",[e._v("SimpleMetadataStore")]),e._v("."),o("br"),e._v("By default, this filter is also applied together with the filename pattern (or regex) as well."),o("br"),e._v("If you need to allow duplicates, you can use the "),o("code",[e._v("AcceptAllFileListFilter")]),e._v("."),o("br"),e._v("You can handle any other use cases by using "),o("code",[e._v("CompositeFileListFilter")]),e._v(" (or "),o("code",[e._v("ChainFileListFilter")]),e._v(")."),o("br"),e._v("The Java configuration "),o("a",{attrs:{href:"#sftp-streaming-java-config"}},[e._v("shown later")]),e._v(" shows one technique to remove the remote file after processing, avoiding duplicates.")])])]),e._v(" "),o("tbody")]),e._v(" "),o("p",[e._v("For more information about the "),o("code",[e._v("SftpPersistentAcceptOnceFileListFilter")]),e._v(", and how it is used, see "),o("RouterLink",{attrs:{to:"/en/spring-integration/file.html#remote-persistent-flf"}},[e._v("Remote Persistent File List Filters")]),e._v(".")],1),e._v(" "),o("p",[e._v("You can use the "),o("code",[e._v("max-fetch-size")]),e._v(" attribute to limit the number of files fetched on each poll when a fetch is necessary.\nSet it to "),o("code",[e._v("1")]),e._v(" and use a persistent filter when running in a clustered environment.\nSee "),o("a",{attrs:{href:"#sftp-max-fetch"}},[e._v("Inbound Channel Adapters: Controlling Remote File Fetching")]),e._v(" for more information.")]),e._v(" "),o("p",[e._v("The adapter puts the remote directory and the file name in headers ("),o("code",[e._v("FileHeaders.REMOTE_DIRECTORY")]),e._v(" and "),o("code",[e._v("FileHeaders.REMOTE_FILE")]),e._v(", respectively).\nStarting with version 5.0, the "),o("code",[e._v("FileHeaders.REMOTE_FILE_INFO")]),e._v(" header provides additional remote file information (in JSON).\nIf you set the "),o("code",[e._v("fileInfoJson")]),e._v(" property on the "),o("code",[e._v("SftpStreamingMessageSource")]),e._v(" to "),o("code",[e._v("false")]),e._v(", the header contains an "),o("code",[e._v("SftpFileInfo")]),e._v(" object.\nYou can access the "),o("code",[e._v("LsEntry")]),e._v(" object provided by the underlying Jsch library by using the "),o("code",[e._v("SftpFileInfo.getFileInfo()")]),e._v(" method.\nThe "),o("code",[e._v("fileInfoJson")]),e._v(" property is not available when you use XML configuration, but you can set it by injecting the "),o("code",[e._v("SftpStreamingMessageSource")]),e._v(" into one of your configuration classes.\nSee also "),o("a",{attrs:{href:"#sftp-remote-file-info"}},[e._v("Remote File Information")]),e._v(".")]),e._v(" "),o("p",[e._v("Starting with version 5.1, the generic type of the "),o("code",[e._v("comparator")]),e._v(" is "),o("code",[e._v("LsEntry")]),e._v(".\nPreviously, it was "),o("code",[e._v("AbstractFileInfo")]),e._v(".\nThis is because the sort is now performed earlier in the processing, before filtering and applying "),o("code",[e._v("maxFetch")]),e._v(".")]),e._v(" "),o("h4",{attrs:{id:"configuring-with-java-configuration-2"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#configuring-with-java-configuration-2"}},[e._v("#")]),e._v(" Configuring with Java Configuration")]),e._v(" "),o("p",[e._v("The following Spring Boot application shows an example of how to configure the inbound adapter with Java:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@SpringBootApplication\npublic class SftpJavaApplication {\n\n public static void main(String[] args) {\n new SpringApplicationBuilder(SftpJavaApplication.class)\n .web(false)\n .run(args);\n }\n\n @Bean\n @InboundChannelAdapter(channel = "stream")\n public MessageSource ftpMessageSource() {\n SftpStreamingMessageSource messageSource = new SftpStreamingMessageSource(template());\n messageSource.setRemoteDirectory("sftpSource/");\n messageSource.setFilter(new AcceptAllFileListFilter<>());\n messageSource.setMaxFetchSize(1);\n return messageSource;\n }\n\n @Bean\n @Transformer(inputChannel = "stream", outputChannel = "data")\n public org.springframework.integration.transformer.Transformer transformer() {\n return new StreamTransformer("UTF-8");\n }\n\n @Bean\n public SftpRemoteFileTemplate template() {\n return new SftpRemoteFileTemplate(sftpSessionFactory());\n }\n\n @ServiceActivator(inputChannel = "data", adviceChain = "after")\n @Bean\n public MessageHandler handle() {\n return System.out::println;\n }\n\n @Bean\n public ExpressionEvaluatingRequestHandlerAdvice after() {\n ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();\n advice.setOnSuccessExpression(\n "@template.remove(headers[\'file_remoteDirectory\'] + headers[\'file_remoteFile\'])");\n advice.setPropagateEvaluationFailures(true);\n return advice;\n }\n\n}\n')])])]),o("p",[e._v("Notice that, in this example, the message handler downstream of the transformer has an advice that removes the remote file after processing.")]),e._v(" "),o("h3",{attrs:{id:"inbound-channel-adapters-polling-multiple-servers-and-directories"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#inbound-channel-adapters-polling-multiple-servers-and-directories"}},[e._v("#")]),e._v(" Inbound Channel Adapters: Polling Multiple Servers and Directories")]),e._v(" "),o("p",[e._v("Starting with version 5.0.7, the "),o("code",[e._v("RotatingServerAdvice")]),e._v(" is available; when configured as a poller advice, the inbound adapters can poll multiple servers and directories.\nConfigure the advice and add it to the poller’s advice chain as normal.\nA "),o("code",[e._v("DelegatingSessionFactory")]),e._v(" is used to select the server see "),o("RouterLink",{attrs:{to:"/en/spring-integration/ftp.html#ftp-dsf"}},[e._v("Delegating Session Factory")]),e._v(" for more information.\nThe advice configuration consists of a list of "),o("code",[e._v("RotationPolicy.KeyDirectory")]),e._v(" objects.")],1),e._v(" "),o("p",[e._v("Example")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@Bean\npublic RotatingServerAdvice advice() {\n List keyDirectories = new ArrayList<>();\n keyDirectories.add(new RotationPolicy.KeyDirectory("one", "foo"));\n keyDirectories.add(new RotationPolicy.KeyDirectory("one", "bar"));\n keyDirectories.add(new RotationPolicy.KeyDirectory("two", "baz"));\n keyDirectories.add(new RotationPolicy.KeyDirectory("two", "qux"));\n keyDirectories.add(new RotationPolicy.KeyDirectory("three", "fiz"));\n keyDirectories.add(new RotationPolicy.KeyDirectory("three", "buz"));\n return new RotatingServerAdvice(delegatingSf(), keyDirectories);\n}\n')])])]),o("p",[e._v("This advice will poll directory "),o("code",[e._v("foo")]),e._v(" on server "),o("code",[e._v("one")]),e._v(" until no new files exist then move to directory "),o("code",[e._v("bar")]),e._v(" and then directory "),o("code",[e._v("baz")]),e._v(" on server "),o("code",[e._v("two")]),e._v(", etc.")]),e._v(" "),o("p",[e._v("This default behavior can be modified with the "),o("code",[e._v("fair")]),e._v(" constructor arg:")]),e._v(" "),o("p",[e._v("fair")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v("@Bean\npublic RotatingServerAdvice advice() {\n ...\n return new RotatingServerAdvice(delegatingSf(), keyDirectories, true);\n}\n")])])]),o("p",[e._v("In this case, the advice will move to the next server/directory regardless of whether the previous poll returned a file.")]),e._v(" "),o("p",[e._v("Alternatively, you can provide your own "),o("code",[e._v("RotationPolicy")]),e._v(" to reconfigure the message source as needed:")]),e._v(" "),o("p",[e._v("policy")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v("public interface RotationPolicy {\n\n void beforeReceive(MessageSource source);\n\n void afterReceive(boolean messageReceived, MessageSource source);\n\n}\n")])])]),o("p",[e._v("and")]),e._v(" "),o("p",[e._v("custom")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v("@Bean\npublic RotatingServerAdvice advice() {\n return new RotatingServerAdvice(myRotationPolicy());\n}\n")])])]),o("p",[e._v("The "),o("code",[e._v("local-filename-generator-expression")]),e._v(" attribute ("),o("code",[e._v("localFilenameGeneratorExpression")]),e._v(" on the synchronizer) can now contain the "),o("code",[e._v("#remoteDirectory")]),e._v(" variable.\nThis allows files retrieved from different directories to be downloaded to similar directories locally:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@Bean\npublic IntegrationFlow flow() {\n return IntegrationFlows.from(Sftp.inboundAdapter(sf())\n .filter(new SftpPersistentAcceptOnceFileListFilter(new SimpleMetadataStore(), "rotate"))\n .localDirectory(new File(tmpDir))\n .localFilenameExpression("#remoteDirectory + T(java.io.File).separator + #root")\n .remoteDirectory("."),\n e -> e.poller(Pollers.fixedDelay(1).advice(advice())))\n .channel(MessageChannels.queue("files"))\n .get();\n}\n')])])]),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("Do not configure a "),o("code",[e._v("TaskExecutor")]),e._v(" on the poller when using this advice; see "),o("RouterLink",{attrs:{to:"/en/spring-integration/polling-consumer.html#conditional-pollers"}},[e._v("Conditional Pollers for Message Sources")]),e._v(" for more information.")],1)])]),e._v(" "),o("tbody")]),e._v(" "),o("h3",{attrs:{id:"inbound-channel-adapters-controlling-remote-file-fetching"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#inbound-channel-adapters-controlling-remote-file-fetching"}},[e._v("#")]),e._v(" Inbound Channel Adapters: Controlling Remote File Fetching")]),e._v(" "),o("p",[e._v("You should consider two properties when configuring inbound channel adapters."),o("code",[e._v("max-messages-per-poll")]),e._v(", as with all pollers, can be used to limit the number of messages emitted on each poll (if more than the configured value are ready)."),o("code",[e._v("max-fetch-size")]),e._v(" (since version 5.0) can limit the number of files retrieved from the remote server at a time.")]),e._v(" "),o("p",[e._v("The following scenarios assume the starting state is an empty local directory:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("max-messages-per-poll=2")]),e._v(" and "),o("code",[e._v("max-fetch-size=1")]),e._v(": The adapter fetches one file, emits it, fetches the next file, and emit it.\nThen it sleeps until the next poll.")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("max-messages-per-poll=2")]),e._v(" and "),o("code",[e._v("max-fetch-size=2")]),e._v("): The adapter fetch both files and then emits each one.")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("max-messages-per-poll=2")]),e._v(" and "),o("code",[e._v("max-fetch-size=4")]),e._v(": The adapter fetches up to 4 files (if available) and emits the first two (if there are at least two).\nThe next two files will be emitted on the next poll.")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("max-messages-per-poll=2")]),e._v(" and "),o("code",[e._v("max-fetch-size")]),e._v(" not specified: The adapter fetches all remote files and emits the first two (if there are at least two).\nThe subsequent files are emitted on subsequent polls (two at a time).\nWhen all are consumed, the remote fetch is attempted again, to pick up any new files.")])])]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("When you deploy multiple instances of an application, we recommend setting a small "),o("code",[e._v("max-fetch-size")]),e._v(", to avoid one instance “grabbing” all the files and starving other instances.")])])]),e._v(" "),o("tbody")]),e._v(" "),o("p",[e._v("Another use for "),o("code",[e._v("max-fetch-size")]),e._v(" is when you want to stop fetching remote files but continue to process files that have already been fetched.\nSetting the "),o("code",[e._v("maxFetchSize")]),e._v(" property on the "),o("code",[e._v("MessageSource")]),e._v(" (programmatically, via JMX, or via a "),o("RouterLink",{attrs:{to:"/en/spring-integration/control-bus.html#control-bus"}},[e._v("control bus")]),e._v(") effectively stops the adapter from fetching more files but lets the poller continue to emit messages for files that have previously been fetched.\nIf the poller is active when the property is changed, the change takes effect on the next poll.")],1),e._v(" "),o("p",[e._v("Starting with version 5.1, the synchronizer can be provided with a "),o("code",[e._v("Comparator")]),e._v(".\nThis is useful when restricting the number of files fetched with "),o("code",[e._v("maxFetchSize")]),e._v(".")]),e._v(" "),o("h3",{attrs:{id:"sftp-outbound-channel-adapter"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#sftp-outbound-channel-adapter"}},[e._v("#")]),e._v(" SFTP Outbound Channel Adapter")]),e._v(" "),o("p",[e._v("The SFTP outbound channel adapter is a special "),o("code",[e._v("MessageHandler")]),e._v(" that connects to the remote directory and initiates a file transfer for every file it receives as the payload of an incoming "),o("code",[e._v("Message")]),e._v(".\nIt also supports several representations of the file so that you are not limited to the "),o("code",[e._v("File")]),e._v(" object.\nSimilar to the FTP outbound adapter, the SFTP outbound channel adapter supports the following payloads:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("java.io.File")]),e._v(": The actual file object")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("byte[]")]),e._v(": A byte array that represents the file contents")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("java.lang.String")]),e._v(": Text that represents the file contents")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("java.io.InputStream")]),e._v(": a stream of data to transfer to remote file")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("org.springframework.core.io.Resource")]),e._v(": a resource for data to transfer to remote file")])])]),e._v(" "),o("p",[e._v("The following example shows how to configure an SFTP outbound channel adapter:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n')])])]),o("p",[e._v("See the "),o("a",{attrs:{href:"https://github.com/spring-projects/spring-integration/tree/main/spring-integration-core/src/main/resources/org/springframework/integration/config",target:"_blank",rel:"noopener noreferrer"}},[e._v("schema"),o("OutboundLink")],1),e._v(" for more detail on these attributes.")]),e._v(" "),o("h4",{attrs:{id:"spel-and-the-sftp-outbound-adapter"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#spel-and-the-sftp-outbound-adapter"}},[e._v("#")]),e._v(" SpEL and the SFTP Outbound Adapter")]),e._v(" "),o("p",[e._v("As with many other components in Spring Integration, you can use the Spring Expression Language (SpEL) when you configure an SFTP outbound channel adapter by specifying two attributes: "),o("code",[e._v("remote-directory-expression")]),e._v(" and "),o("code",[e._v("remote-filename-generator-expression")]),e._v(" ("),o("a",{attrs:{href:"#sftp-inbound"}},[e._v("described earlier")]),e._v(").\nThe expression evaluation context has the message as its root object, which lets you use expressions that can dynamically compute the file name or the existing directory path based on the data in the message (from either the 'payload' or the 'headers').\nIn the preceding example, we define the "),o("code",[e._v("remote-filename-generator-expression")]),e._v(" attribute with an expression value that computes the file name based on its original name while also appending a suffix: '-mysuffix'.")]),e._v(" "),o("p",[e._v("Starting with version 4.1, you can specify the "),o("code",[e._v("mode")]),e._v(" when you transferring the file.\nBy default, an existing file is overwritten.\nThe modes are defined by the "),o("code",[e._v("FileExistsMode")]),e._v(" enumeration, which includes the following values:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("REPLACE")]),e._v(" (default)")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("REPLACE_IF_MODIFIED")])])]),e._v(" "),o("li",[o("p",[o("code",[e._v("APPEND")])])]),e._v(" "),o("li",[o("p",[o("code",[e._v("APPEND_NO_FLUSH")])])]),e._v(" "),o("li",[o("p",[o("code",[e._v("IGNORE")])])]),e._v(" "),o("li",[o("p",[o("code",[e._v("FAIL")])])])]),e._v(" "),o("p",[e._v("With "),o("code",[e._v("IGNORE")]),e._v(" and "),o("code",[e._v("FAIL")]),e._v(", the file is not transferred."),o("code",[e._v("FAIL")]),e._v(" causes an exception to be thrown, while "),o("code",[e._v("IGNORE")]),e._v(" silently ignores the transfer (although a "),o("code",[e._v("DEBUG")]),e._v(" log entry is produced).")]),e._v(" "),o("p",[e._v("Version 4.3 introduced the "),o("code",[e._v("chmod")]),e._v(" attribute, which you can use to change the remote file permissions after upload.\nYou can use the conventional Unix octal format (for example, "),o("code",[e._v("600")]),e._v(" allows read-write for the file owner only).\nWhen configuring the adapter using java, you can use "),o("code",[e._v('setChmodOctal("600")')]),e._v(" or "),o("code",[e._v("setChmod(0600)")]),e._v(".")]),e._v(" "),o("h4",{attrs:{id:"avoiding-partially-written-files"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#avoiding-partially-written-files"}},[e._v("#")]),e._v(" Avoiding Partially Written Files")]),e._v(" "),o("p",[e._v("One of the common problems when dealing with file transfers is the possibility of processing a partial file.\nA file might appear in the file system before its transfer is actually complete.")]),e._v(" "),o("p",[e._v("To deal with this issue, Spring Integration SFTP adapters use a common algorithm in which files are transferred under a temporary name and than renamed once they are fully transferred.")]),e._v(" "),o("p",[e._v("By default, every file that is in the process of being transferred appear in the file system with an additional suffix, which, by default, is "),o("code",[e._v(".writing")]),e._v(".\nYou can change by setting the "),o("code",[e._v("temporary-file-suffix")]),e._v(" attribute.")]),e._v(" "),o("p",[e._v("However, there may be situations where you do not want to use this technique (for example, if the server does not permit renaming files).\nFor situations like this, you can disable this feature by setting "),o("code",[e._v("use-temporary-file-name")]),e._v(" to "),o("code",[e._v("false")]),e._v(" (the default is "),o("code",[e._v("true")]),e._v(").\nWhen this attribute is "),o("code",[e._v("false")]),e._v(", the file is written with its final name, and the consuming application needs some other mechanism to detect that the file is completely uploaded before accessing it.")]),e._v(" "),o("h4",{attrs:{id:"configuring-with-java-configuration-3"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#configuring-with-java-configuration-3"}},[e._v("#")]),e._v(" Configuring with Java Configuration")]),e._v(" "),o("p",[e._v("The following Spring Boot application shows an example of how to configure the outbound adapter with Java:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@SpringBootApplication\n@IntegrationComponentScan\npublic class SftpJavaApplication {\n\n public static void main(String[] args) {\n ConfigurableApplicationContext context =\n new SpringApplicationBuilder(SftpJavaApplication.class)\n .web(false)\n .run(args);\n MyGateway gateway = context.getBean(MyGateway.class);\n gateway.sendToSftp(new File("/foo/bar.txt"));\n }\n\n @Bean\n public SessionFactory sftpSessionFactory() {\n DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);\n factory.setHost("localhost");\n factory.setPort(port);\n factory.setUser("foo");\n factory.setPassword("foo");\n factory.setAllowUnknownKeys(true);\n factory.setTestSession(true);\n return new CachingSessionFactory(factory);\n }\n\n @Bean\n @ServiceActivator(inputChannel = "toSftpChannel")\n public MessageHandler handler() {\n SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());\n handler.setRemoteDirectoryExpressionString("headers[\'remote-target-dir\']");\n handler.setFileNameGenerator(new FileNameGenerator() {\n\n @Override\n public String generateFileName(Message message) {\n return "handlerContent.test";\n }\n\n });\n return handler;\n }\n\n @MessagingGateway\n public interface MyGateway {\n\n @Gateway(requestChannel = "toSftpChannel")\n void sendToSftp(File file);\n\n }\n}\n')])])]),o("h4",{attrs:{id:"configuring-with-the-java-dsl-2"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#configuring-with-the-java-dsl-2"}},[e._v("#")]),e._v(" Configuring with the Java DSL")]),e._v(" "),o("p",[e._v("The following Spring Boot application shows an example of how to configure the outbound adapter with the Java DSL:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@SpringBootApplication\npublic class SftpJavaApplication {\n\n public static void main(String[] args) {\n new SpringApplicationBuilder(SftpJavaApplication.class)\n .web(false)\n .run(args);\n }\n\n @Bean\n public IntegrationFlow sftpOutboundFlow() {\n return IntegrationFlows.from("toSftpChannel")\n .handle(Sftp.outboundAdapter(this.sftpSessionFactory, FileExistsMode.FAIL)\n .useTemporaryFileName(false)\n .remoteDirectory("/foo")\n ).get();\n }\n\n}\n')])])]),o("h3",{attrs:{id:"sftp-outbound-gateway"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#sftp-outbound-gateway"}},[e._v("#")]),e._v(" SFTP Outbound Gateway")]),e._v(" "),o("p",[e._v("The SFTP outbound gateway provides a limited set of commands that let you interact with a remote SFTP server:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("ls")]),e._v(" (list files)")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("nlst")]),e._v(" (list file names)")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("get")]),e._v(" (retrieve a file)")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("mget")]),e._v(" (retrieve multiple files)")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("rm")]),e._v(" (remove file(s))")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("mv")]),e._v(" (move and rename file)")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("put")]),e._v(" (send a file)")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("mput")]),e._v(" (send multiple files)")])])]),e._v(" "),o("h4",{attrs:{id:"using-the-ls-command"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#using-the-ls-command"}},[e._v("#")]),e._v(" Using the "),o("code",[e._v("ls")]),e._v(" Command")]),e._v(" "),o("p",[o("code",[e._v("ls")]),e._v(" lists remote files and supports the following options:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("-1")]),e._v(": Retrieve a list of filenames.\nThe default is to retrieve a list of "),o("code",[e._v("FileInfo")]),e._v(" objects")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-a")]),e._v(": Include all files (including those starting with '.')")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-f")]),e._v(": Do not sort the list")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-dirs")]),e._v(": Include directories (excluded by default)")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-links")]),e._v(": Include symbolic links (excluded by default)")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-R")]),e._v(": List the remote directory recursively")])])]),e._v(" "),o("p",[e._v("In addition, filename filtering is provided in the same manner as the "),o("code",[e._v("inbound-channel-adapter")]),e._v(".")]),e._v(" "),o("p",[e._v("The message payload resulting from an "),o("code",[e._v("ls")]),e._v(" operation is a list of file names or a list of "),o("code",[e._v("FileInfo")]),e._v(" objects (depending on whether you usr the "),o("code",[e._v("-1")]),e._v(" switch).\nThese objects provide information such as modified time, permissions, and others.")]),e._v(" "),o("p",[e._v("The remote directory that the "),o("code",[e._v("ls")]),e._v(" command acted on is provided in the "),o("code",[e._v("file_remoteDirectory")]),e._v(" header.")]),e._v(" "),o("p",[e._v("When using the recursive option ("),o("code",[e._v("-R")]),e._v("), the "),o("code",[e._v("fileName")]),e._v(" includes any subdirectory elements and represents the relative path to the file (relative to the remote directory).\nIf you use the "),o("code",[e._v("-dirs")]),e._v(" option, each recursive directory is also returned as an element in the list.\nIn this case, we recommend that you not use the "),o("code",[e._v("-1")]),e._v(" option, because you would not be able to distinguish files from directories, which you can do when you use "),o("code",[e._v("FileInfo")]),e._v(" objects.")]),e._v(" "),o("h4",{attrs:{id:"using-nlst-command"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#using-nlst-command"}},[e._v("#")]),e._v(" Using "),o("code",[e._v("nlst")]),e._v(" Command")]),e._v(" "),o("p",[e._v("Version 5 introduced support for the "),o("code",[e._v("nlst")]),e._v(" command.")]),e._v(" "),o("p",[o("code",[e._v("nlst")]),e._v(" lists remote file names and supports only one option:")]),e._v(" "),o("ul",[o("li",[o("code",[e._v("-f")]),e._v(": Do not sort the list")])]),e._v(" "),o("p",[e._v("The message payload resulting from an "),o("code",[e._v("nlst")]),e._v(" operation is a list of file names.")]),e._v(" "),o("p",[e._v("The "),o("code",[e._v("file_remoteDirectory")]),e._v(" header holds the remote directory on which the "),o("code",[e._v("nlst")]),e._v(" command acted.")]),e._v(" "),o("p",[e._v("The SFTP protocol does not provide the ability to list names.\nThis command is the equivalent of the "),o("code",[e._v("ls")]),e._v(" command with the "),o("code",[e._v("-1")]),e._v(" option and is added here for convenience.")]),e._v(" "),o("h4",{attrs:{id:"using-the-get-command"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#using-the-get-command"}},[e._v("#")]),e._v(" Using the "),o("code",[e._v("get")]),e._v(" Command")]),e._v(" "),o("p",[o("code",[e._v("get")]),e._v(" retrieves a remote file and supports the following options:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("-P")]),e._v(": Preserve the timestamp of the remote file.")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-stream")]),e._v(": Retrieve the remote file as a stream.")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-D")]),e._v(": Delete the remote file after successful transfer.\nThe remote file is not deleted if the transfer is ignored, because the "),o("code",[e._v("FileExistsMode")]),e._v(" is "),o("code",[e._v("IGNORE")]),e._v(" and the local file already exists.")])])]),e._v(" "),o("p",[e._v("The "),o("code",[e._v("file_remoteDirectory")]),e._v(" header holds the remote directory, and the "),o("code",[e._v("file_remoteFile")]),e._v(" header holds the filename.")]),e._v(" "),o("p",[e._v("The message payload resulting from a "),o("code",[e._v("get")]),e._v(" operation is a "),o("code",[e._v("File")]),e._v(" object representing the retrieved file.\nIf you use the "),o("code",[e._v("-stream")]),e._v(" option, the payload is an "),o("code",[e._v("InputStream")]),e._v(" rather than a "),o("code",[e._v("File")]),e._v(".\nFor text files, a common use case is to combine this operation with a "),o("RouterLink",{attrs:{to:"/en/spring-integration/file.html#file-splitter"}},[e._v("file splitter")]),e._v(" or a"),o("RouterLink",{attrs:{to:"/en/spring-integration/transformer.html#stream-transformer"}},[e._v("stream transformer")]),e._v(".\nWhen consuming remote files as streams, you are responsible for closing the "),o("code",[e._v("Session")]),e._v(" after the stream is consumed.\nFor convenience, the "),o("code",[e._v("Session")]),e._v(" is provided in the "),o("code",[e._v("closeableResource")]),e._v(" header, and "),o("code",[e._v("IntegrationMessageHeaderAccessor")]),e._v(" offers convenience method:")],1),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v("Closeable closeable = new IntegrationMessageHeaderAccessor(message).getCloseableResource();\nif (closeable != null) {\n closeable.close();\n}\n")])])]),o("p",[e._v("Framework components, such as the "),o("RouterLink",{attrs:{to:"/en/spring-integration/file.html#file-splitter"}},[e._v("File Splitter")]),e._v(" and "),o("RouterLink",{attrs:{to:"/en/spring-integration/transformer.html#stream-transformer"}},[e._v("Stream Transformer")]),e._v(",\nautomatically close the session after the data is transferred.")],1),e._v(" "),o("p",[e._v("The following example shows how to consume a file as a stream:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n\n\n')])])]),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("If you consume the input stream in a custom component, you must close the "),o("code",[e._v("Session")]),e._v("."),o("br"),e._v("You can either do that in your custom code or route a copy of the message to a "),o("code",[e._v("service-activator")]),e._v(" and use SpEL, as the following example shows:")])])]),e._v(" "),o("tbody")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n')])])]),o("h4",{attrs:{id:"using-the-mget-command"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#using-the-mget-command"}},[e._v("#")]),e._v(" Using the "),o("code",[e._v("mget")]),e._v(" Command")]),e._v(" "),o("p",[o("code",[e._v("mget")]),e._v(" retrieves multiple remote files based on a pattern and supports the following options:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("-P")]),e._v(": Preserve the timestamps of the remote files.")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-R")]),e._v(": Retrieve the entire directory tree recursively.")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-x")]),e._v(": Throw an exception if no files match the pattern (otherwise, an empty list is returned).")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("-D")]),e._v(": Delete each remote file after successful transfer.\nIf the transfer is ignored, the remote file is not deleted, because the "),o("code",[e._v("FileExistsMode")]),e._v(" is "),o("code",[e._v("IGNORE")]),e._v(" and the local file already exists.")])])]),e._v(" "),o("p",[e._v("The message payload resulting from an "),o("code",[e._v("mget")]),e._v(" operation is a "),o("code",[e._v("List")]),e._v(" object (that is, a "),o("code",[e._v("List")]),e._v(" of "),o("code",[e._v("File")]),e._v(" objects, each representing a retrieved file).")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("Starting with version 5.0, if the "),o("code",[e._v("FileExistsMode")]),e._v(" is "),o("code",[e._v("IGNORE")]),e._v(", the payload of the output message no longer contain files that were not fetched due to the file already existing."),o("br"),e._v("Previously, the array contained all files, including those that already existed.")])])]),e._v(" "),o("tbody")]),e._v(" "),o("p",[e._v("The expression you use determine the remote path should produce a result that ends with `` "),o("strong",[e._v("for example "),o("code",[e._v("myfiles/")])]),e._v(" fetches the complete tree under "),o("code",[e._v("myfiles")]),e._v(".")]),e._v(" "),o("p",[e._v("Starting with version 5.0, you can use a recursive "),o("code",[e._v("MGET")]),e._v(", combined with the "),o("code",[e._v("FileExistsMode.REPLACE_IF_MODIFIED")]),e._v(" mode, to periodically synchronize an entire remote directory tree locally.\nThis mode sets the local file’s last modified timestamp to the remote file’s timestamp, regardless of the "),o("code",[e._v("-P")]),e._v(" (preserve timestamp) option.")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("Notes for when using recursion ("),o("code",[e._v("-R")]),e._v(")"),o("br"),o("br"),e._v("The pattern is ignored and "),o("code",[e._v("*")]),e._v(" is assumed."),o("br"),e._v("By default, the entire remote tree is retrieved."),o("br"),e._v("However, you can filter files in the tree by providing a "),o("code",[e._v("FileListFilter")]),e._v("."),o("br"),e._v("You can also filter directories in the tree this way."),o("br"),e._v("A "),o("code",[e._v("FileListFilter")]),e._v(" can be provided by reference or by "),o("code",[e._v("filename-pattern")]),e._v(" or "),o("code",[e._v("filename-regex")]),e._v(" attributes."),o("br"),e._v("For example, "),o("code",[e._v('filename-regex="(subDir|.*1.txt)"')]),e._v(" retrieves all files ending with "),o("code",[e._v("1.txt")]),e._v(" in the remote directory and the subdirectory "),o("code",[e._v("subDir")]),e._v("."),o("br"),e._v("However, we describe an alternative available after this note."),o("br"),o("br"),e._v("If you filter a subdirectory, no additional traversal of that subdirectory is performed."),o("br"),o("br"),e._v("The "),o("code",[e._v("-dirs")]),e._v(" option is not allowed (the recursive "),o("code",[e._v("mget")]),e._v(" uses the recursive "),o("code",[e._v("ls")]),e._v(" to obtain the directory tree and the directories themselves cannot be included in the list)."),o("br"),o("br"),e._v("Typically, you would use the "),o("code",[e._v("#remoteDirectory")]),e._v(" variable in the "),o("code",[e._v("local-directory-expression")]),e._v(" so that the remote directory structure is retained locally.")])])]),e._v(" "),o("tbody")]),e._v(" "),o("p",[e._v("The persistent file list filters now have a boolean property "),o("code",[e._v("forRecursion")]),e._v(".\nSetting this property to "),o("code",[e._v("true")]),e._v(", also sets "),o("code",[e._v("alwaysAcceptDirectories")]),e._v(", which means that the recursive operation on the outbound gateways ("),o("code",[e._v("ls")]),e._v(" and "),o("code",[e._v("mget")]),e._v(") will now always traverse the full directory tree each time.\nThis is to solve a problem where changes deep in the directory tree were not detected.\nIn addition, "),o("code",[e._v("forRecursion=true")]),e._v(" causes the full path to files to be used as the metadata store keys; this solves a problem where the filter did not work properly if a file with the same name appears multiple times in different directories.\nIMPORTANT: This means that existing keys in a persistent metadata store will not be found for files beneath the top level directory.\nFor this reason, the property is "),o("code",[e._v("false")]),e._v(" by default; this may change in a future release.")]),e._v(" "),o("p",[e._v("Starting with version 5.0, you can configure the "),o("code",[e._v("SftpSimplePatternFileListFilter")]),e._v(" and "),o("code",[e._v("SftpRegexPatternFileListFilter")]),e._v(" to always pass directories by setting the "),o("code",[e._v("alwaysAcceptDirectorties")]),e._v(" to "),o("code",[e._v("true")]),e._v(".\nDoing so allows recursion for a simple pattern, as the following examples show:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n \n \n\n\n\n \n \n\n')])])]),o("p",[e._v("You can provide one of these filters by using the "),o("code",[e._v("filter")]),e._v(" property on the gateway.")]),e._v(" "),o("p",[e._v("See also "),o("a",{attrs:{href:"#sftp-partial"}},[e._v("Outbound Gateway Partial Success ("),o("code",[e._v("mget")]),e._v(" and "),o("code",[e._v("mput")]),e._v(")")]),e._v(".")]),e._v(" "),o("h4",{attrs:{id:"using-the-put-command"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#using-the-put-command"}},[e._v("#")]),e._v(" Using the "),o("code",[e._v("put")]),e._v(" Command")]),e._v(" "),o("p",[o("code",[e._v("put")]),e._v(" sends a file to the remote server.\nThe payload of the message can be a "),o("code",[e._v("java.io.File")]),e._v(", a "),o("code",[e._v("byte[]")]),e._v(", or a "),o("code",[e._v("String")]),e._v(".\nA "),o("code",[e._v("remote-filename-generator")]),e._v(" (or expression) is used to name the remote file.\nOther available attributes include "),o("code",[e._v("remote-directory")]),e._v(", "),o("code",[e._v("temporary-remote-directory")]),e._v(" and their "),o("code",[e._v("*-expression")]),e._v(" equivalents: "),o("code",[e._v("use-temporary-file-name")]),e._v(" and "),o("code",[e._v("auto-create-directory")]),e._v(".\nSee the "),o("a",{attrs:{href:"https://github.com/spring-projects/spring-integration/tree/main/spring-integration-core/src/main/resources/org/springframework/integration/config",target:"_blank",rel:"noopener noreferrer"}},[e._v("schema documentation"),o("OutboundLink")],1),e._v(" for more information.")]),e._v(" "),o("p",[e._v("The message payload resulting from a "),o("code",[e._v("put")]),e._v(" operation is a "),o("code",[e._v("String")]),e._v(" that contains the full path of the file on the server after transfer.")]),e._v(" "),o("p",[e._v("Version 4.3 introduced the "),o("code",[e._v("chmod")]),e._v(" attribute, which changes the remote file permissions after upload.\nYou can use the conventional Unix octal format (for example, "),o("code",[e._v("600")]),e._v(" allows read-write for the file owner only).\nWhen configuring the adapter using java, you can use "),o("code",[e._v("setChmod(0600)")]),e._v(".")]),e._v(" "),o("h4",{attrs:{id:"using-the-mput-command"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#using-the-mput-command"}},[e._v("#")]),e._v(" Using the "),o("code",[e._v("mput")]),e._v(" Command")]),e._v(" "),o("p",[o("code",[e._v("mput")]),e._v(" sends multiple files to the server and supports the following option:")]),e._v(" "),o("ul",[o("li",[o("code",[e._v("-R")]),e._v(": Recursive — send all files (possibly filtered) in the directory and subdirectories")])]),e._v(" "),o("p",[e._v("The message payload must be a "),o("code",[e._v("java.io.File")]),e._v(" (or "),o("code",[e._v("String")]),e._v(") that represents a local directory.\nSince version 5.1, a collection of "),o("code",[e._v("File")]),e._v(" or "),o("code",[e._v("String")]),e._v(" is also supported.")]),e._v(" "),o("p",[e._v("The same attributes as the "),o("a",{attrs:{href:"#sftp-put-command"}},[o("code",[e._v("put")]),e._v(" command")]),e._v(" are supported.\nIn addition, you can filter files in the local directory with one of "),o("code",[e._v("mput-pattern")]),e._v(", "),o("code",[e._v("mput-regex")]),e._v(", "),o("code",[e._v("mput-filter")]),e._v(", or "),o("code",[e._v("mput-filter-expression")]),e._v(".\nThe filter works with recursion, as long as the subdirectories themselves pass the filter.\nSubdirectories that do not pass the filter are not recursed.")]),e._v(" "),o("p",[e._v("The message payload resulting from an "),o("code",[e._v("mput")]),e._v(" operation is a "),o("code",[e._v("List")]),e._v(" object (that is, a "),o("code",[e._v("List")]),e._v(" of remote file paths resulting from the transfer).")]),e._v(" "),o("p",[e._v("See also "),o("a",{attrs:{href:"#sftp-partial"}},[e._v("Outbound Gateway Partial Success ("),o("code",[e._v("mget")]),e._v(" and "),o("code",[e._v("mput")]),e._v(")")]),e._v(".")]),e._v(" "),o("p",[e._v("Version 4.3 introduced the "),o("code",[e._v("chmod")]),e._v(" attribute, which lets you change the remote file permissions after upload.\nYou can use the conventional Unix octal format (for example, "),o("code",[e._v("600")]),e._v(" allows read-write for the file owner only).\nWhen configuring the adapter with Java, you can use "),o("code",[e._v('setChmodOctal("600")')]),e._v(" or "),o("code",[e._v("setChmod(0600)")]),e._v(".")]),e._v(" "),o("h4",{attrs:{id:"using-the-rm-command"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#using-the-rm-command"}},[e._v("#")]),e._v(" Using the "),o("code",[e._v("rm")]),e._v(" Command")]),e._v(" "),o("p",[e._v("The "),o("code",[e._v("rm")]),e._v(" command has no options.")]),e._v(" "),o("p",[e._v("If the remove operation was successful, the resulting message payload is "),o("code",[e._v("Boolean.TRUE")]),e._v(".\nOtherwise, the message payload is "),o("code",[e._v("Boolean.FALSE")]),e._v(".\nThe "),o("code",[e._v("file_remoteDirectory")]),e._v(" header holds the remote directory, and the "),o("code",[e._v("file_remoteFile")]),e._v(" header holds the file name.")]),e._v(" "),o("h4",{attrs:{id:"using-the-mv-command"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#using-the-mv-command"}},[e._v("#")]),e._v(" Using the "),o("code",[e._v("mv")]),e._v(" Command")]),e._v(" "),o("p",[e._v("The "),o("code",[e._v("mv")]),e._v(" command has no options.")]),e._v(" "),o("p",[e._v("The "),o("code",[e._v("expression")]),e._v(" attribute defines the “from” path, and the "),o("code",[e._v("rename-expression")]),e._v(" attribute defines the “to” path.\nBy default, the "),o("code",[e._v("rename-expression")]),e._v(" is "),o("code",[e._v("headers['file_renameTo']")]),e._v(".\nThis expression must not evaluate to null or an empty "),o("code",[e._v("String")]),e._v(".\nIf necessary, any remote directories needed are created.\nThe payload of the result message is "),o("code",[e._v("Boolean.TRUE")]),e._v(".\nThe "),o("code",[e._v("file_remoteDirectory")]),e._v(" header holds the original remote directory, and the "),o("code",[e._v("file_remoteFile")]),e._v(" header holds the filename.\nThe "),o("code",[e._v("file_renameTo")]),e._v(" header holds the new path.")]),e._v(" "),o("p",[e._v("Starting with version 5.5.6, the "),o("code",[e._v("remoteDirectoryExpression")]),e._v(" can be used in the "),o("code",[e._v("mv")]),e._v(" command for convenience.\nIf the “from” file is not a full file path, the result of "),o("code",[e._v("remoteDirectoryExpression")]),e._v(" is used as the remote directory.\nThe same applies for the “to” file, for example, if the task is just to rename a remote file in some directory.")]),e._v(" "),o("h4",{attrs:{id:"additional-command-information"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#additional-command-information"}},[e._v("#")]),e._v(" Additional Command Information")]),e._v(" "),o("p",[e._v("The "),o("code",[e._v("get")]),e._v(" and "),o("code",[e._v("mget")]),e._v(" commands support the "),o("code",[e._v("local-filename-generator-expression")]),e._v(" attribute.\nIt defines a SpEL expression to generate the names of local files during the transfer.\nThe root object of the evaluation context is the request message.\nThe "),o("code",[e._v("remoteFileName")]),e._v(" variable is also available.\nIt is particularly useful for "),o("code",[e._v("mget")]),e._v(" (for example: "),o("code",[e._v('local-filename-generator-expression="#remoteFileName.toUpperCase() + headers.foo"')]),e._v(").")]),e._v(" "),o("p",[e._v("The "),o("code",[e._v("get")]),e._v(" and "),o("code",[e._v("mget")]),e._v(" commands support the "),o("code",[e._v("local-directory-expression")]),e._v(" attribute.\nIt defines a SpEL expression to generate the names of local directories during the transfer.\nThe root object of the evaluation context is the request message.\nThe "),o("code",[e._v("remoteDirectory")]),e._v(" variable is also available.\nIt is particularly useful for mget (for example: "),o("code",[e._v("local-directory-expression=\"'/tmp/local/' + #remoteDirectory.toUpperCase() + headers.myheader\"")]),e._v(").\nThis attribute is mutually exclusive with the "),o("code",[e._v("local-directory")]),e._v(" attribute.")]),e._v(" "),o("p",[e._v("For all commands, the 'expression' property of the gateway holds the path on which the command acts.\nFor the "),o("code",[e._v("mget")]),e._v(" command, the expression might evaluate to ``"),o("strong",[e._v(", meaning to retrieve all files, "),o("code",[e._v("somedirectory/")])]),e._v(", and other values that end with "),o("code",[e._v("*")]),e._v(".")]),e._v(" "),o("p",[e._v("The following example shows a gateway configured for an "),o("code",[e._v("ls")]),e._v(" command:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('\n')])])]),o("p",[e._v("The payload of the message sent to the "),o("code",[e._v("toSplitter")]),e._v(" channel is a list of "),o("code",[e._v("String")]),e._v(" objects, each of which contains the name of a file.\nIf you omitted "),o("code",[e._v('command-options="-1"')]),e._v(", the payload would be a list of "),o("code",[e._v("FileInfo")]),e._v(" objects.\nYou can provide options as a space-delimited list (for example, "),o("code",[e._v('command-options="-1 -dirs -links"')]),e._v(").")]),e._v(" "),o("p",[e._v("Starting with version 4.2, the "),o("code",[e._v("GET")]),e._v(", "),o("code",[e._v("MGET")]),e._v(", "),o("code",[e._v("PUT")]),e._v(", and "),o("code",[e._v("MPUT")]),e._v(" commands support a "),o("code",[e._v("FileExistsMode")]),e._v(" property ("),o("code",[e._v("mode")]),e._v(" when using the namespace support).\nThis affects the behavior when the local file exists ("),o("code",[e._v("GET")]),e._v(" and "),o("code",[e._v("MGET")]),e._v(") or the remote file exists ("),o("code",[e._v("PUT")]),e._v(" and "),o("code",[e._v("MPUT")]),e._v(").\nThe supported modes are "),o("code",[e._v("REPLACE")]),e._v(", "),o("code",[e._v("APPEND")]),e._v(", "),o("code",[e._v("FAIL")]),e._v(", and "),o("code",[e._v("IGNORE")]),e._v(".\nFor backwards compatibility, the default mode for "),o("code",[e._v("PUT")]),e._v(" and "),o("code",[e._v("MPUT")]),e._v(" operations is "),o("code",[e._v("REPLACE")]),e._v(".\nFor "),o("code",[e._v("GET")]),e._v(" and "),o("code",[e._v("MGET")]),e._v(" operations, the default is "),o("code",[e._v("FAIL")]),e._v(".")]),e._v(" "),o("h4",{attrs:{id:"configuring-with-java-configuration-4"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#configuring-with-java-configuration-4"}},[e._v("#")]),e._v(" Configuring with Java Configuration")]),e._v(" "),o("p",[e._v("The following Spring Boot application shows an example of how to configure the outbound gateway with Java:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@SpringBootApplication\npublic class SftpJavaApplication {\n\n public static void main(String[] args) {\n new SpringApplicationBuilder(SftpJavaApplication.class)\n .web(false)\n .run(args);\n }\n\n @Bean\n @ServiceActivator(inputChannel = "sftpChannel")\n public MessageHandler handler() {\n return new SftpOutboundGateway(ftpSessionFactory(), "ls", "\'my_remote_dir/\'");\n }\n\n}\n')])])]),o("h4",{attrs:{id:"configuring-with-the-java-dsl-3"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#configuring-with-the-java-dsl-3"}},[e._v("#")]),e._v(" Configuring with the Java DSL")]),e._v(" "),o("p",[e._v("The following Spring Boot application shows an example of how to configure the outbound gateway with the Java DSL:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@SpringBootApplication\npublic class SftpJavaApplication {\n\n public static void main(String[] args) {\n new SpringApplicationBuilder(SftpJavaApplication.class)\n .web(false)\n .run(args);\n }\n\n @Bean\n public SessionFactory sftpSessionFactory() {\n DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();\n sf.setHost("localhost");\n sf.setPort(port);\n sf.setUsername("foo");\n sf.setPassword("foo");\n factory.setTestSession(true);\n return new CachingSessionFactory(sf);\n }\n\n @Bean\n public QueueChannelSpec remoteFileOutputChannel() {\n return MessageChannels.queue();\n }\n\n @Bean\n public IntegrationFlow sftpMGetFlow() {\n return IntegrationFlows.from("sftpMgetInputChannel")\n .handle(Sftp.outboundGateway(sftpSessionFactory(),\n AbstractRemoteFileOutboundGateway.Command.MGET, "payload")\n .options(AbstractRemoteFileOutboundGateway.Option.RECURSIVE)\n .regexFileNameFilter("(subSftpSource|.*1.txt)")\n .localDirectoryExpression("\'myDir/\' + #remoteDirectory")\n .localFilenameExpression("#remoteFileName.replaceFirst(\'sftpSource\', \'localTarget\')"))\n .channel("remoteFileOutputChannel")\n .get();\n }\n\n}\n')])])]),o("h4",{attrs:{id:""}},[o("a",{staticClass:"header-anchor",attrs:{href:"#"}},[e._v("#")])]),e._v(" "),o("p",[e._v("When performing operations on multiple files (by using "),o("code",[e._v("mget")]),e._v(" and "),o("code",[e._v("mput")]),e._v(") an exception can occur some time after one or more files have been transferred.\nIn this case (starting with version 4.2), a "),o("code",[e._v("PartialSuccessException")]),e._v(" is thrown.\nAs well as the usual "),o("code",[e._v("MessagingException")]),e._v(" properties ("),o("code",[e._v("failedMessage")]),e._v(" and "),o("code",[e._v("cause")]),e._v("), this exception has two additional properties:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("partialResults")]),e._v(": The successful transfer results.")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("derivedInput")]),e._v(": The list of files generated from the request message (such as local files to transfer for an "),o("code",[e._v("mput")]),e._v(").")])])]),e._v(" "),o("p",[e._v("These attributes let you determine which files were successfully transferred and which were not.")]),e._v(" "),o("p",[e._v("In the case of a recursive "),o("code",[e._v("mput")]),e._v(", the "),o("code",[e._v("PartialSuccessException")]),e._v(" may have nested "),o("code",[e._v("PartialSuccessException")]),e._v(" instances.")]),e._v(" "),o("p",[e._v("Consider the following directory structure:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v("root/\n|- file1.txt\n|- subdir/\n | - file2.txt\n | - file3.txt\n|- zoo.txt\n")])])]),o("p",[e._v("If the exception occurs on "),o("code",[e._v("file3.txt")]),e._v(", the "),o("code",[e._v("PartialSuccessException")]),e._v(" thrown by the gateway has "),o("code",[e._v("derivedInput")]),e._v(" of "),o("code",[e._v("file1.txt")]),e._v(", "),o("code",[e._v("subdir")]),e._v(", and "),o("code",[e._v("zoo.txt")]),e._v(" and "),o("code",[e._v("partialResults")]),e._v(" of "),o("code",[e._v("file1.txt")]),e._v(".\nIts "),o("code",[e._v("cause")]),e._v(" is another "),o("code",[e._v("PartialSuccessException")]),e._v(" with "),o("code",[e._v("derivedInput")]),e._v(" of "),o("code",[e._v("file2.txt")]),e._v(" and "),o("code",[e._v("file3.txt")]),e._v(" and "),o("code",[e._v("partialResults")]),e._v(" of "),o("code",[e._v("file2.txt")]),e._v(".")]),e._v(" "),o("h3",{attrs:{id:"sftp-jsch-logging"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#sftp-jsch-logging"}},[e._v("#")]),e._v(" SFTP/JSCH Logging")]),e._v(" "),o("p",[e._v("Since we use JSch libraries to provide SFTP support, you may at times require more information from the JSch API itself, especially if something is not working properly (such as authentication exceptions).\nUnfortunately JSch does not use "),o("code",[e._v("commons-logging")]),e._v(" but instead relies on custom implementations of their "),o("code",[e._v("com.jcraft.jsch.Logger")]),e._v(" interface.\nAs of Spring Integration 2.0.1, we have implemented this interface.\nSo now, to enable JSch logging, you can configure your logger the way you usually do.\nFor example, the following example is valid configuration of a logger that uses Log4J:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v("log4j.category.com.jcraft.jsch=DEBUG\n")])])]),o("h3",{attrs:{id:"messagesessioncallback"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#messagesessioncallback"}},[e._v("#")]),e._v(" MessageSessionCallback")]),e._v(" "),o("p",[e._v("Starting with Spring Integration version 4.2, you can use a "),o("code",[e._v("MessageSessionCallback")]),e._v(" implementation with the "),o("code",[e._v("")]),e._v(" ("),o("code",[e._v("SftpOutboundGateway")]),e._v(") to perform any operation on the "),o("code",[e._v("Session")]),e._v(" with the "),o("code",[e._v("requestMessage")]),e._v(" context.\nYou can use it for any non-standard or low-level SFTP operation (or several), such as allowing access from an integration flow definition, or functional interface (lambda) implementation injection.\nThe following example uses a lambda:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v('@Bean\n@ServiceActivator(inputChannel = "sftpChannel")\npublic MessageHandler sftpOutboundGateway(SessionFactory sessionFactory) {\n return new SftpOutboundGateway(sessionFactory,\n (session, requestMessage) -> session.list(requestMessage.getPayload()));\n}\n')])])]),o("p",[e._v("Another example might be to pre- or post-process the file data being sent or retrieved.")]),e._v(" "),o("p",[e._v("When using XML configuration, the "),o("code",[e._v("")]),e._v(" provides a "),o("code",[e._v("session-callback")]),e._v(" attribute that lets you specify the "),o("code",[e._v("MessageSessionCallback")]),e._v(" bean name.")]),e._v(" "),o("table",[o("thead",[o("tr",[o("th"),e._v(" "),o("th",[e._v("The "),o("code",[e._v("session-callback")]),e._v(" is mutually exclusive with the "),o("code",[e._v("command")]),e._v(" and "),o("code",[e._v("expression")]),e._v(" attributes."),o("br"),e._v("When configuring with Java, the "),o("code",[e._v("SftpOutboundGateway")]),e._v(" class offers different constructors.")])])]),e._v(" "),o("tbody")]),e._v(" "),o("h3",{attrs:{id:"apache-mina-sftp-server-events"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#apache-mina-sftp-server-events"}},[e._v("#")]),e._v(" Apache Mina SFTP Server Events")]),e._v(" "),o("p",[e._v("The "),o("code",[e._v("ApacheMinaSftpEventListener")]),e._v(", added in version 5.2, listens for certain Apache Mina SFTP server events and publishes them as "),o("code",[e._v("ApplicationEvent")]),e._v(" s which can be received by any "),o("code",[e._v("ApplicationListener")]),e._v(" bean, "),o("code",[e._v("@EventListener")]),e._v(" bean method, or "),o("RouterLink",{attrs:{to:"/en/spring-integration/event.html#appevent-inbound"}},[e._v("Event Inbound Channel Adapter")]),e._v(".")],1),e._v(" "),o("p",[e._v("Currently supported events are:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("SessionOpenedEvent")]),e._v(" - a client session was opened")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("DirectoryCreatedEvent")]),e._v(" - a directory was created")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("FileWrittenEvent")]),e._v(" - a file was written to")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("PathMovedEvent")]),e._v(" - a file or directory was renamed")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("PathRemovedEvent")]),e._v(" - a file or directory was removed")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("SessionClosedEvent")]),e._v(" - the client has disconnected")])])]),e._v(" "),o("p",[e._v("Each of these is a subclass of "),o("code",[e._v("ApacheMinaSftpEvent")]),e._v("; you can configure a single listener to receive all of the event types.\nThe "),o("code",[e._v("source")]),e._v(" property of each event is a "),o("code",[e._v("ServerSession")]),e._v(", from which you can obtain information such as the client address; a convenient "),o("code",[e._v("getSession()")]),e._v(" method is provided on the abstract event.")]),e._v(" "),o("p",[e._v("To configure the server with the listener (which must be a Spring bean), simply add it to the "),o("code",[e._v("SftpSubsystemFactory")]),e._v(":")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v("server = SshServer.setUpDefaultServer();\n...\nSftpSubsystemFactory sftpFactory = new SftpSubsystemFactory();\nsftpFactory.addSftpEventListener(apacheMinaSftpEventListenerBean);\n...\n")])])]),o("p",[e._v("To consume these events using a Spring Integration event adapter:")]),e._v(" "),o("div",{staticClass:"language- extra-class"},[o("pre",{pre:!0,attrs:{class:"language-text"}},[o("code",[e._v("@Bean\npublic ApplicationEventListeningMessageProducer eventsAdapter() {\n ApplicationEventListeningMessageProducer producer =\n new ApplicationEventListeningMessageProducer();\n producer.setEventTypes(ApacheMinaSftpEvent.class);\n producer.setOutputChannel(eventChannel());\n return producer;\n}\n")])])]),o("h3",{attrs:{id:"remote-file-information"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#remote-file-information"}},[e._v("#")]),e._v(" Remote File Information")]),e._v(" "),o("p",[e._v("Starting with version 5.2, the "),o("code",[e._v("SftpStreamingMessageSource")]),e._v(" ("),o("a",{attrs:{href:"#sftp-streaming"}},[e._v("SFTP Streaming Inbound Channel Adapter")]),e._v("), "),o("code",[e._v("SftpInboundFileSynchronizingMessageSource")]),e._v(" ("),o("a",{attrs:{href:"#sftp-inbound"}},[e._v("SFTP Inbound Channel Adapter")]),e._v(') and "read"-commands of the '),o("code",[e._v("SftpOutboundGateway")]),e._v(" ("),o("a",{attrs:{href:"#sftp-outbound-gateway"}},[e._v("SFTP Outbound Gateway")]),e._v(") provide additional headers in the message to produce with an information about the remote file:")]),e._v(" "),o("ul",[o("li",[o("p",[o("code",[e._v("FileHeaders.REMOTE_HOST_PORT")]),e._v(" - the host:port pair the remote session has been connected to during file transfer operation;")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("FileHeaders.REMOTE_DIRECTORY")]),e._v(" - the remote directory the operation has been performed;")])]),e._v(" "),o("li",[o("p",[o("code",[e._v("FileHeaders.REMOTE_FILE")]),e._v(" - the remote file name; applicable only for single file operations.")])])]),e._v(" "),o("p",[e._v("Since the "),o("code",[e._v("SftpInboundFileSynchronizingMessageSource")]),e._v(" doesn’t produce messages against remote files, but using a local copy, the "),o("code",[e._v("AbstractInboundFileSynchronizer")]),e._v(" stores an information about remote file in the "),o("code",[e._v("MetadataStore")]),e._v(" (which can be configured externally) in the URI style ("),o("code",[e._v("protocol://host:port/remoteDirectory#remoteFileName")]),e._v(") during synchronization operation.\nThis metadata is retrieved by the "),o("code",[e._v("SftpInboundFileSynchronizingMessageSource")]),e._v(" when local file is polled.\nWhen local file is deleted, it is recommended to remove its metadata entry.\nThe "),o("code",[e._v("AbstractInboundFileSynchronizer")]),e._v(" provides a "),o("code",[e._v("removeRemoteFileMetadata()")]),e._v(" callback for this purpose.\nIn addition there is a "),o("code",[e._v("setMetadataStorePrefix()")]),e._v(" to be used in the metadata keys.\nIt is recommended to have this prefix be different from the one used in the "),o("code",[e._v("MetadataStore")]),e._v("-based "),o("code",[e._v("FileListFilter")]),e._v(" implementations, when the same "),o("code",[e._v("MetadataStore")]),e._v(" instance is shared between these components, to avoid entry overriding because both filter and "),o("code",[e._v("AbstractInboundFileSynchronizer")]),e._v(" use the same local file name for the metadata entry key.")])])}),[],!1,null,null,null);t.default=a.exports}}]);