Comment récupérer un fichier d'un serveur via SFTP?

228

J'essaie de récupérer un fichier à partir d'un serveur en utilisant SFTP (par opposition à FTPS) en utilisant Java. Comment puis-je faire ceci?

David Hayes
la source

Réponses:

198

Une autre option consiste à envisager de consulter la bibliothèque JSch . JSch semble être la bibliothèque préférée pour quelques grands projets open source, y compris Eclipse, Ant et Apache Commons HttpClient, entre autres.

Il prend en charge à la fois les connexions utilisateur / passe et basées sur des certificats, ainsi que toute une foule d'autres fonctionnalités SSH2 délicieuses.

Voici un simple fichier distant récupéré via SFTP. La gestion des erreurs est laissée en exercice au lecteur :-)

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
la source
1
Cheekysoft, j'ai remarqué - lors de l'utilisation de Jsch - la suppression de fichiers sur le serveur sftp ne fonctionne pas. Le changement de nom des fichiers ne fonctionne pas non plus. Des idées s'il vous plaît ??? Andy
1
Désolé, ce n'est pas quelque chose avec lequel je travaille en ce moment. (Veuillez essayer de laisser ce type de réponses sous forme de commentaires - comme ce message - et non comme une nouvelle réponse à la question d'origine)
Cheekysoft
1
Quel est ce bloc de code après l'attribution de la session? Est-ce une syntaxe Java sophistiquée que je n'ai jamais vue? Si oui - qu'est-ce que cela permet d'être écrit de cette façon?
Michael Peterson
3
@ p1x3l5 la syntaxe java standard permet d'insérer un bloc n'importe où; il peut être utilisé pour fournir un contrôle plus fin sur la portée variable, si vous le souhaitez. Cependant, dans ce cas, ce n'est qu'une aide visuelle pour aider à indiquer les deux choix d'implémentation: soit utiliser la version interactive qui demande un mot de passe à l'utilisateur, soit utiliser un mot de passe codé en dur ne nécessitant aucune intervention de l'utilisateur mais sans doute un risque de sécurité supplémentaire.
Cheekysoft
109

Voici le code source complet d'un exemple utilisant JSch sans avoir à vous soucier de la vérification de la clé 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
la source
15
Un finallybloc doit être utilisé pour inclure le code de nettoyage de canal, pour garantir qu'il s'exécute toujours.
hotshot309
Je reçois cette exception maintenant: 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
J'ai trouvé que JSCH avait 0 ou 1 dépendances supplémentaires. Vous pouvez ignorer la dépendance JZLIB si vous désactivez la compression. // désactiver la compression session.setConfig ("compression.s2c", "none"); session.setConfig ("compression.c2s", "aucun");
englebart
1
Sans vérification stricte de l'hôte, vous êtes vulnérable à une attaque de l'homme du milieu.
rustyx
44

Voici un exemple d'utilisation d'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
la source
5
Une autre bonne chose à faire est de définir le délai d'expiration, de sorte que si le système distant est hors ligne, vous n'y restiez pas éternellement. Vous pouvez le faire comme pour la désactivation de la vérification de la clé hôte: SftpFileSystemConfigBuilder.getInstance (). SetTimeout (fsOptions, 5000);
Scott Jones
Comment conseilleriez-vous de fermer cette connexion lorsque vous utilisez plusieurs clients SFTP en même temps?
2Big2BeSmall
2
Et si mon mot de passe contient le symbole @?
User3
23

C'est la solution que j'ai trouvée avec http://sourceforge.net/projects/sshtools/ (la plupart des erreurs de gestion ont été omises pour plus de clarté). Ceci est un extrait de mon 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
la source
7
Je suis d'accord (tardivement), cela a bien fonctionné pour le site d'origine / téléchargement dont j'avais besoin, mais cela a refusé de fonctionner pour le nouveau. Je suis en train de passer à JSch
David Hayes
23

Une belle abstraction au-dessus de Jsch est Apache commons-vfs qui offre une API de système de fichiers virtuel qui rend l'accès et l'écriture des fichiers SFTP presque transparents. A bien fonctionné pour nous.

