All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.rx.net.SftpClient Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
package org.rx.net;

import com.jcraft.jsch.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.rx.core.*;
import org.rx.core.Arrays;
import org.rx.core.exception.InvalidException;
import org.rx.io.CurdFile;
import org.rx.io.Files;
import org.rx.io.IOStream;

import java.io.InputStream;
import java.util.*;

import static org.rx.core.App.quietly;
import static org.rx.core.App.require;

/**
 * "cd remotePath                       Change remote directory to 'remotePath'\n"+
 * "lcd remotePath                      Change local directory to 'remotePath'\n"+
 * "chgrp grp remotePath                Change group of file 'remotePath' to 'grp'\n"+
 * "chmod mode remotePath               Change permissions of file 'remotePath' to 'mode'\n"+
 * "chown own remotePath                Change owner of file 'remotePath' to 'own'\n"+
 * "df [remotePath]                     Display statistics for current directory or\n"+
 * "                              filesystem containing 'remotePath'\n"+
 * "help                          Display this help text\n"+
 * "get remote-remotePath [local-remotePath]  Download file\n"+
 * "get-resume remote-remotePath [local-remotePath]  Resume to download file.\n"+
 * "get-append remote-remotePath [local-remotePath]  Append remote file to local file\n"+
 * "hardlink oldpath newpath      Hardlink remote file\n"+
 * "*lls [ls-options [remotePath]]      Display local directory listing\n"+
 * "ln oldpath newpath            Symlink remote file\n"+
 * "*lmkdir remotePath                  Create local directory\n"+
 * "lpwd                          Print local working directory\n"+
 * "ls [remotePath]                     Display remote directory listing\n"+
 * "*lumask umask                 Set local umask to 'umask'\n"+
 * "mkdir remotePath                    Create remote directory\n"+
 * "put local-remotePath [remote-remotePath]  Upload file\n"+
 * "put-resume local-remotePath [remote-remotePath]  Resume to upload file\n"+
 * "put-append local-remotePath [remote-remotePath]  Append local file to remote file.\n"+
 * "pwd                           Display remote working directory\n"+
 * "stat remotePath                     Display info about remotePath\n"+
 * "exit                          Quit sftp\n"+
 * "quit                          Quit sftp\n"+
 * "rename oldpath newpath        Rename remote file\n"+
 * "rmdir remotePath                    Remove remote directory\n"+
 * "rm remotePath                       Delete remote file\n"+
 * "symlink oldpath newpath       Symlink remote file\n"+
 * "readlink remotePath                 Check the target of a symbolic link\n"+
 * "realpath remotePath                 Canonicalize the remotePath\n"+
 * "rekey                         Key re-exchanging\n"+
 * "compression level             Packet compression will be enabled\n"+
 * "version                       Show SFTP version\n"+
 * "?                             Synonym for help";
 */
@Slf4j
public class SftpClient extends Disposable implements CurdFile {
    private static final List skipDirectories = Arrays.toList(".", "..");

    private final JSch jsch = new JSch();
    private final Session session;
    private final ChannelSftp channel;

    public SftpClient(AuthenticEndpoint endpoint) {
        this(endpoint, App.getConfig().getNetTimeoutMillis());
    }

    @SneakyThrows
    public SftpClient(AuthenticEndpoint endpoint, int timeoutMillis) {
        require(endpoint);

        session = jsch.getSession(endpoint.getUsername(), endpoint.getEndpoint().getHostString(), endpoint.getEndpoint().getPort());
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.setPassword(endpoint.getPassword());
        session.connect(timeoutMillis);
        channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect(timeoutMillis);
    }

    @Override
    protected void freeObjects() {
        channel.disconnect();
        session.disconnect();
    }

    @SneakyThrows
    @Override
    public void createDirectory(String remotePath) {
        String dirPath = FilenameUtils.getFullPath(remotePath);
        if (exists(dirPath)) {
            return;
        }
        channel.mkdir(dirPath);
    }

    @Override
    public void saveFile(String remotePath, InputStream in) {
        uploadFile(IOStream.wrap(FilenameUtils.getName(remotePath), in), remotePath);
    }

    @SneakyThrows
    public void delete(String remotePath) {
        if (isDirectory(remotePath)) {
            for (SftpFile file : listFiles(remotePath, true)) {
                channel.rm(file.getPath());
            }
            channel.rmdir(remotePath);
            return;
        }
        channel.rm(remotePath);
    }

    @Override
    public boolean isDirectory(String remotePath) {
        if (Strings.isEmpty(remotePath)) {
            throw new IllegalArgumentException("remotePath");
        }

        char ch = remotePath.charAt(remotePath.length() - 1);
        return ch == '/' || Strings.isEmpty(FilenameUtils.getExtension(remotePath));
    }

    @Override
    public boolean exists(String path) {
        try {
            channel.stat(path);
            return true;
        } catch (SftpException e) {
            log.warn("exists", e);
        }
        return false;
    }

    @Override
    public NQuery listDirectories(String remotePath, boolean recursive) {
        List root = new ArrayList<>();
        listDirectories(root, recursive, remotePath);
        return NQuery.of(root);
    }

    @SneakyThrows
    private void listDirectories(List result, boolean recursive, String directory) {
        List list = channel.ls(directory);
        for (ChannelSftp.LsEntry entry : list) {
            if (skipDirectories.contains(entry.getFilename()) || !entry.getAttrs().isDir()) {
                continue;
            }
            result.add(new SftpFile(Files.concatPath(directory, entry.getFilename()), entry.getFilename(), entry.getLongname()));
            if (recursive) {
                listDirectories(result, recursive, Files.concatPath(directory, entry.getFilename()));
            }
        }
    }

    @Override
    public NQuery listFiles(String remotePath, boolean recursive) {
        List root = new ArrayList<>();
        listFiles(root, recursive, remotePath);
        return NQuery.of(root);
    }

    @SneakyThrows
    private void listFiles(List result, boolean recursive, String directory) {
        List list = channel.ls(directory);
        for (ChannelSftp.LsEntry entry : list) {
            if (skipDirectories.contains(entry.getFilename())) {
                continue;
            }
            if (entry.getAttrs().isDir()) {
                if (recursive) {
                    quietly(() -> listFiles(result, recursive, Files.concatPath(directory, entry.getFilename())));
                }
                continue;
            }
            result.add(new SftpFile(Files.concatPath(directory, entry.getFilename()), entry.getFilename(), entry.getLongname()));
        }
    }

    public void uploadFile(String localPath, String remotePath) {
        uploadFile(IOStream.wrap(localPath), remotePath);
    }

    @SneakyThrows
    public void uploadFile(IOStream stream, String remotePath) {
        require(stream, remotePath);

        if (Files.isDirectory(remotePath)) {
            if (Strings.isEmpty(stream.getName())) {
                throw new InvalidException("stream name is empty");
            }
            remotePath = padDirectoryPath(remotePath) + stream.getName();
        }

        createDirectory(remotePath);
        channel.put(stream.getReader(), remotePath);
    }

    public void downloadFile(String remotePath, String localPath) {
        Files.createDirectory(localPath);

        downloadFile(remotePath, IOStream.wrap(localPath));
    }

    @SneakyThrows
    public void downloadFile(String remotePath, IOStream stream) {
        require(remotePath, stream);

        channel.get(remotePath, stream.getWriter());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy