# Download einer Datei durch SFTP mit Java



## Ollek (16. Feb 2010)

Morgen,

ich programmiere gerade eine Schnittstelle, die eine Datei von einem Server via SFTP herunterladen soll.
Die Schnittstelle programmiere ich mit der com.trilead.ssh2-Lib. Ich baue eine erfolreiche Verbindung zum Server auf. Allerdings happerts dann am Download der Datei.

Dort schmeißt er mir folgendes:

Starting download....
java.io.IOException: Error during SCP transfer.
	at XXX(SCPClient.java:724)
	at XXX.SCPClient.get(SCPClient.java:616)
	at XXX.SFTProcessor.initDownload(SFTProcessor.java:28)
	at XXX.XXX.<init>(XXX.java:20)
	at XXX.main(XXX.java:12)
Caused by: java.io.IOException: Remote scp terminated unexpectedly.
	at XXX.SCPClient.receiveFiles(SCPClient.java:319)
	at XXX.SCPClient.get(SCPClient.java:720)
	... 4 more
An error has occured while dowloading: 
An error has occured while dowloading: Error during SCP transfer.

Kennt sich jemand damit aus? :rtfm:

Gruß

Ollek


----------



## FArt (16. Feb 2010)

> Kennt sich jemand damit aus?


Ja.

Es gab einen Fehler beim SCP Transfer. Dieser wurde über eine Exception gemeldet.


----------



## Ollek (16. Feb 2010)

> Es gab einen Fehler beim SCP Transfer. Dieser wurde über eine Exception gemeldet.



Jap.

