Como recuperar um arquivo de um servidor via SFTP?

228

Estou tentando recuperar um arquivo de um servidor usando SFTP (em oposição ao FTPS) usando Java. Como posso fazer isso?

David Hayes
fonte

Respostas:

198

Outra opção é considerar a consulta à biblioteca JSch . JSch parece ser a biblioteca preferida para alguns grandes projetos de código aberto, incluindo Eclipse, Ant e Apache Commons HttpClient, entre outros.

Ele suporta logins baseados em certificado / usuário e certificado, bem como todo um host inteiro de outros recursos SSH2 saborosos.

Aqui está um simples arquivo remoto recuperado por SFTP. O tratamento de erros é deixado como um exercício para o leitor :-)

JSch jsch = new JSch();

String knownHostsFilename = "/home/username/.ssh/known_hosts";
jsch.setKnownHosts( knownHostsFilename );

Session session = jsch.getSession( "remote-username", "remote-host" );    
{
  // "interactive" version
  // can selectively update specified known_hosts file 
  // need to implement UserInfo interface
  // MyUserInfo is a swing implementation provided in 
  //  examples/Sftp.java in the JSch dist
  UserInfo ui = new MyUserInfo();
  session.setUserInfo(ui);

  // OR non-interactive version. Relies in host key being in known-hosts file
  session.setPassword( "remote-password" );
}

session.connect();

Channel channel = session.openChannel( "sftp" );
channel.connect();

ChannelSftp sftpChannel = (ChannelSftp) channel;

sftpChannel.get("remote-file", "local-file" );
// OR
InputStream in = sftpChannel.get( "remote-file" );
  // process inputstream as needed

sftpChannel.exit();
session.disconnect();
Cheekysoft
fonte
1
Cheekysoft, notei - ao usar o Jsch - a remoção de arquivos no servidor sftp não funciona. Também renomear arquivos também não funciona. Alguma idéia por favor ??? Andy
1
Desculpe, não é algo com o qual trabalho no momento. (Por favor, tente e deixe esse tipo de resposta como comentários - como esta mensagem - e não como uma nova resposta para a pergunta original)
Cheekysoft
1
O que é esse bloco de código após a atribuição da sessão? É alguma sintaxe sofisticada de Java que eu nunca vi? Se sim - o que é que é ser escrito dessa maneira?
Michael Peterson
3
@ p1x3l5 A sintaxe java padrão permite que um bloco seja inserido em qualquer lugar; pode ser usado para fornecer um controle mais refinado sobre o escopo variável, se você desejar. No entanto, nesse caso, é apenas uma ajuda visual para ajudar a indicar as duas opções de implementação: use a versão interativa que solicita uma senha do usuário ou use uma senha codificada que não requer intervenção do usuário, mas sem dúvida um risco adicional à segurança.
Cheekysoft
109

Aqui está o código fonte completo de um exemplo usando JSch sem ter que se preocupar com a verificação da chave ssh.

import com.jcraft.jsch.*;

public class TestJSch {
    public static void main(String args[]) {
        JSch jsch = new JSch();
        Session session = null;
        try {
            session = jsch.getSession("username", "127.0.0.1", 22);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setPassword("password");
            session.connect();

            Channel channel = session.openChannel("sftp");
            channel.connect();
            ChannelSftp sftpChannel = (ChannelSftp) channel;
            sftpChannel.get("remotefile.txt", "localfile.txt");
            sftpChannel.exit();
            session.disconnect();
        } catch (JSchException e) {
            e.printStackTrace();  
        } catch (SftpException e) {
            e.printStackTrace();
        }
    }
}
Iraklis
fonte
15
Um finallybloco deve ser usado para incluir o código de limpeza do canal, para garantir que ele sempre seja executado.
hotshot309
Estou recebendo essa exceção agora: com.jcraft.jsch.JSchException: Session.connect: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 2048 (inclusive)
anon58192932 19/01
Eu achei o JSCH com 0 ou 1 dependências extras. Você pode ignorar a dependência JZLIB se desativar a compactação. // desabilita a compressão session.setConfig ("pression.s2c "," none "); session.setConfig ("pression.c2s "," none ");
#
1
Sem uma verificação estrita do host, você é suscetível a um ataque do tipo intermediário.
rustyx
44

Abaixo está um exemplo usando o Apache Common VFS:

