简体   繁体   English

如何使用Java中的Apache Mina SSHD SFTP服务器处理传入文件

[英]How To Handle Incoming Files In Apache Mina SSHD SFTP Server in Java

Currently i am working on a SFTP protocol.I have created SFTP client Using Jsch Library and SFTP Server using Apache Mina Sshd library.I have made connection between them and can successfully send files to SFTP server.Now i am working on creating a SFTP server side file handler that handles the incoming files.As a example let say SFTP server can receive files from SFTP client but currently in my implementation there is no way to notify when file is arrived into server.I just go server root folder and see if there is a files available.That is how i know if files are arrived. 目前我正在研究SFTP协议。我使用Apache Mina Sshd库创建了SFTP客户端使用Jsch库和SFTP服务器。我已经建立了它们之间的连接,并且可以成功地将文件发送到SFTP服务器。现在我正在创建一个SFTP服务器处理传入文件的辅助文件处理程序。举个例子,假设SFTP服务器可以从SFTP客户端接收文件,但是当前在我的实现中,当文件到达服务器时没有办法通知。我只是去服务器根文件夹,看看是否有是一个可用的文件。这就是我知道文件是否到达的方式。

I would like to implement that when files arrive into server it will notify user to files are arrived and files content.(file Name and other details).But the problem is that i am new to Apache Mina sshd API.i have gone through documentation but i couldn't figured it out. 我想实现当文件到达服务器时它将通知用户文件到达和文件内容。(文件名和其他详细信息)。但问题是我是Apache Mina的新手sshd API.i已经通过文档但我无法弄明白。

Please I would like know that if there are any already implemented listeners for handle incoming files in Apache Mina Sshd server or if not how can i implement my own listener for incoming files. 请问我是否知道如果在Apache Mina Sshd服务器中有任何已经实现的侦听器来处理传入文件,或者如果没有,我如何为传入文件实现我自己的侦听器。

SFTP Server Code SFTP服务器代码

public class SftpServerStarter {

    private SshServer sshd;
    private final static Logger logger = LoggerFactory.getLogger(SftpServerStarter.class);

    public void start(){


        sshd = SshServer.setUpDefaultServer();
        sshd.setPort(22);
        sshd.setHost("localhost");

        sshd.setPasswordAuthenticator(new MyPasswordAuthenticator());
        sshd.setPublickeyAuthenticator(new MyPublickeyAuthenticator());
        sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
        sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
        sshd.setCommandFactory(new ScpCommandFactory());
        sshd.setFileSystemFactory(new VirtualFileSystemFactory("C:/root"));


        try {
            logger.info("Starting ...");
            sshd.start();
            logger.info("Started");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            logger.info("Can not Start Server");
        }
    }

}

I started out using @gihan's suggestion, but I ran into some problems with the File Monitors processing the file before the user was finished uploading it on some clients. 我开始使用@gihan的建议,但是在用户完成在某些客户端上传之前,我遇到了文件监视器处理文件的一些问题。

Here is the solution I found from poking around in Mina's source code. 这是我在Mina的源代码中找到的解决方案。 In spite of the sparse, useless documentation on Apache Mina's site, I think this is the way they intended for developers to use their library. 尽管有关Apache Mina网站上的稀疏,无用的文档,但我认为这是他们打算让开发人员使用他们的库的方式。

NOTE: Since your needs are probably different than mine, keep in mind that this may not be a copy-and-paste solution. 注意: 由于您的需求可能与我的不同,请记住,这可能不是复制粘贴解决方案。 You will probably need to adapt this code to suit your needs , but I'm fairly confident that this code does provide the key to the solution you're looking for. 您可能需要调整此代码以满足您的需求 ,但我相信这段代码确实为您正在寻找的解决方案提供了关键。

Step 1: Implement SftpEventListener 第1步:实现SftpEventListener

Create your own class that implements org.apache.sshd.server.subsystem.sftp.SftpEventListener . 创建自己的类,实现org.apache.sshd.server.subsystem.sftp.SftpEventListener Here's mine as an example . 这是我的例子 My implementation is set up to run a series of registered FileUploadCompleteListener methods whenever a file is newly uploaded or overwritten, and block user attempts to navigate or create directories. 我的实现设置为在新上载或覆盖文件时运行一系列已注册的FileUploadCompleteListener方法,并阻止用户尝试导航或创建目录。

public class SFTPServiceSFTPEventListener implements SftpEventListener {

    Logger logger = Logger.getLogger(SFTPServiceSFTPEventListener.class);

    SFTPService service;

    public SFTPServiceSFTPEventListener(SFTPService service) {
        this.service = service;
    }