Leider komme ich da auch nicht mit weiter... ;(
Ich bekomme nur diese Ausgabe, die ich dort oben auch gepostet habe...


----------



## FArt (16. Feb 2010)

java debug ssl - Google-Suche
Caused by: java.io.IOException: Remote scp terminated unexpectedly - Google-Suche
java use debugger - Google-Suche


----------



## Ollek (16. Feb 2010)

Das habe ich auch schon probiert und komme so auch nicht weiter!


----------



## punkt21 (16. Feb 2010)

wie wäre es mit codeausschnitt?


----------



## HoaX (16. Feb 2010)

Schonmal mit nem anderen SFTP-Client versucht (außerhalb von Java)? Sicher dass es SFTP ist und nicht FTPS? Welcher Server (Software) wird denn verwendet?


----------



## Ollek (17. Feb 2010)

Ich habe es schon mit Filezilla probiert, klappt alles wunderbar. Bei Filezilla muss ich ja sftp:// davor schreiben.

Hier ein paar Codebeispiele...

SFTProcessor

```
public class SFTProcessor {
	
	SSHConnection sshCon = new SSHConnection();
	private boolean downloadError = false;

	
	public void initDownload() {

        sshCon.initSSH();
        
        String strDirName = "C:\\Downloads";
    	//Directory.createDirectory(strDirName);

    	SSHClient sshClient = new SSHClient();
        sshClient.executeSSHcommand(sshCon.getSession(), "cd Verzeichniss; ls;");
        SCPClient scpClient = new SCPClient(sshCon.getConnection());

        try {
            System.out.println("Starting download....");
            try {
                scpClient.get("Dateiname", strDirName);
            }catch(IllegalArgumentException e) {
                System.out.println("Nothing to download ?!");
                System.out.println("Nothing to download ?! "+e.getMessage()+"\r\n");
            }
            System.out.println("Download finished !");
        } catch (IOException e) {
            downloadError = true;
            System.out.println("An error has occured while dowloading: ");
            System.out.println("An error has occured while dowloading: "+e.getMessage()+"\r\n");
            e.printStackTrace();
        }

//        if (sshClient.getErrorMessages().length() > 0) {
//            downloadError = true;
//            System.out.println("ErrorMessage from RemoteConsole: ");
//            System.out.println("ErrorMessage from RemoteConsole: ");
//        }

        sshCon.getSession().close();
    }

}
```

Jetzt kommt der Standart von trilead. der SCPClient


```
/**
 * A very basic <code>SCPClient</code> that can be used to copy files from/to
 * the SSH-2 server. On the server side, the "scp" program must be in the PATH.
 * <p>
 * This scp client is thread safe - you can download (and upload) different sets
 * of files concurrently without any troubles. The <code>SCPClient</code> is
 * actually mapping every request to a distinct {@link Session}.
 * 
 * @author Christian Plattner, [email]plattner@trilead.com[/email]
 * @version $Id: SCPClient.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
 */

public class SCPClient
{
	Connection conn;

	class LenNamePair
	{
		long length;
		String filename;
	}

	public SCPClient(Connection conn)
	{
		if (conn == null)
			throw new IllegalArgumentException("Cannot accept null argument!");
		this.conn = conn;
	}

	private void readResponse(InputStream is) throws IOException
	{
		int c = is.read();

		if (c == 0)
			return;

		if (c == -1)
			throw new IOException("Remote scp terminated unexpectedly.");

		if ((c != 1) && (c != 2))
			throw new IOException("Remote scp sent illegal error code.");

		if (c == 2)
			throw new IOException("Remote scp terminated with error.");

		String err = receiveLine(is);
		throw new IOException("Remote scp terminated with error (" + err + ").");
	}

	private String receiveLine(InputStream is) throws IOException
	{
		StringBuffer sb = new StringBuffer(30);

		while (true)
		{
			/*
			 * This is a random limit - if your path names are longer, then
			 * adjust it
			 */

			if (sb.length() > 8192)
				throw new IOException("Remote scp sent a too long line");

			int c = is.read();

			if (c < 0)
				throw new IOException("Remote scp terminated unexpectedly.");

			if (c == '\n')
				break;

			sb.append((char) c);

		}
		return sb.toString();
	}

	private LenNamePair parseCLine(String line) throws IOException
	{
		/* Minimum line: "xxxx y z" ---> 8 chars */

		long len;

		if (line.length() < 8)
			throw new IOException("Malformed C line sent by remote SCP binary, line too short.");

		if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
			throw new IOException("Malformed C line sent by remote SCP binary.");

		int length_name_sep = line.indexOf(' ', 5);

		if (length_name_sep == -1)
			throw new IOException("Malformed C line sent by remote SCP binary.");

		String length_substring = line.substring(5, length_name_sep);
		String name_substring = line.substring(length_name_sep + 1);

		if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
			throw new IOException("Malformed C line sent by remote SCP binary.");

		if ((6 + length_substring.length() + name_substring.length()) != line.length())
			throw new IOException("Malformed C line sent by remote SCP binary.");

		try
		{
			len = Long.parseLong(length_substring);
		}
		catch (NumberFormatException e)
		{
			throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length.");
		}

		if (len < 0)
			throw new IOException("Malformed C line sent by remote SCP binary, illegal file length.");

		LenNamePair lnp = new LenNamePair();
		lnp.length = len;
		lnp.filename = name_substring;

		return lnp;
	}

	private void sendBytes(Session sess, byte[] data, String fileName, String mode) throws IOException
	{
		OutputStream os = sess.getStdin();
		InputStream is = new BufferedInputStream(sess.getStdout(), 512);

		readResponse(is);

		String cline = "C" + mode + " " + data.length + " " + fileName + "\n";

		os.write(cline.getBytes("ISO-8859-1"));
		os.flush();

		readResponse(is);

		os.write(data, 0, data.length);
		os.write(0);
		os.flush();

		readResponse(is);

		os.write("E\n".getBytes("ISO-8859-1"));
		os.flush();
	}

	private void sendFiles(Session sess, String[] files, String[] remoteFiles, String mode) throws IOException
	{
		byte[] buffer = new byte[8192];

		OutputStream os = new BufferedOutputStream(sess.getStdin(), 40000);
		InputStream is = new BufferedInputStream(sess.getStdout(), 512);

		readResponse(is);

		for (int i = 0; i < files.length; i++)
		{
			File f = new File(files[i]);
			long remain = f.length();

			String remoteName;

			if ((remoteFiles != null) && (remoteFiles.length > i) && (remoteFiles[i] != null))
				remoteName = remoteFiles[i];
			else
				remoteName = f.getName();

			String cline = "C" + mode + " " + remain + " " + remoteName + "\n";

			os.write(cline.getBytes("ISO-8859-1"));
			os.flush();

			readResponse(is);

			FileInputStream fis = null;

			try
			{
				fis = new FileInputStream(f);

				while (remain > 0)
				{
					int trans;
					if (remain > buffer.length)
						trans = buffer.length;
					else
						trans = (int) remain;

					if (fis.read(buffer, 0, trans) != trans)
						throw new IOException("Cannot read enough from local file " + files[i]);

					os.write(buffer, 0, trans);

					remain -= trans;
				}
			}
			finally
			{
				if (fis != null)
					fis.close();
			}

			os.write(0);
			os.flush();

			readResponse(is);
		}

		os.write("E\n".getBytes("ISO-8859-1"));
		os.flush();
	}

	private void receiveFiles(Session sess, OutputStream[] targets) throws IOException
	{
		byte[] buffer = new byte[8192];

		OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
		InputStream is = new BufferedInputStream(sess.getStdout(), 40000);

		os.write(0x0);
		os.flush();

		for (int i = 0; i < targets.length; i++)
		{
			LenNamePair lnp = null;

			while (true)
			{
				int c = is.read();
				if (c < 0)
					throw new IOException("Remote scp terminated unexpectedly.");

				String line = receiveLine(is);

				if (c == 'T')
				{
					/* Ignore modification times */

					continue;
				}

				if ((c == 1) || (c == 2))
					throw new IOException("Remote SCP error: " + line);

				if (c == 'C')
				{
					lnp = parseCLine(line);
					break;

				}
				throw new IOException("Remote SCP error: " + ((char) c) + line);
			}

			os.write(0x0);
			os.flush();

			long remain = lnp.length;

			while (remain > 0)
			{
				int trans;
				if (remain > buffer.length)
					trans = buffer.length;
				else
					trans = (int) remain;

				int this_time_received = is.read(buffer, 0, trans);

				if (this_time_received < 0)
				{
					throw new IOException("Remote scp terminated connection unexpectedly");
				}

				targets[i].write(buffer, 0, this_time_received);

				remain -= this_time_received;
			}

			readResponse(is);

			os.write(0x0);
			os.flush();
		}
	}

	private void receiveFiles(Session sess, String[] files, String target) throws IOException
	{
		byte[] buffer = new byte[8192];

		OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
		InputStream is = new BufferedInputStream(sess.getStdout(), 40000);

		os.write(0x0);
		os.flush();

		for (int i = 0; i < files.length; i++)
		{
			LenNamePair lnp = null;

			while (true)
			{
				int c = is.read();
				if (c < 0)
					throw new IOException("Remote scp terminated unexpectedly.");

				String line = receiveLine(is);

				if (c == 'T')
				{
					/* Ignore modification times */

					continue;
				}

				if ((c == 1) || (c == 2))
					throw new IOException("Remote SCP error: " + line);

				if (c == 'C')
				{
					lnp = parseCLine(line);
					break;

				}
				throw new IOException("Remote SCP error: " + ((char) c) + line);
			}

			os.write(0x0);
			os.flush();

			File f = new File(target + File.separatorChar + lnp.filename);
			FileOutputStream fop = null;

			try
			{
				fop = new FileOutputStream(f);

				long remain = lnp.length;

				while (remain > 0)
				{
					int trans;
					if (remain > buffer.length)
						trans = buffer.length;
					else
						trans = (int) remain;

					int this_time_received = is.read(buffer, 0, trans);

					if (this_time_received < 0)
					{
						throw new IOException("Remote scp terminated connection unexpectedly");
					}

					fop.write(buffer, 0, this_time_received);

					remain -= this_time_received;
				}
			}
			finally
			{
				if (fop != null)
					fop.close();
			}

			readResponse(is);

			os.write(0x0);
			os.flush();
		}
	}

	/**
	 * Copy a local file to a remote directory, uses mode 0600 when creating the
	 * file on the remote side.
	 * 
	 * @param localFile
	 *            Path and name of local file.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the
	 *            default directory.
	 * 
	 * @throws IOException
	 */
	public void put(String localFile, String remoteTargetDirectory) throws IOException
	{
		put(new String[] { localFile }, remoteTargetDirectory, "0600");
	}

	/**
	 * Copy a set of local files to a remote directory, uses mode 0600 when
	 * creating files on the remote side.
	 * 
	 * @param localFiles
	 *            Paths and names of local file names.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the
	 *            default directory.
	 * 
	 * @throws IOException
	 */

	public void put(String[] localFiles, String remoteTargetDirectory) throws IOException
	{
		put(localFiles, remoteTargetDirectory, "0600");
	}

	/**
	 * Copy a local file to a remote directory, uses the specified mode when
	 * creating the file on the remote side.
	 * 
	 * @param localFile
	 *            Path and name of local file.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the
	 *            default directory.
	 * @param mode
	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
	 * @throws IOException
	 */
	public void put(String localFile, String remoteTargetDirectory, String mode) throws IOException
	{
		put(new String[] { localFile }, remoteTargetDirectory, mode);
	}

	/**
	 * Copy a local file to a remote directory, uses the specified mode and
	 * remote filename when creating the file on the remote side.
	 * 
	 * @param localFile
	 *            Path and name of local file.
	 * @param remoteFileName
	 *            The name of the file which will be created in the remote
	 *            target directory.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the
	 *            default directory.
	 * @param mode
	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
	 * @throws IOException
	 */
	public void put(String localFile, String remoteFileName, String remoteTargetDirectory, String mode)
			throws IOException
	{
		put(new String[] { localFile }, new String[] { remoteFileName }, remoteTargetDirectory, mode);
	}

	/**
	 * Create a remote file and copy the contents of the passed byte array into
	 * it. Uses mode 0600 for creating the remote file.
	 * 
	 * @param data
	 *            the data to be copied into the remote file.
	 * @param remoteFileName
	 *            The name of the file which will be created in the remote
	 *            target directory.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the
	 *            default directory.
	 * @throws IOException
	 */

	public void put(byte[] data, String remoteFileName, String remoteTargetDirectory) throws IOException
	{
		put(data, remoteFileName, remoteTargetDirectory, "0600");
	}

	/**
	 * Create a remote file and copy the contents of the passed byte array into
	 * it. The method use the specified mode when creating the file on the
	 * remote side.
	 * 
	 * @param data
	 *            the data to be copied into the remote file.
	 * @param remoteFileName
	 *            The name of the file which will be created in the remote
	 *            target directory.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the
	 *            default directory.
	 * @param mode
	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
	 * @throws IOException
	 */
	public void put(byte[] data, String remoteFileName, String remoteTargetDirectory, String mode) throws IOException
	{
		Session sess = null;

		if ((remoteFileName == null) || (remoteTargetDirectory == null) || (mode == null))
			throw new IllegalArgumentException("Null argument.");

		if (mode.length() != 4)
			throw new IllegalArgumentException("Invalid mode.");

		for (int i = 0; i < mode.length(); i++)
			if (Character.isDigit(mode.charAt(i)) == false)
				throw new IllegalArgumentException("Invalid mode.");

		remoteTargetDirectory = remoteTargetDirectory.trim();
		remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";

		String cmd = "scp -t -d " + remoteTargetDirectory;

		try
		{
			sess = conn.openSession();
			sess.execCommand(cmd);
			sendBytes(sess, data, remoteFileName, mode);
		}
		catch (IOException e)
		{
			throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
		}
		finally
		{
			if (sess != null)
				sess.close();
		}
	}

	/**
	 * Copy a set of local files to a remote directory, uses the specified mode
	 * when creating the files on the remote side.
	 * 
	 * @param localFiles
	 *            Paths and names of the local files.
	 * @param remoteTargetDirectory
	 *            Remote target directory. Use an empty string to specify the
	 *            default directory.
	 * @param mode
	 *            a four digit string (e.g., 0644, see "man chmod", "man open")
	 * @throws IOException
	 */
	public void put(String[] localFiles, String remoteTargetDirectory, String mode) throws IOException
	{
		put(localFiles, null, remoteTargetDirectory, mode);
	}

	public void put(String[] localFiles, String[] remoteFiles, String remoteTargetDirectory, String mode)
			throws IOException
	{
		Session sess = null;

		/*
		 * remoteFiles may be null, indicating that the local filenames shall be
		 * used
		 */

		if ((localFiles == null) || (remoteTargetDirectory == null) || (mode == null))
			throw new IllegalArgumentException("Null argument.");

		if (mode.length() != 4)
			throw new IllegalArgumentException("Invalid mode.");

		for (int i = 0; i < mode.length(); i++)
			if (Character.isDigit(mode.charAt(i)) == false)
				throw new IllegalArgumentException("Invalid mode.");

		if (localFiles.length == 0)
			return;

		remoteTargetDirectory = remoteTargetDirectory.trim();
		remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";

		String cmd = "scp -t -d " + remoteTargetDirectory;

		for (int i = 0; i < localFiles.length; i++)
		{
			if (localFiles[i] == null)
				throw new IllegalArgumentException("Cannot accept null filename.");
		}

		try
		{
			sess = conn.openSession();
			sess.execCommand(cmd);
			sendFiles(sess, localFiles, remoteFiles, mode);
		}
		catch (IOException e)
		{
			throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
		}
		finally
		{
			if (sess != null)
				sess.close();
		}
	}

	/**
	 * Download a file from the remote server to a local directory.
	 * 
	 * @param remoteFile
	 *            Path and name of the remote file.
	 * @param localTargetDirectory
	 *            Local directory to put the downloaded file.
	 * 
	 * @throws IOException
	 */
	public void get(String remoteFile, String localTargetDirectory) throws IOException
	{
		get(new String[] { remoteFile }, localTargetDirectory);
	}

	/**
	 * Download a file from the remote server and pipe its contents into an
	 * <code>OutputStream</code>. Please note that, to enable flexible usage
	 * of this method, the <code>OutputStream</code> will not be closed nor
	 * flushed.
	 * 
	 * @param remoteFile
	 *            Path and name of the remote file.
	 * @param target
	 *            OutputStream where the contents of the file will be sent to.
	 * @throws IOException
	 */
	public void get(String remoteFile, OutputStream target) throws IOException
	{
		get(new String[] { remoteFile }, new OutputStream[] { target });
	}

	private void get(String remoteFiles[], OutputStream[] targets) throws IOException
	{
		Session sess = null;

		if ((remoteFiles == null) || (targets == null))
			throw new IllegalArgumentException("Null argument.");

		if (remoteFiles.length != targets.length)
			throw new IllegalArgumentException("Length of arguments does not match.");

		if (remoteFiles.length == 0)
			return;

		String cmd = "scp -f";

		for (int i = 0; i < remoteFiles.length; i++)
		{
			if (remoteFiles[i] == null)
				throw new IllegalArgumentException("Cannot accept null filename.");

			String tmp = remoteFiles[i].trim();

			if (tmp.length() == 0)
				throw new IllegalArgumentException("Cannot accept empty filename.");

			cmd += (" " + tmp);
		}

		try
		{
			sess = conn.openSession();
			sess.execCommand(cmd);
			receiveFiles(sess, targets);
		}
		catch (IOException e)
		{
			throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
		}
		finally
		{
			if (sess != null)
				sess.close();
		}
	}

	/**
	 * Download a set of files from the remote server to a local directory.
	 * 
	 * @param remoteFiles
	 *            Paths and names of the remote files.
	 * @param localTargetDirectory
	 *            Local directory to put the downloaded files.
	 * 
	 * @throws IOException
	 */
	public void get(String remoteFiles[], String localTargetDirectory) throws IOException
	{
		Session sess = null;

		if ((remoteFiles == null) || (localTargetDirectory == null))
			throw new IllegalArgumentException("Null argument.");

		if (remoteFiles.length == 0)
			return;

		String cmd = "scp -f";

		for (int i = 0; i < remoteFiles.length; i++)
		{
			if (remoteFiles[i] == null)
				throw new IllegalArgumentException("Cannot accept null filename.");

			String tmp = remoteFiles[i].trim();

			if (tmp.length() == 0)
				throw new IllegalArgumentException("Cannot accept empty filename.");

			cmd += (" " + tmp);
		}

		try
		{
			sess = conn.openSession();
			sess.execCommand(cmd);
			receiveFiles(sess, remoteFiles, localTargetDirectory);
		}
		catch (IOException e)
		{
			throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
		}
		finally
		{
			if (sess != null)
				sess.close();
		}
	}
}
```

Und hier kommt die Klasse, die die Verbindungen aufbaut...


```
public class SSHConnection {
	
	private String host = "";
	private String user = "";
	private String password = "";
	private Connection connection;
	private Session session;
	boolean isAuthenticated = false;
	

	public void initSSH() {
		
		initConnection();
		authenticate();
		createsession();
	}
	
	
	/*
	 * Funktion disconnectFromFTP
	 * 
	 * Diese Funktion beendet die Verbindung zum FTP-Server.
	 */
	public void disconnect(){
		try {
			if(connection != null)
				connection.close();
			System.out.print("INFO: SSH-Connection successfully disconnected.");
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("FEHLER: Probleme beim disconnect");
		}
	}
	
	public void initConnection() {
		try {
			System.out.println("INFO: SSH Starting Connection");
			connection = new Connection(host);
			connection.connect();
			System.out.println("INFO: SSH-Connected");
		} catch (IOException e) {
			System.err.println("FEHLER: SSH-Connection failed");
			e.printStackTrace();
		}
	}
		
	public void authenticate(){
		try {
			connection.authenticateWithPassword(user, password);
			isAuthenticated = true;
			System.out.println("INFO: Authentication successfull");
		} catch (IOException e) {
			System.err.println("FEHLER: SSH-Authentication failed");
			e.printStackTrace();
		}
	}
	
	public void createsession(){
		try {
			System.out.println("INFO: Opening a SSH-Session");
			session = connection.openSession();
			System.out.println("INFO: SSH-Session successfully open");
		} catch (IOException e) {
			System.err.println("FEHLER: SSH-Session failed");
			e.printStackTrace();
		}
	}
	
	
  
    public Connection getConnection() {
        return connection;
    }

 
    public boolean isAuthenticated() {
		return isAuthenticated;
    }

  
    public Session getSession() {
        return session;
    }
```


----------



## HoaX (17. Feb 2010)

Sicher dass die Datei in dem Verzeichnis liegt, auf das der SCPClient zugreift? Mal mit dem absoluten Pfad probiert?
Ansonsten fällt mir grad noch das "Caused by: java.io.IOException: Remote scp terminated unexpectedly." auf. Mal in die Logdatei auf dem Server geschaut?


----------



## Ollek (17. Feb 2010)

Die Datei liegt Dort.

Und in den Logdateien steht auch nix, was interessant sein könnte. :-(
Ich denke es muss mit dem sourcecode was sein.. Allerdings weiß ich nicht, wo und was!


----------



## Geeeee (17. Feb 2010)

Hast du schonmal nach der Stelle mit dem 
	
	
	
	





```
SCPClient scpClient = new SCPClient(sshCon.getConnection());
```
 geschaut, ob du überhaupt mit dem Server verbunden bist?


----------



## Ollek (17. Feb 2010)

bin durch 
	
	
	
	





```
sshCon.initSSH();
```
 verbunden. Dieses gibt er mir auch auf der Konsole aus.


----------



## FArt (17. Feb 2010)

Wenn ich das richtig sehe, ist das eine kommerzielle Library, die nicht mehr supportet wird.

Versuche es mal damit, ist ziemlich einfach: Commons VFS - Commons Virtual File System


----------