Boris Terzic
la source
1
est-il possible d'utiliser des clés pré-partagées en combinaison avec commons-vfs?
Benedikt Waldvogel,
2
Oui, ça l'est. Si vous avez besoin d'identités non standard, vous pouvez appeler SftpFileSystemConfigBuilder.getInstance (). SetIdentities (...).
Russ Hayward
Vous pouvez utiliser des clés pré-partagées. Mais ces clés doivent être sans mot de passe. OtrosLogViewer utilise l'autorisation de clé SSH avec VFS mais nécessite de supprimer la phrase secrète de la clé ( code.google.com/p/otroslogviewer/wiki/SftpAuthPubKey )
KrzyH
19

Il y a une belle comparaison des 3 bibliothèques Java matures pour SFTP: Commons VFS, SSHJ et JSch

Pour résumer, SSHJ possède l'API la plus claire et c'est la meilleure d'entre elles si vous n'avez pas besoin d'autres supports de stockage fournis par Commons VFS.

Voici un exemple SSHJ édité 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
la source
2
Existe-t-il un moyen d'obtenir le fichier en tant que InputStream?
Johan
2
sshj en 2019 est toujours bien entretenu et est utilisé par le projet Alpakka (Akka)
Maxence
13

Bibliothèque SFTP Apache Commons

Fichier de propriétés Java commun pour tous les exemples

serverAddress = 111.222.333.444

userId = myUserId

mot de passe = myPassword

remoteDirectory = produits /

localDirectory = import /

Télécharger un fichier sur un serveur distant à l'aide de 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;
 }


}

Télécharger le fichier à partir du serveur distant à l'aide de 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;
 }

}

Supprimer un fichier sur un serveur distant à l'aide de 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_
la source
comment configurer tout en ayant ssh-key (clé publique) pour copier des fichiers sur le serveur. Parce que j'ai besoin de faire ssh_trust entre mon serveur et un serveur distant.
MS Parmar
7

hierynomus / sshj a une implémentation complète de SFTP version 3 (ce qu'OpenSSH implémente)

Exemple de code 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
la source
2
Bon travail!! un exemple dans la page principale pourrait cependant être utile.
OhadR
4

La bibliothèque JSch est la bibliothèque puissante qui peut être utilisée pour lire des fichiers à partir du serveur SFTP. Voici le code testé pour lire le fichier de l'emplacement SFTP ligne par ligne

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();
        }

Veuillez consulter le blog pour l'ensemble du programme.

Ankur jain
la source
3

Andy, pour supprimer un fichier sur un système distant, vous devez utiliser (channelExec)JSch et passer des commandes unix / linux pour le supprimer.

Pushpinder Rattan
la source
2

Essayez edtFTPj / PRO , une bibliothèque client SFTP mature et robuste qui prend en charge les pools de connexions et les opérations asynchrones. Prend également en charge FTP et FTPS afin que toutes les bases du transfert de fichiers sécurisé soient couvertes.

Bruce Blackshaw
la source
2

Bien que les réponses ci-dessus aient été très utiles, j'ai passé une journée à les faire fonctionner, face à diverses exceptions comme "canal cassé", "clé rsa inconnue" et "paquet corrompu".

Vous trouverez ci-dessous une classe réutilisable fonctionnelle pour le téléchargement / téléchargement de fichiers SFTP à l'aide de la bibliothèque JSch.

Utilisation de téléchargement:

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

Utilisation du téléchargement:

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

Le code de 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
la source
1

Vous avez également JFileUpload avec le module complémentaire SFTP (Java aussi): http://www.jfileupload.com/products/sftp/index.html


la source
JFileUpload est une applet, pas une lib. La licence est commerciale. Ne semble pas actif non plus.
rü-
1

J'utilise cette API SFTP appelée Zehon, c'est génial, si facile à utiliser avec beaucoup d'exemples de code. Voici le site http://www.zehon.com


la source
2
Zehon semble mort. Et où est la source? Quelle «licence» se cache derrière «gratuit»?
rü-
0

La meilleure solution que j'ai trouvée est Paramiko . Il existe une version Java.

Brian Clapper
la source
github.com/terencehonles/jaramiko est abandonné au profit de JSch (voir avis sur github).
rü-