FileSystemOptions fsOptions = new FileSystemOptions();
SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(fsOptions, "no");
FileSystemManager fsManager = VFS.getManager();
String uri = "sftp://user:password@host:port/absolute-path";
FileObject fo = fsManager.resolveFile(uri, fsOptions);
Chris J
fonte
5
Outra coisa boa a fazer é definir o tempo limite, para que, se o sistema remoto estiver offline, você não fique para sempre. Você pode fazer isso da mesma forma que foi feito para a desativação da verificação da chave do host: SftpFileSystemConfigBuilder.getInstance (). SetTimeout (fsOptions, 5000);
Scott Jones
Como você recomendaria fechar essa conexão ao usar vários clientes SFTP ao mesmo tempo?
2Big2BeSmall
2
E se a minha senha contiver o símbolo @?
usar o seguinte
23

Esta foi a solução que eu vim com http://sourceforge.net/projects/sshtools/ (a maioria dos erros omitidos por questões de clareza). Este é um trecho do meu blog

SshClient ssh = new SshClient();
ssh.connect(host, port);
//Authenticate
PasswordAuthenticationClient passwordAuthenticationClient = new PasswordAuthenticationClient();
passwordAuthenticationClient.setUsername(userName);
passwordAuthenticationClient.setPassword(password);
int result = ssh.authenticate(passwordAuthenticationClient);
if(result != AuthenticationProtocolState.COMPLETE){
     throw new SFTPException("Login to " + host + ":" + port + " " + userName + "/" + password + " failed");
}
//Open the SFTP channel
SftpClient client = ssh.openSftpClient();
//Send the file
client.put(filePath);
//disconnect
client.quit();
ssh.disconnect();
David Hayes
fonte
7
Eu concordo (tardiamente), funcionou bem para o site / download original que eu solicitei, mas voltou a funcionar para o novo. Estou no processo de mudança para JSch
David Hayes
23

Uma boa abstração sobre o Jsch é o Apache commons-vfs, que oferece uma API de sistema de arquivos virtual que torna quase transparente o acesso e a gravação de arquivos SFTP. Funcionou bem para nós.

Boris Terzic
fonte
1
é possível usar chaves pré-compartilhadas em combinação com commons-vfs?
Benedikt Waldvogel #
2
Sim, ele é. Se você precisar de identidades não padrão, poderá chamar SftpFileSystemConfigBuilder.getInstance (). SetIdentities (...).
Russ Hayward
Você pode usar chaves pré-compartilhadas. Mas essas chaves precisam estar sem senha. O OtrosLogViewer está usando a autorização de chave SSH com o VFS, mas requer a remoção da frase secreta da chave ( code.google.com/p/otroslogviewer/wiki/SftpAuthPubKey )
KrzyH
19

Há uma boa comparação das três bibliotecas Java maduras para SFTP: Commons VFS, SSHJ e JSch

Para resumir, o SSHJ tem a API mais clara e é a melhor delas, se você não precisar de outros apoios de armazenamento fornecidos pelo Commons VFS.

Aqui está o exemplo SSHJ editado no github :

final SSHClient ssh = new SSHClient();
ssh.loadKnownHosts(); // or, to skip host verification: ssh.addHostKeyVerifier(new PromiscuousVerifier())
ssh.connect("localhost");
try {
    ssh.authPassword("user", "password"); // or ssh.authPublickey(System.getProperty("user.name"))
    final SFTPClient sftp = ssh.newSFTPClient();
    try {
        sftp.get("test_file", "/tmp/test.tmp");
    } finally {
        sftp.close();
    }
} finally {
    ssh.disconnect();
}
Sasha
fonte
2
Existe uma maneira de obter o arquivo como um InputStream?
7268 Johan Johan
2
sshj em 2019 ainda está bem conservado e é usado por Alpakka (Akka) projeto
Maxence
13

Biblioteca SFTP do Apache Commons

Arquivo de propriedades Java comum para todos os exemplos

serverAddress = 111.222.333.444

userId = myUserId

password = myPassword

remoteDirectory = produtos /

localDirectory = import /

Carregar arquivo para o servidor remoto usando SFTP

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.Selectors;
import org.apache.commons.vfs2.impl.StandardFileSystemManager;
import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;

public class SendMyFiles {

 static Properties props;

 public static void main(String[] args) {

  SendMyFiles sendMyFiles = new SendMyFiles();
  if (args.length < 1)
  {
   System.err.println("Usage: java " + sendMyFiles.getClass().getName()+
     " Properties_file File_To_FTP ");
   System.exit(1);
  }

  String propertiesFile = args[0].trim();
  String fileToFTP = args[1].trim();
  sendMyFiles.startFTP(propertiesFile, fileToFTP);

 }

 public boolean startFTP(String propertiesFilename, String fileToFTP){

  props = new Properties();
  StandardFileSystemManager manager = new StandardFileSystemManager();

  try {

   props.load(new FileInputStream("properties/" + propertiesFilename));
   String serverAddress = props.getProperty("serverAddress").trim();
   String userId = props.getProperty("userId").trim();
   String password = props.getProperty("password").trim();
   String remoteDirectory = props.getProperty("remoteDirectory").trim();
   String localDirectory = props.getProperty("localDirectory").trim();

   //check if the file exists
   String filepath = localDirectory +  fileToFTP;
   File file = new File(filepath);
   if (!file.exists())
    throw new RuntimeException("Error. Local file not found");

   //Initializes the file manager
   manager.init();

   //Setup our SFTP configuration
   FileSystemOptions opts = new FileSystemOptions();
   SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(
     opts, "no");
   SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, true);
   SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000);

   //Create the SFTP URI using the host name, userid, password,  remote path and file name
   String sftpUri = "sftp://" + userId + ":" + password +  "@" + serverAddress + "/" + 
     remoteDirectory + fileToFTP;

   // Create local file object
   FileObject localFile = manager.resolveFile(file.getAbsolutePath());

   // Create remote file object
   FileObject remoteFile = manager.resolveFile(sftpUri, opts);

   // Copy local file to sftp server
   remoteFile.copyFrom(localFile, Selectors.SELECT_SELF);
   System.out.println("File upload successful");

  }
  catch (Exception ex) {
   ex.printStackTrace();
   return false;
  }
  finally {
   manager.close();
  }

  return true;
 }


}

Baixar arquivo do servidor remoto usando SFTP

import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.Selectors;
import org.apache.commons.vfs2.impl.StandardFileSystemManager;
import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;

public class GetMyFiles {

 static Properties props;

 public static void main(String[] args) {

  GetMyFiles getMyFiles = new GetMyFiles();
  if (args.length < 1)
  {
   System.err.println("Usage: java " + getMyFiles.getClass().getName()+
   " Properties_filename File_To_Download ");
   System.exit(1);
  }

  String propertiesFilename = args[0].trim();
  String fileToDownload = args[1].trim();
  getMyFiles.startFTP(propertiesFilename, fileToDownload);

 }

 public boolean startFTP(String propertiesFilename, String fileToDownload){

  props = new Properties();
  StandardFileSystemManager manager = new StandardFileSystemManager();

  try {

   props.load(new FileInputStream("properties/" + propertiesFilename));
   String serverAddress = props.getProperty("serverAddress").trim();
   String userId = props.getProperty("userId").trim();
   String password = props.getProperty("password").trim();
   String remoteDirectory = props.getProperty("remoteDirectory").trim();
   String localDirectory = props.getProperty("localDirectory").trim();


   //Initializes the file manager
   manager.init();

   //Setup our SFTP configuration
   FileSystemOptions opts = new FileSystemOptions();
   SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(
     opts, "no");
   SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, true);
   SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000);

   //Create the SFTP URI using the host name, userid, password,  remote path and file name
   String sftpUri = "sftp://" + userId + ":" + password +  "@" + serverAddress + "/" + 
     remoteDirectory + fileToDownload;

   // Create local file object
   String filepath = localDirectory +  fileToDownload;
   File file = new File(filepath);
   FileObject localFile = manager.resolveFile(file.getAbsolutePath());

   // Create remote file object
   FileObject remoteFile = manager.resolveFile(sftpUri, opts);

   // Copy local file to sftp server
   localFile.copyFrom(remoteFile, Selectors.SELECT_SELF);
   System.out.println("File download successful");

  }
  catch (Exception ex) {
   ex.printStackTrace();
   return false;
  }
  finally {
   manager.close();
  }

  return true;
 }

}

Excluir um arquivo no servidor remoto usando SFTP

import java.io.FileInputStream;
import java.util.Properties;

import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.impl.StandardFileSystemManager;
import org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder;

public class DeleteRemoteFile {

 static Properties props;

