Can scp a file without it being on the file system

Introduced an abstraction of a file and two concrete implementation of it:
one "in memory" file and a file on the file system.
This commit is contained in:
Cyril Ledru
2011-02-25 23:49:02 +08:00
committed by Shikhar Bhushan
parent c56f9997f4
commit 41ac277023
7 changed files with 307 additions and 9 deletions

View File

@@ -47,6 +47,27 @@ public class DefaultModeGetter
throw new IOException("Unsupported file type: " + f);
}
@Override
public long getLastAccessTime(LocalFile f) {
return System.currentTimeMillis() / 1000;
}
@Override
public long getLastModifiedTime(LocalFile f) {
return f.lastModified() / 1000;
}
@Override
public int getPermissions(LocalFile f)
throws IOException {
if (f.isDirectory())
return 0755;
else if (f.isFile())
return 0644;
else
throw new IOException("Unsupported file type: " + f);
}
@Override
public boolean preservesTimes() {
return true;

View File

@@ -0,0 +1,73 @@
package net.schmizz.sshj.xfer;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
public class FileSystemFile implements LocalFile {
private File file;
private FileFilter fileFilter;
public FileSystemFile(String path) {
this.file = new File(path);
}
public void setFileFilter(FileFilter fileFilter) {
this.fileFilter = fileFilter;
}
@Override
public String getName() {
return file.getName();
}
@Override
public boolean isDirectory() {
return file.isDirectory();
}
@Override
public boolean isFile() {
return file.isFile();
}
@Override
public long length() {
return file.length();
}
@Override
public long lastModified() {
return file.lastModified();
}
@Override
public InputStream stream() throws IOException {
return new FileInputStream(file);
}
@Override
public Iterable<LocalFile> getChildren() throws IOException {
return getChildren(file);
}
private Iterable<LocalFile> getChildren(File f) throws IOException {
Collection<LocalFile> files = new ArrayList<LocalFile>();
File[] childFiles = fileFilter == null ? f.listFiles() : f.listFiles(fileFilter);
if (childFiles == null)
throw new IOException("Error listing files in directory: " + f);
for (File childFile : childFiles) {
FileSystemFile localChild = new FileSystemFile(childFile.getName());
localChild.setFileFilter(fileFilter);
files.add(localChild);
}
return files;
}
}

View File

@@ -0,0 +1,75 @@
package net.schmizz.sshj.xfer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
public class InMemoryFile implements LocalFile {
private static final int EOF = -1;
private static final int DEFAULT_BUFFER_SIZE = 4096;
private String name;
private Long cachedLength;
private InputStream stream;
public InMemoryFile(String filename, ByteArrayInputStream stream) {
this.name = filename;
this.stream = stream;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isDirectory() {
return false;
}
@Override
public boolean isFile() {
return true;
}
@Override
public long length() {
if (cachedLength == null) {
cachedLength = computeLength();
}
return cachedLength;
}
private long computeLength() {
try {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long length = 0;
int readBytes = 0;
while (EOF != (readBytes = stream.read(buffer))) {
length += readBytes;
}
stream.reset();
return length;
} catch (IOException e) {
throw new RuntimeException("Impossible to read in memory file", e);
}
}
@Override
public long lastModified() {
return System.currentTimeMillis() / 1000;
}
@Override
public InputStream stream() {
return stream;
}
@Override
public Iterable<LocalFile> getChildren() {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,19 @@
package net.schmizz.sshj.xfer;
import java.io.IOException;
import java.io.InputStream;
public interface LocalFile {
String getName();
boolean isDirectory();
boolean isFile();
long length();
long lastModified();
InputStream stream() throws IOException;
Iterable<LocalFile> getChildren() throws IOException;
}

View File

@@ -45,6 +45,7 @@ public interface ModeGetter {
long getLastModifiedTime(File f)
throws IOException;
/**
* Returns the permissions for {@code f}.
*
@@ -57,6 +58,43 @@ public interface ModeGetter {
int getPermissions(File f)
throws IOException;
/**
* Returns last access time for {@code f}.
*
* @param f the file
*
* @return time in seconds since Unix epoch
*
* @throws IOException
*/
long getLastAccessTime(LocalFile f)
throws IOException;
/**
* Returns last modified time for {@code f}.
*
* @param f the file
*
* @return time in seconds since Unix epoch
*
* @throws IOException
*/
long getLastModifiedTime(LocalFile f)
throws IOException;
/**
* Returns the permissions for {@code f}.
*
* @param f the file
*
* @return permissions in octal format, e.g. 0644
*
* @throws IOException
*/
int getPermissions(LocalFile f)
throws IOException;
/** @return whether this implementation can provide mtime and atime information. */
boolean preservesTimes();

View File

@@ -15,11 +15,12 @@
*/
package net.schmizz.sshj.xfer.scp;
import java.io.IOException;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.xfer.AbstractFileTransfer;
import net.schmizz.sshj.xfer.FileTransfer;
import java.io.IOException;
import net.schmizz.sshj.xfer.LocalFile;
public class SCPFileTransfer
extends AbstractFileTransfer
@@ -51,4 +52,8 @@ public class SCPFileTransfer
newSCPUploadClient().copy(localPath, remotePath);
}
public void upload(LocalFile localFile, String remotePath)
throws IOException {
newSCPUploadClient().copy(localFile, remotePath);
}
}

View File

@@ -15,12 +15,6 @@
*/
package net.schmizz.sshj.xfer.scp;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.xfer.ModeGetter;
import net.schmizz.sshj.xfer.TransferListener;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
@@ -29,6 +23,13 @@ import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
import net.schmizz.sshj.xfer.LocalFile;
import net.schmizz.sshj.xfer.ModeGetter;
import net.schmizz.sshj.xfer.TransferListener;
/** Support for uploading files over a connected link using SCP. */
public final class SCPUploadClient
extends SCPEngine {
@@ -49,6 +50,18 @@ public final class SCPUploadClient
return super.copy(sourcePath, targetPath);
}
/** Upload a local file from {@code localFile} to {@code targetPath} on the remote host. */
public synchronized int copy(LocalFile sourceFile, String remotePath)
throws IOException {
cleanSlate();
try {
startCopy(sourceFile, remotePath);
} finally {
exit();
}
return exitStatus;
}
public void setFileFilter(FileFilter fileFilter) {
this.fileFilter = fileFilter;
}
@@ -61,6 +74,13 @@ public final class SCPUploadClient
process(new File(sourcePath));
}
protected synchronized void startCopy(LocalFile sourceFile, String targetPath)
throws IOException {
init(targetPath);
check("Start status OK");
process(sourceFile);
}
private File[] getChildren(File f)
throws IOException {
File[] files = fileFilter == null ? f.listFiles() : f.listFiles(fileFilter);
@@ -93,6 +113,20 @@ public final class SCPUploadClient
throw new IOException(f + " is not a regular file or directory");
}
private void process(LocalFile f)
throws IOException {
if (f.isDirectory()) {
listener.startedDir(f.getName());
sendDirectory(f);
listener.finishedDir();
} else if (f.isFile()) {
listener.startedFile(f.getName(), f.length());
sendFile(f);
listener.finishedFile();
} else
throw new IOException(f + " is not a regular file or directory");
}
private void sendDirectory(File f)
throws IOException {
preserveTimeIfPossible(f);
@@ -102,6 +136,15 @@ public final class SCPUploadClient
sendMessage("E");
}
private void sendDirectory(LocalFile f)
throws IOException {
preserveTimeIfPossible(f);
sendMessage("D0" + getPermString(f) + " 0 " + f.getName());
for (LocalFile child : f.getChildren())
process(child);
sendMessage("E");
}
private void sendFile(File f)
throws IOException {
preserveTimeIfPossible(f);
@@ -116,15 +159,39 @@ public final class SCPUploadClient
}
}
private void sendFile(LocalFile f)
throws IOException {
preserveTimeIfPossible(f);
final InputStream src = f.stream();
try {
sendMessage("C0" + getPermString(f) + " " + f.length() + " " + f.getName());
transfer(src, scp.getOutputStream(), scp.getRemoteMaxPacketSize(), f.length());
signal("Transfer done");
check("Remote agrees transfer done");
} finally {
IOUtils.closeQuietly(src);
}
}
private void preserveTimeIfPossible(File f)
throws IOException {
if (modeGetter.preservesTimes())
sendMessage("T" + modeGetter.getLastModifiedTime(f) + " 0 " + modeGetter.getLastAccessTime(f) + " 0");
}
private void preserveTimeIfPossible(LocalFile f)
throws IOException {
if (modeGetter.preservesTimes())
sendMessage("T" + modeGetter.getLastModifiedTime(f) + " 0 " + modeGetter.getLastAccessTime(f) + " 0");
}
private String getPermString(File f)
throws IOException {
return Integer.toOctalString(modeGetter.getPermissions(f) & 07777);
}
private String getPermString(LocalFile f)
throws IOException {
return Integer.toOctalString(modeGetter.getPermissions(f) & 07777);
}
}