mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 15:20:54 +03:00
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:
committed by
Shikhar Bhushan
parent
c56f9997f4
commit
41ac277023
@@ -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;
|
||||
|
||||
73
src/main/java/net/schmizz/sshj/xfer/FileSystemFile.java
Normal file
73
src/main/java/net/schmizz/sshj/xfer/FileSystemFile.java
Normal 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;
|
||||
}
|
||||
}
|
||||
75
src/main/java/net/schmizz/sshj/xfer/InMemoryFile.java
Normal file
75
src/main/java/net/schmizz/sshj/xfer/InMemoryFile.java
Normal 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();
|
||||
}
|
||||
}
|
||||
19
src/main/java/net/schmizz/sshj/xfer/LocalFile.java
Normal file
19
src/main/java/net/schmizz/sshj/xfer/LocalFile.java
Normal 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;
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user