mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 23:30:55 +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
@@ -46,6 +46,27 @@ public class DefaultModeGetter
|
|||||||
else
|
else
|
||||||
throw new IOException("Unsupported file type: " + f);
|
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
|
@Override
|
||||||
public boolean preservesTimes() {
|
public boolean preservesTimes() {
|
||||||
|
|||||||
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)
|
long getLastModifiedTime(File f)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the permissions for {@code f}.
|
* Returns the permissions for {@code f}.
|
||||||
*
|
*
|
||||||
@@ -56,7 +57,44 @@ public interface ModeGetter {
|
|||||||
*/
|
*/
|
||||||
int getPermissions(File f)
|
int getPermissions(File f)
|
||||||
throws IOException;
|
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. */
|
/** @return whether this implementation can provide mtime and atime information. */
|
||||||
boolean preservesTimes();
|
boolean preservesTimes();
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.xfer.scp;
|
package net.schmizz.sshj.xfer.scp;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
|
import net.schmizz.sshj.connection.channel.direct.SessionFactory;
|
||||||
import net.schmizz.sshj.xfer.AbstractFileTransfer;
|
import net.schmizz.sshj.xfer.AbstractFileTransfer;
|
||||||
import net.schmizz.sshj.xfer.FileTransfer;
|
import net.schmizz.sshj.xfer.FileTransfer;
|
||||||
|
import net.schmizz.sshj.xfer.LocalFile;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class SCPFileTransfer
|
public class SCPFileTransfer
|
||||||
extends AbstractFileTransfer
|
extends AbstractFileTransfer
|
||||||
@@ -51,4 +52,8 @@ public class SCPFileTransfer
|
|||||||
newSCPUploadClient().copy(localPath, remotePath);
|
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;
|
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.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@@ -29,6 +23,13 @@ import java.io.InputStream;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
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. */
|
/** Support for uploading files over a connected link using SCP. */
|
||||||
public final class SCPUploadClient
|
public final class SCPUploadClient
|
||||||
extends SCPEngine {
|
extends SCPEngine {
|
||||||
@@ -49,6 +50,18 @@ public final class SCPUploadClient
|
|||||||
return super.copy(sourcePath, targetPath);
|
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) {
|
public void setFileFilter(FileFilter fileFilter) {
|
||||||
this.fileFilter = fileFilter;
|
this.fileFilter = fileFilter;
|
||||||
}
|
}
|
||||||
@@ -61,6 +74,13 @@ public final class SCPUploadClient
|
|||||||
process(new File(sourcePath));
|
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)
|
private File[] getChildren(File f)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
File[] files = fileFilter == null ? f.listFiles() : f.listFiles(fileFilter);
|
File[] files = fileFilter == null ? f.listFiles() : f.listFiles(fileFilter);
|
||||||
@@ -92,6 +112,20 @@ public final class SCPUploadClient
|
|||||||
} else
|
} else
|
||||||
throw new IOException(f + " is not a regular file or directory");
|
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)
|
private void sendDirectory(File f)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
@@ -102,6 +136,15 @@ public final class SCPUploadClient
|
|||||||
sendMessage("E");
|
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)
|
private void sendFile(File f)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
preserveTimeIfPossible(f);
|
preserveTimeIfPossible(f);
|
||||||
@@ -115,16 +158,40 @@ public final class SCPUploadClient
|
|||||||
IOUtils.closeQuietly(src);
|
IOUtils.closeQuietly(src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
private void preserveTimeIfPossible(File f)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (modeGetter.preservesTimes())
|
if (modeGetter.preservesTimes())
|
||||||
sendMessage("T" + modeGetter.getLastModifiedTime(f) + " 0 " + modeGetter.getLastAccessTime(f) + " 0");
|
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)
|
private String getPermString(File f)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return Integer.toOctalString(modeGetter.getPermissions(f) & 07777);
|
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