    public interface FileUploadCompleteListener {
        void onFileReady(File file);
    }

    private List<FileUploadCompleteListener> fileReadyListeners = new ArrayList<FileUploadCompleteListener>();

    public void addFileUploadCompleteListener(FileUploadCompleteListener listener) {
        fileReadyListeners.add(listener);
    }

    public void removeFileUploadCompleteListener(FileUploadCompleteListener listener) {
        fileReadyListeners.remove(listener);
    }

    @Override
    public void initialized(ServerSession serverSession, int version) {

    }

    @Override
    public void destroying(ServerSession serverSession) {

    }

    @Override
    public void open(ServerSession serverSession, String remoteHandle, Handle localHandle) {
        File openedFile = localHandle.getFile().toFile();
        if (openedFile.exists() && openedFile.isFile()) {
        }
    }

    @Override
    public void read(ServerSession serverSession, String remoteHandle, DirectoryHandle localHandle, Map<String,Path> entries) {

    }

    @Override
    public void read(ServerSession serverSession, String remoteHandle, FileHandle localHandle, long offset, byte[] data, int dataOffset, int dataLen, int readLen) {

    }

    @Override
    public void write(ServerSession serverSession, String remoteHandle, FileHandle localHandle, long offset, byte[] data, int dataOffset, int dataLen) {
    }

    @Override
    public void blocking(ServerSession serverSession,  String remoteHandle, FileHandle localHandle, long offset, long length, int mask) {
    }

    @Override
    public void blocked(ServerSession serverSession, String remoteHandle, FileHandle localHandle, long offset, long length, int mask, Throwable thrown) {
    }

    @Override
    public void unblocking(ServerSession serverSession, String remoteHandle, FileHandle localHandle, long offset, long length) {
    }

    @Override
    public void unblocked(ServerSession serverSession, String remoteHandle, FileHandle localHandle, long offset, long length, Boolean result, Throwable thrown) {
    }

    @Override
    public void close(ServerSession serverSession, String remoteHandle, Handle localHandle) {
        File closedFile = localHandle.getFile().toFile();
        if (closedFile.exists() && closedFile.isFile()) {
            logger.info(String.format("User %s closed file: \"%s\"", serverSession.getUsername(), localHandle.getFile().toAbsolutePath()));
            this.service.UserWroteFile(serverSession.getUsername(), localHandle.getFile());

            for (FileUploadCompleteListener fileReadyListener : fileReadyListeners) {
                fileReadyListener.onFileReady(closedFile);
            }
        }
    }

    @Override
    public void creating(ServerSession serverSession, Path path, Map<String,?> attrs) throws UnsupportedOperationException {
        logger.warn(String.format("Blocked user %s attempt to create a directory \"%s\"", serverSession.getUsername(), path.toString()));
        throw new UnsupportedOperationException("Creating sub-directories is not permitted.");
    }

    @Override
    public void created(ServerSession serverSession, Path path, Map<String,?> attrs, Throwable thrown) {
        String username = serverSession.getUsername();
        logger.info(String.format("User %s created: \"%s\"", username, path.toString()));
        service.UserWroteFile(username, path);
    }

    @Override
    public void moving(ServerSession serverSession, Path path, Path path1, Collection<CopyOption> collection) {

    }

    @Override
    public void moved(ServerSession serverSession, Path source, Path destination, Collection<CopyOption> collection, Throwable throwable) {
        String username = serverSession.getUsername();
        logger.info(String.format("User %s moved: \"%s\" to \"%s\"", username, source.toString(), destination.toString()));
        service.UserWroteFile(username, destination);
    }

    @Override
    public void removing(ServerSession serverSession, Path path) {

    }

    @Override
    public void removed(ServerSession serverSession, Path path, Throwable thrown) {

    }

    @Override
    public void linking(ServerSession serverSession, Path source, Path target, boolean symLink) throws UnsupportedOperationException {
        logger.warn(String.format("Blocked user %s attempt to create a link to \"%s\" at \"%s\"", serverSession.getUsername(), target.toString(), source.toString()));
        throw new UnsupportedOperationException("Creating links is not permitted");
    }

    @Override
    public void linked(ServerSession serverSession, Path source, Path target, boolean symLink, Throwable thrown) {

    }

    @Override
    public void modifyingAttributes(ServerSession serverSession, Path path, Map<String,?> attrs) {

    }

    @Override
    public void modifiedAttributes(ServerSession serverSession, Path path, Map<String,?> attrs, Throwable thrown) {
        String username = serverSession.getUsername();
        service.UserWroteFile(username, path);
    }
}

Step 2: Add an instance of your listener to your server 第2步:将侦听器的实例添加到服务器

