¿Cómo recuperar un archivo de un servidor a través de SFTP?

228

Estoy tratando de recuperar un archivo de un servidor usando SFTP (a diferencia de FTPS) usando Java. ¿Cómo puedo hacer esto?

David Hayes
fuente

Respuestas:

198

Otra opción es considerar mirar la biblioteca JSch . JSch parece ser la biblioteca preferida para algunos grandes proyectos de código abierto, incluidos Eclipse, Ant y Apache Commons HttpClient, entre otros.

Admite muy bien los inicios de sesión de usuario / pase y certificados, así como todas las otras características SSH2 deliciosas.

Aquí hay una simple recuperación remota de archivos a través de SFTP. El manejo de errores se deja como un ejercicio para el lector :-)

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
fuente
1
Cheekysoft, noté, mientras usaba Jsch, eliminar archivos en el servidor sftp no funciona. También cambiar el nombre de los archivos no funciona también. ¿Alguna idea por favor? Andy
1
Lo siento, no es algo con lo que trabajo en este momento. (Intente dejar este tipo de respuestas como comentarios, como este mensaje, y no como una nueva respuesta a la pregunta original)
Cheekysoft
1
¿Cuál es ese bloque de código después de la asignación de sesión? ¿Es esa una elegante sintaxis Java que nunca he visto? Si es así, ¿qué logra ser escrito de esa manera?
Michael Peterson
3
La sintaxis estándar de Java @ p1x3l5 permite insertar un bloque en cualquier lugar; se puede usar para proporcionar un control más preciso sobre el alcance variable, si lo desea. Sin embargo, en este caso, es solo una ayuda visual para ayudar a indicar las dos opciones de implementación: usar la versión interactiva que solicita una contraseña del usuario, o usar una contraseña codificada que no requiera intervención del usuario, pero posiblemente un riesgo de seguridad adicional.
Cheekysoft
109

Aquí está el código fuente completo de un ejemplo usando JSch sin tener que preocuparse por la comprobación de la clave 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
fuente
15
Se finallydebe usar un bloque para incluir el código de limpieza del canal, para garantizar que siempre se ejecute.
hotshot309
Recibo esta excepción ahora: 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
Encontré que JSCH tenía 0 o 1 dependencias adicionales. Puede ignorar la dependencia JZLIB si deshabilita la compresión. // deshabilitar la compresión session.setConfig ("compress.s2c", "none"); session.setConfig ("compress.c2s", "none");
englebart 01 de
1
Sin una estricta comprobación de host, eres susceptible a un ataque de hombre en el medio.
rustyx
44

A continuación se muestra un ejemplo usando 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
fuente
55
Otra buena cosa que hacer es establecer el tiempo de espera, de modo que si el sistema remoto está fuera de línea, no se quede allí para siempre. Puede hacer esto tal como se hizo para deshabilitar la comprobación de clave de host: SftpFileSystemConfigBuilder.getInstance (). SetTimeout (fsOptions, 5000);
Scott Jones,
¿Cómo recomendaría cerrar esta conexión cuando se utilizan varios clientes SFTP al mismo tiempo?
2Big2BeSmall
2
¿Qué pasa si mi contraseña contiene el símbolo @?
Usuario3
23

Esta fue la solución que se me ocurrió http://sourceforge.net/projects/sshtools/ (la mayoría de los errores se omiten para mayor claridad). Este es un extracto de mi 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
fuente
77
Estoy de acuerdo (tardíamente), funcionó bien para el sitio / descarga original que necesitaba pero reafirmó su funcionamiento para el nuevo. Estoy en el proceso de cambiarme a JSch
David Hayes
23

Una buena abstracción sobre Jsch es Apache commons-vfs, que ofrece una API de sistema de archivos virtual que hace que el acceso y la escritura de archivos SFTP sean casi transparentes. Funcionó bien para nosotros.

Boris Terzic
fuente
1
¿Es posible usar claves precompartidas en combinación con commons-vfs?
Benedikt Waldvogel
2
Sí lo es. Si necesita identidades no estándar, puede llamar a SftpFileSystemConfigBuilder.getInstance (). SetIdentities (...).
Russ Hayward
Puede usar claves previamente compartidas. Pero estas claves deben estar sin contraseña. OtrosLogViewer está utilizando la autorización de clave SSH con VFS pero requiere eliminar la frase de contraseña de la clave ( code.google.com/p/otroslogviewer/wiki/SftpAuthPubKey )
KrzyH
19

Hay una buena comparación de las 3 bibliotecas Java maduras para SFTP: Commons VFS, SSHJ y JSch

En resumen, SSHJ tiene la API más clara y es la mejor de ellas si no necesita otro soporte de almacenamiento proporcionado por Commons VFS.

Aquí está el ejemplo editado SSHJ de 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
fuente
2
¿Hay alguna manera de obtener el archivo como InputStream?
Johan
2
sshj en 2019 todavía está bien mantenido y es utilizado por el proyecto Alpakka (Akka)
Maxence
13

Biblioteca SFTP de Apache Commons

Archivo de propiedades de Java común para todos los ejemplos

serverAddress = 111.222.333.444

userId = myUserId

contraseña = myPassword

remoteDirectory = productos /

localDirectory = importar /

Subir archivo al 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;
 }


}

Descargue el archivo del servidor remoto utilizando 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;
 }

}

Eliminar un archivo en el 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;
 }

}
ARIZONA_
fuente
cómo configurar mientras se tiene ssh-key (clave pública) para copiar archivos en el servidor. Porque necesito hacer ssh_trust entre mi servidor y el servidor remoto.
MS Parmar
7

hierynomus / sshj tiene una implementación completa de SFTP versión 3 (lo que implementa OpenSSH)

Código de ejemplo 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
fuente
2
¡¡buen trabajo!! Sin embargo, un ejemplo en la página principal podría ser útil.
OhadR
4

La biblioteca JSch es la biblioteca poderosa que se puede usar para leer archivos del servidor SFTP. A continuación se muestra el código probado para leer el archivo de la ubicación SFTP línea por línea

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 el blog para todo el programa.

Ankur jain
fuente
3

Andy, para eliminar el archivo en el sistema remoto, debes usar (channelExec)JSch y pasar los comandos unix / linux para eliminarlo.

Ratán Pushpinder
fuente
2

Pruebe edtFTPj / PRO , una biblioteca cliente SFTP madura y robusta que admite agrupaciones de conexiones y operaciones asincrónicas. También es compatible con FTP y FTPS, por lo que todas las bases para la transferencia segura de archivos están cubiertas.

Bruce Blackshaw
fuente
2

Aunque las respuestas anteriores fueron muy útiles, pasé un día para que funcionen, enfrentando varias excepciones como "canal roto", "clave rsa desconocida" y "paquete corrupto".

A continuación se muestra una clase reutilizable de trabajo para la carga / descarga de archivos SFTP utilizando la biblioteca JSch.

Subir uso:

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

Uso de descarga:

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

El código de clase:

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
fuente
1

Utilizo esta API SFTP llamada Zehon, es genial, muy fácil de usar con una gran cantidad de código de muestra. Aquí está el sitio http://www.zehon.com


fuente
2
Zehon parece muerto. ¿Y dónde está la fuente? ¿Qué 'licencia' está detrás de 'gratis'?
rü-