 public static void main(String[] args) {

  DeleteRemoteFile getMyFiles = new DeleteRemoteFile();
  if (args.length < 1)
  {
   System.err.println("Usage: java " + getMyFiles.getClass().getName()+
   " Properties_filename File_To_Delete ");
   System.exit(1);
  }

  String propertiesFilename = args[0].trim();
  String fileToDownload = args[1].trim();
  getMyFiles.startFTP(propertiesFilename, fileToDownload);

 }

 public boolean startFTP(String propertiesFilename, String fileToDownload){

  props = new Properties();
  StandardFileSystemManager manager = new StandardFileSystemManager();

  try {

   props.load(new FileInputStream("properties/" + propertiesFilename));
   String serverAddress = props.getProperty("serverAddress").trim();
   String userId = props.getProperty("userId").trim();
   String password = props.getProperty("password").trim();
   String remoteDirectory = props.getProperty("remoteDirectory").trim();


   //Initializes the file manager
   manager.init();

   //Setup our SFTP configuration
   FileSystemOptions opts = new FileSystemOptions();
   SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(
     opts, "no");
   SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(opts, true);
   SftpFileSystemConfigBuilder.getInstance().setTimeout(opts, 10000);

   //Create the SFTP URI using the host name, userid, password,  remote path and file name
   String sftpUri = "sftp://" + userId + ":" + password +  "@" + serverAddress + "/" + 
     remoteDirectory + fileToDownload;

   //Create remote file object
   FileObject remoteFile = manager.resolveFile(sftpUri, opts);

   //Check if the file exists
   if(remoteFile.exists()){
    remoteFile.delete();
    System.out.println("File delete successful");
   }

  }
  catch (Exception ex) {
   ex.printStackTrace();
   return false;
  }
  finally {
   manager.close();
  }

  return true;
 }

}
AZ_
fonte
como configurar enquanto possui ssh-key (chave pública) para copiar arquivos no servidor. Porque eu preciso fazer ssh_trust entre meu servidor e o servidor remoto.
MS Parmar
7

hierynomus / sshj possui uma implementação completa do SFTP versão 3 (o que o OpenSSH implementa)

Código de exemplo de SFTPUpload.java

package net.schmizz.sshj.examples;

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.xfer.FileSystemFile;

import java.io.File;
import java.io.IOException;

/** This example demonstrates uploading of a file over SFTP to the SSH server. */
public class SFTPUpload {

    public static void main(String[] args)
            throws IOException {
        final SSHClient ssh = new SSHClient();
        ssh.loadKnownHosts();
        ssh.connect("localhost");
        try {
            ssh.authPublickey(System.getProperty("user.name"));
            final String src = System.getProperty("user.home") + File.separator + "test_file";
            final SFTPClient sftp = ssh.newSFTPClient();
            try {
                sftp.put(new FileSystemFile(src), "/tmp");
            } finally {
                sftp.close();
            }
        } finally {
            ssh.disconnect();
        }
    }

}
shikhar
fonte
2
bom trabalho!! um exemplo na página principal pode ser útil.
OhadR 4/15/15
4

A biblioteca JSch é a biblioteca poderosa que pode ser usada para ler arquivos do servidor SFTP. Abaixo está o código testado para ler o arquivo do local SFTP linha por linha

JSch jsch = new JSch();
        Session session = null;
        try {
            session = jsch.getSession("user", "127.0.0.1", 22);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setPassword("password");
            session.connect();

            Channel channel = session.openChannel("sftp");
            channel.connect();
            ChannelSftp sftpChannel = (ChannelSftp) channel;

            InputStream stream = sftpChannel.get("/usr/home/testfile.txt");
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(stream));
                String line;
                while ((line = br.readLine()) != null) {
                    System.out.println(line);
                }

            } catch (IOException io) {
                System.out.println("Exception occurred during reading file from SFTP server due to " + io.getMessage());
                io.getMessage();

            } catch (Exception e) {
                System.out.println("Exception occurred during reading file from SFTP server due to " + e.getMessage());
                e.getMessage();

            }

            sftpChannel.exit();
            session.disconnect();
        } catch (JSchException e) {
            e.printStackTrace();
        } catch (SftpException e) {
            e.printStackTrace();
        }

Por favor, consulte o blog para todo o programa.

Ankur jain
fonte
3

Andy, para excluir o arquivo no sistema remoto, você precisa usar o (channelExec)JSch e passar os comandos unix / linux para excluí-lo.

Pushpinder Rattan
fonte
2

Experimente o edtFTPj / PRO , uma biblioteca de clientes SFTP robusta e madura que suporta pools de conexões e operações assíncronas. Também suporta FTP e FTPS, para que todas as bases para transferência segura de arquivos sejam cobertas.

Bruce Blackshaw
fonte
2

Embora as respostas acima tenham sido muito úteis, passei um dia para fazê-las funcionar, enfrentando várias exceções como "canal quebrado", "chave rsa desconhecida" e "pacote corrompido".

Abaixo está uma classe reutilizável de trabalho para SFTP FILE UPLOAD / DOWNLOAD usando a biblioteca JSch.

Uso de upload:

SFTPFileCopy upload = new SFTPFileCopy(true, /path/to/sourcefile.png", /path/to/destinationfile.png");

Uso do download:

SFTPFileCopy download = new SFTPFileCopy(false, "/path/to/sourcefile.png", "/path/to/destinationfile.png");

O código da classe:

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UIKeyboardInteractive;
import com.jcraft.jsch.UserInfo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.swing.JOptionPane;
import menue.Menue;

public class SFTPFileCopy1 {

    public SFTPFileCopy1(boolean upload, String sourcePath, String destPath) throws FileNotFoundException, IOException {
        Session session = null;
        Channel channel = null;
        ChannelSftp sftpChannel = null;
        try {
            JSch jsch = new JSch();
            //jsch.setKnownHosts("/home/user/.putty/sshhostkeys");
            session = jsch.getSession("login", "mysite.com", 22);
            session.setPassword("password");

            UserInfo ui = new MyUserInfo() {
                public void showMessage(String message) {

                    JOptionPane.showMessageDialog(null, message);

                }

                public boolean promptYesNo(String message) {

                    Object[] options = {"yes", "no"};

                    int foo = JOptionPane.showOptionDialog(null,
                            message,
                            "Warning",
                            JOptionPane.DEFAULT_OPTION,
                            JOptionPane.WARNING_MESSAGE,
                            null, options, options[0]);

                    return foo == 0;

                }
            };
            session.setUserInfo(ui);

            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();
            channel = session.openChannel("sftp");
            channel.setInputStream(System.in);
            channel.setOutputStream(System.out);
            channel.connect();
            sftpChannel = (ChannelSftp) channel;

            if (upload) { // File upload.
                byte[] bufr = new byte[(int) new File(sourcePath).length()];
                FileInputStream fis = new FileInputStream(new File(sourcePath));
                fis.read(bufr);
                ByteArrayInputStream fileStream = new ByteArrayInputStream(bufr);
                sftpChannel.put(fileStream, destPath);
                fileStream.close();
            } else { // File download.
                byte[] buffer = new byte[1024];
                BufferedInputStream bis = new BufferedInputStream(sftpChannel.get(sourcePath));
                OutputStream os = new FileOutputStream(new File(destPath));
                BufferedOutputStream bos = new BufferedOutputStream(os);
                int readCount;
                while ((readCount = bis.read(buffer)) > 0) {
                    bos.write(buffer, 0, readCount);
                }
                bis.close();
                bos.close();
            }
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            if (sftpChannel != null) {
                sftpChannel.exit();
            }
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        }
    }

    public static abstract class MyUserInfo
            implements UserInfo, UIKeyboardInteractive {

        public String getPassword() {
            return null;
        }

        public boolean promptYesNo(String str) {
            return false;
        }

        public String getPassphrase() {
            return null;
        }

        public boolean promptPassphrase(String message) {
            return false;
        }

        public boolean promptPassword(String message) {
            return false;
        }

        public void showMessage(String message) {
        }

        public String[] promptKeyboardInteractive(String destination,
                String name,
                String instruction,
                String[] prompt,
                boolean[] echo) {

            return null;
        }
    }
}
Zon
fonte
1

Você também possui o JFileUpload com complemento SFTP (Java também): http://www.jfileupload.com/products/sftp/index.html


fonte
JFileUpload é um applet, não uma lib. A licença é comercial. Também não parece ativo.
rü-
1

Eu uso essa API SFTP chamada Zehon, é ótima, muito fácil de usar com muito código de exemplo. Aqui está o site http://www.zehon.com


fonte
2
Zehon parece morto. E onde está a fonte? Qual 'licença' está por trás de 'grátis'?
rü-
0

A melhor solução que encontrei é a Paramiko . Existe uma versão Java.

Brian Clapper
fonte
O github.com/terencehonles/jaramiko é abandonado em favor do JSch (consulte o aviso no github).
ru-