Once you've implemented your class, all you need to do is instantiate it and add it to your server using an SftpSubsystemFactory before calling start() on your server: 一旦实现了类,您需要做的就是实例化它并使用SftpSubsystemFactory将它添加到服务器, SftpSubsystemFactory再在服务器上调用start()

// Your SSHD Server
SshServer sshd = SshServer.setUpDefaultServer();

SftpSubsystemFactory sftpSubsystemFactory= new SftpSubsystemFactory();

// This is where to put your implementation of SftpEventListener
SFTPServiceSFTPEventListener sftpEventListener = new SFTPServiceSFTPEventListener(this);
sftpEventListener.addFileUploadCompleteListener(new SFTPServiceSFTPEventListener.FileUploadCompleteListener() {
    @Override
    public void onFileReady(File file) {
        try {
            doThingsWithFile(file);
        } catch (Exception e) {
            logger.warn(String.format("An error occurred while attempting to do things with the file: \"%s\"", file.getName()), e);
        }
    }
});
sftpSubsystemFactory.addSftpEventListener(sftpEventListener);

List<NamedFactory<Command>> namedFactoryList = new ArrayList<NamedFactory<Command>>();
namedFactoryList.add(sftpSubsystemFactory);
sshd.setSubsystemFactories(namedFactoryList);

// Do your other init stuff...

sshd.start();

Once you've done that, your implementation of SftpEventListener will start automatically responding to the events you've implemented. 完成后, SftpEventListener的实现将自动响应您已实现的事件。 Mine basically just responds to when the user closes the file (which occurs when the file upload is complete), but as I said, you can feel free to implement the other methods to respond to other events . 我基本上只响应用户关闭文件的时间(文件上传完成时发生),但正如我所说,你可以随意实现其他方法来响应其他事件

I finally found a solution but it is not coming from Apache Mina SSHD API. 我终于找到了一个解决方案,但它不是来自Apache Mina SSHD API。 Here is the concept: We can monitor the server's root directory for file changes. 这是一个概念:我们可以监视服务器的根目录以进行文件更改。 If there is an file changed in server folder, it will trigger an event. 如果在服务器文件夹中更改了文件,则会触发事件。 There are plenty of API's available to do this. 有很多API可以做到这一点。 In my code snippet, I'm using org.apache.commons.io.monitor . 在我的代码片段中,我正在使用org.apache.commons.io.monitor

SFTPFileListner Class SFTPFileListner类

public static void startMonitor(String rootFolder) throws Exception {

        //every 5 seconds it will start monitoring
        final long pollingInterval = 5 * 1000;

        File folder = new File(rootFolder);

        if (!folder.exists()) {

            throw new RuntimeException("ERROR : Server root directory not found: " + rootFolder);
        }

        FileAlterationObserver observer = new FileAlterationObserver(folder);
        FileAlterationMonitor monitor = new FileAlterationMonitor(pollingInterval);
        FileAlterationListener listener = new FileAlterationListenerAdaptor() {

            @Override
            public void onFileCreate(File file) {
                try {

                    System.out.println("[SFTPFileListner] Received :"+ file.getName());
                    System.out.println("[SFTPFileListner] Received File Path :"+ file.getCanonicalPath());



                } catch (IOException e) {
                    throw new RuntimeException("ERROR: Unrecoverable error when creating files " + e.getMessage(),e);
                }
            }

        };

        observer.addListener(listener);
        monitor.addObserver(observer);
        monitor.start();
    }

After creating the monitor class, you can call implemented method in your SFTP server class. 创建监视器类后,可以在SFTP服务器类中调用实现的方法。

SFTP server Class SFTP服务器类

//pass server root directory 
SFTPFileListner.startMonitor("C:/root");

As I mentioned in my other post, Mina didn't directly give us the capability to handle triggers on receipt or partial receipt of an incoming file but our needs were very specific. 正如我在其他帖子中提到的,Mina没有直接给我们处理接收或部分接收传入文件的触发器的能力,但我们的需求非常具体。 So our only option was to step outside of Mina, which your solution above is doing. 所以我们唯一的选择就是走出Mina,你的解决方案就是这样做的。 It might be worth pushing that as a pull request / feature for Mina or developing it further as an open source complimenting solution. 作为Mina的拉取请求/功能或者作为开源补充解决方案进一步发展它可能是值得的。 I think its a common problem that people would face to have an active notification / trigger system when something lands in their server. 我认为,当某些东西落入他们的服务器时,人们将面临一个常见问题,即拥有一个主动通知/触发系统。 Good luck with the rest of your development! 祝你好好继续开发!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM