Please provide the code changes or file diffs you would like me to summarize.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
package com.sshmanager.service;
|
||||
|
||||
package com.sshmanager.service;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.Session;
|
||||
@@ -8,12 +8,12 @@ import com.jcraft.jsch.SftpException;
|
||||
import com.jcraft.jsch.SftpProgressMonitor;
|
||||
import com.sshmanager.entity.Connection;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
@@ -21,86 +21,86 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@Service
|
||||
public class SftpService {
|
||||
|
||||
private ExecutorService executorService = Executors.newFixedThreadPool(2);
|
||||
|
||||
public void setExecutorService(ExecutorService executorService) {
|
||||
this.executorService = executorService;
|
||||
}
|
||||
|
||||
|
||||
@Service
|
||||
public class SftpService {
|
||||
|
||||
private ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
public void setExecutorService(ExecutorService executorService) {
|
||||
this.executorService = executorService;
|
||||
}
|
||||
|
||||
public SftpSession connect(Connection conn, String password, String privateKey, String passphrase)
|
||||
throws Exception {
|
||||
JSch jsch = new JSch();
|
||||
|
||||
if (conn.getAuthType() == Connection.AuthType.PRIVATE_KEY && privateKey != null && !privateKey.isEmpty()) {
|
||||
byte[] keyBytes = privateKey.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] passphraseBytes = (passphrase != null && !passphrase.isEmpty())
|
||||
? passphrase.getBytes(StandardCharsets.UTF_8) : null;
|
||||
jsch.addIdentity("key", keyBytes, null, passphraseBytes);
|
||||
}
|
||||
|
||||
Session session = jsch.getSession(conn.getUsername(), conn.getHost(), conn.getPort());
|
||||
session.setConfig("StrictHostKeyChecking", "no");
|
||||
// Use only DH-based kex to avoid "Algorithm ECDH not available" on Java 8 / minimal JRE
|
||||
session.setConfig("kex", "diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1");
|
||||
if (conn.getAuthType() == Connection.AuthType.PASSWORD) {
|
||||
if (password == null || password.isEmpty()) {
|
||||
throw new IllegalArgumentException("Password is required for password authentication");
|
||||
}
|
||||
session.setConfig("PreferredAuthentications", "password");
|
||||
session.setPassword(password);
|
||||
} else {
|
||||
session.setConfig("PreferredAuthentications", "publickey");
|
||||
}
|
||||
session.connect(10000);
|
||||
|
||||
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
|
||||
channel.connect(5000);
|
||||
|
||||
return new SftpSession(session, channel);
|
||||
}
|
||||
|
||||
public static class SftpSession {
|
||||
private final Session session;
|
||||
private final ChannelSftp channel;
|
||||
|
||||
public SftpSession(Session session, ChannelSftp channel) {
|
||||
this.session = session;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public ChannelSftp getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (channel != null && channel.isConnected()) {
|
||||
channel.disconnect();
|
||||
}
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return channel != null && channel.isConnected();
|
||||
}
|
||||
}
|
||||
|
||||
throws Exception {
|
||||
JSch jsch = new JSch();
|
||||
|
||||
if (conn.getAuthType() == Connection.AuthType.PRIVATE_KEY && privateKey != null && !privateKey.isEmpty()) {
|
||||
byte[] keyBytes = privateKey.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] passphraseBytes = (passphrase != null && !passphrase.isEmpty())
|
||||
? passphrase.getBytes(StandardCharsets.UTF_8) : null;
|
||||
jsch.addIdentity("key", keyBytes, null, passphraseBytes);
|
||||
}
|
||||
|
||||
Session session = jsch.getSession(conn.getUsername(), conn.getHost(), conn.getPort());
|
||||
session.setConfig("StrictHostKeyChecking", "no");
|
||||
// Use only DH-based kex to avoid "Algorithm ECDH not available" on Java 8 / minimal JRE
|
||||
session.setConfig("kex", "diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1");
|
||||
if (conn.getAuthType() == Connection.AuthType.PASSWORD) {
|
||||
if (password == null || password.isEmpty()) {
|
||||
throw new IllegalArgumentException("Password is required for password authentication");
|
||||
}
|
||||
session.setConfig("PreferredAuthentications", "password");
|
||||
session.setPassword(password);
|
||||
} else {
|
||||
session.setConfig("PreferredAuthentications", "publickey");
|
||||
}
|
||||
session.connect(10000);
|
||||
|
||||
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
|
||||
channel.connect(5000);
|
||||
|
||||
return new SftpSession(session, channel);
|
||||
}
|
||||
|
||||
public static class SftpSession {
|
||||
private final Session session;
|
||||
private final ChannelSftp channel;
|
||||
|
||||
public SftpSession(Session session, ChannelSftp channel) {
|
||||
this.session = session;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public ChannelSftp getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (channel != null && channel.isConnected()) {
|
||||
channel.disconnect();
|
||||
}
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return channel != null && channel.isConnected();
|
||||
}
|
||||
}
|
||||
|
||||
public static class FileInfo {
|
||||
public String name;
|
||||
public boolean directory;
|
||||
public long size;
|
||||
public long mtime;
|
||||
|
||||
public FileInfo(String name, boolean directory, long size, long mtime) {
|
||||
this.name = name;
|
||||
this.directory = directory;
|
||||
this.size = size;
|
||||
this.mtime = mtime;
|
||||
|
||||
public FileInfo(String name, boolean directory, long size, long mtime) {
|
||||
this.name = name;
|
||||
this.directory = directory;
|
||||
this.size = size;
|
||||
this.mtime = mtime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,125 +117,126 @@ public class SftpService {
|
||||
|
||||
void onProgress(long transferredBytes, long totalBytes);
|
||||
}
|
||||
|
||||
|
||||
public List<FileInfo> listFiles(SftpSession sftpSession, String path) throws Exception {
|
||||
String listPath = (path == null || path.trim().isEmpty()) ? "." : path.trim();
|
||||
try {
|
||||
Vector<?> entries = sftpSession.getChannel().ls(listPath);
|
||||
List<FileInfo> result = new ArrayList<>();
|
||||
for (Object obj : entries) {
|
||||
ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) obj;
|
||||
String name = entry.getFilename();
|
||||
if (".".equals(name) || "..".equals(name)) continue;
|
||||
result.add(new FileInfo(
|
||||
name,
|
||||
entry.getAttrs().isDir(),
|
||||
entry.getAttrs().getSize(),
|
||||
entry.getAttrs().getMTime() * 1000L
|
||||
));
|
||||
}
|
||||
return result;
|
||||
} catch (SftpException e) {
|
||||
String msg = formatSftpExceptionMessage(e, listPath, "list");
|
||||
throw new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a user-visible message from JSch SftpException (getMessage() is often null).
|
||||
*/
|
||||
public static String formatSftpExceptionMessage(SftpException e, String path, String operation) {
|
||||
int id = e.id;
|
||||
String serverMsg = e.getMessage();
|
||||
String reason = sftpErrorCodeToMessage(id);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(reason);
|
||||
if (path != null && !path.isEmpty()) {
|
||||
sb.append(": ").append(path);
|
||||
}
|
||||
if (serverMsg != null && !serverMsg.trim().isEmpty()) {
|
||||
sb.append(" (").append(serverMsg).append(")");
|
||||
} else {
|
||||
sb.append(" [SFTP status ").append(id).append("]");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String sftpErrorCodeToMessage(int id) {
|
||||
switch (id) {
|
||||
case 2: return "No such file or directory";
|
||||
case 3: return "Permission denied";
|
||||
case 4: return "Operation failed";
|
||||
case 5: return "Bad message";
|
||||
case 6: return "No connection";
|
||||
case 7: return "Connection lost";
|
||||
case 8: return "Operation not supported";
|
||||
default: return "SFTP error";
|
||||
}
|
||||
}
|
||||
|
||||
List<FileInfo> result = new ArrayList<>();
|
||||
for (Object obj : entries) {
|
||||
ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) obj;
|
||||
String name = entry.getFilename();
|
||||
if (".".equals(name) || "..".equals(name)) continue;
|
||||
result.add(new FileInfo(
|
||||
name,
|
||||
entry.getAttrs().isDir(),
|
||||
entry.getAttrs().getSize(),
|
||||
entry.getAttrs().getMTime() * 1000L
|
||||
));
|
||||
}
|
||||
return result;
|
||||
} catch (SftpException e) {
|
||||
String msg = formatSftpExceptionMessage(e, listPath, "list");
|
||||
throw new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a user-visible message from JSch SftpException (getMessage() is often null).
|
||||
*/
|
||||
public static String formatSftpExceptionMessage(SftpException e, String path, String operation) {
|
||||
int id = e.id;
|
||||
String serverMsg = e.getMessage();
|
||||
String reason = sftpErrorCodeToMessage(id);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(reason);
|
||||
if (path != null && !path.isEmpty()) {
|
||||
sb.append(": ").append(path);
|
||||
}
|
||||
if (serverMsg != null && !serverMsg.trim().isEmpty()) {
|
||||
sb.append(" (").append(serverMsg).append(")");
|
||||
} else {
|
||||
sb.append(" [SFTP status ").append(id).append("]");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String sftpErrorCodeToMessage(int id) {
|
||||
switch (id) {
|
||||
case 2: return "No such file or directory";
|
||||
case 3: return "Permission denied";
|
||||
case 4: return "Operation failed";
|
||||
case 5: return "Bad message";
|
||||
case 6: return "No connection";
|
||||
case 7: return "Connection lost";
|
||||
case 8: return "Operation not supported";
|
||||
default: return "SFTP error";
|
||||
}
|
||||
}
|
||||
|
||||
public void download(SftpSession sftpSession, String remotePath, OutputStream out) throws Exception {
|
||||
sftpSession.getChannel().get(remotePath, out);
|
||||
}
|
||||
|
||||
public void upload(SftpSession sftpSession, String remotePath, InputStream in) throws Exception {
|
||||
sftpSession.getChannel().put(in, remotePath);
|
||||
}
|
||||
|
||||
public void upload(SftpSession sftpSession, String remotePath, InputStream in, TransferProgressListener progressListener) throws Exception {
|
||||
sftpSession.getChannel().put(in, remotePath, new SftpProgressMonitor() {
|
||||
@Override
|
||||
public void init(int op, String src, String dest, long max) {
|
||||
if (progressListener != null) {
|
||||
progressListener.onStart(max);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean count(long count) {
|
||||
if (progressListener != null) {
|
||||
progressListener.onProgress(count, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
// Progress listener will be notified by controller
|
||||
}
|
||||
}, ChannelSftp.OVERWRITE);
|
||||
public void upload(SftpSession sftpSession, String remotePath, InputStream in) throws Exception {
|
||||
sftpSession.getChannel().put(in, remotePath);
|
||||
}
|
||||
|
||||
public void delete(SftpSession sftpSession, String remotePath, boolean isDir) throws Exception {
|
||||
if (isDir) {
|
||||
sftpSession.getChannel().rmdir(remotePath);
|
||||
} else {
|
||||
sftpSession.getChannel().rm(remotePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void mkdir(SftpSession sftpSession, String remotePath) throws Exception {
|
||||
sftpSession.getChannel().mkdir(remotePath);
|
||||
}
|
||||
|
||||
public void rename(SftpSession sftpSession, String oldPath, String newPath) throws Exception {
|
||||
sftpSession.getChannel().rename(oldPath, newPath);
|
||||
}
|
||||
|
||||
public String pwd(SftpSession sftpSession) throws Exception {
|
||||
return sftpSession.getChannel().pwd();
|
||||
}
|
||||
|
||||
public void cd(SftpSession sftpSession, String path) throws Exception {
|
||||
sftpSession.getChannel().cd(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer a single file from source session to target session (streaming, no full file in memory).
|
||||
* Fails if sourcePath is a directory.
|
||||
*/
|
||||
|
||||
public void upload(SftpSession sftpSession, String remotePath, InputStream in, TransferProgressListener progressListener) throws Exception {
|
||||
sftpSession.getChannel().put(in, remotePath, new SftpProgressMonitor() {
|
||||
@Override
|
||||
public void init(int op, String src, String dest, long max) {
|
||||
if (progressListener != null) {
|
||||
progressListener.onStart(max);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean count(long count) {
|
||||
if (progressListener != null) {
|
||||
progressListener.onProgress(count, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
// Progress listener will be notified by controller
|
||||
}
|
||||
}, ChannelSftp.OVERWRITE);
|
||||
}
|
||||
|
||||
public void delete(SftpSession sftpSession, String remotePath, boolean isDir) throws Exception {
|
||||
if (isDir) {
|
||||
sftpSession.getChannel().rmdir(remotePath);
|
||||
} else {
|
||||
sftpSession.getChannel().rm(remotePath);
|
||||
}
|
||||
}
|
||||
|
||||
public void mkdir(SftpSession sftpSession, String remotePath) throws Exception {
|
||||
sftpSession.getChannel().mkdir(remotePath);
|
||||
}
|
||||
|
||||
public void rename(SftpSession sftpSession, String oldPath, String newPath) throws Exception {
|
||||
sftpSession.getChannel().rename(oldPath, newPath);
|
||||
}
|
||||
|
||||
public String pwd(SftpSession sftpSession) throws Exception {
|
||||
return sftpSession.getChannel().pwd();
|
||||
}
|
||||
|
||||
public void cd(SftpSession sftpSession, String path) throws Exception {
|
||||
sftpSession.getChannel().cd(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer a file or directory from source session to target session.
|
||||
* If sourcePath is a directory, transfers recursively (the source directory itself is
|
||||
* recreated under targetPath — "plan A" / scp -r behaviour).
|
||||
*/
|
||||
public void transferRemote(SftpSession source, String sourcePath, SftpSession target, String targetPath)
|
||||
throws Exception {
|
||||
throws Exception {
|
||||
transferRemote(source, sourcePath, target, targetPath, null);
|
||||
}
|
||||
|
||||
@@ -246,8 +247,161 @@ public class SftpService {
|
||||
TransferProgressListener progressListener) throws Exception {
|
||||
SftpATTRS attrs = source.getChannel().stat(sourcePath);
|
||||
if (attrs.isDir()) {
|
||||
throw new IllegalArgumentException("Source path is a directory; only single file transfer is supported");
|
||||
final long totalBytes = calculateTotalSize(source, sourcePath);
|
||||
if (progressListener != null) {
|
||||
progressListener.onStart(totalBytes);
|
||||
}
|
||||
AtomicLong transferred = new AtomicLong(0);
|
||||
transferDirectoryRemote(source, sourcePath, target, targetPath, totalBytes, transferred, progressListener);
|
||||
} else {
|
||||
String finalTargetPath = targetPath;
|
||||
try {
|
||||
SftpATTRS targetAttrs = target.getChannel().stat(targetPath);
|
||||
if (targetAttrs.isDir()) {
|
||||
String fileName = sourcePath.contains("/") ? sourcePath.substring(sourcePath.lastIndexOf('/') + 1) : sourcePath;
|
||||
finalTargetPath = targetPath.endsWith("/") ? targetPath + fileName : targetPath + "/" + fileName;
|
||||
}
|
||||
} catch (SftpException e) {
|
||||
if (targetPath.endsWith("/")) {
|
||||
String fileName = sourcePath.contains("/") ? sourcePath.substring(sourcePath.lastIndexOf('/') + 1) : sourcePath;
|
||||
finalTargetPath = targetPath + fileName;
|
||||
}
|
||||
}
|
||||
transferSingleFileRemote(source, sourcePath, target, finalTargetPath, progressListener);
|
||||
}
|
||||
}
|
||||
|
||||
/** Recursively calculate total byte size of all files under a remote path. */
|
||||
private long calculateTotalSize(SftpSession session, String path) throws Exception {
|
||||
SftpATTRS attrs = session.getChannel().stat(path);
|
||||
if (!attrs.isDir()) {
|
||||
return attrs.getSize();
|
||||
}
|
||||
long total = 0;
|
||||
Vector<?> entries = session.getChannel().ls(path);
|
||||
for (Object obj : entries) {
|
||||
ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) obj;
|
||||
String name = entry.getFilename();
|
||||
if (".".equals(name) || "..".equals(name)) continue;
|
||||
String childPath = path.endsWith("/") ? path + name : path + "/" + name;
|
||||
total += calculateTotalSize(session, childPath);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/** Recursively create a directory and its parents on a remote session (mkdir -p). */
|
||||
private void mkdirRecursive(SftpSession session, String path) throws Exception {
|
||||
try {
|
||||
SftpATTRS attrs = session.getChannel().stat(path);
|
||||
if (attrs.isDir()) return;
|
||||
throw new IllegalStateException("Path exists but is not a directory: " + path);
|
||||
} catch (SftpException e) {
|
||||
if (e.id != ChannelSftp.SSH_FX_NO_SUCH_FILE) throw e;
|
||||
}
|
||||
int slash = path.lastIndexOf('/');
|
||||
if (slash > 0) {
|
||||
mkdirRecursive(session, path.substring(0, slash));
|
||||
}
|
||||
try {
|
||||
session.getChannel().mkdir(path);
|
||||
} catch (SftpException e) {
|
||||
// Another thread may have created it concurrently; SSH_FX_FAILURE is returned in that case
|
||||
if (e.id != ChannelSftp.SSH_FX_FAILURE) throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively transfer a directory.
|
||||
* The source directory name is recreated under targetParentPath (plan A / scp -r behaviour).
|
||||
*/
|
||||
private void transferDirectoryRemote(SftpSession source,
|
||||
String sourceDirPath,
|
||||
SftpSession target,
|
||||
String targetParentPath,
|
||||
long totalBytes,
|
||||
AtomicLong transferred,
|
||||
TransferProgressListener progressListener) throws Exception {
|
||||
String dirName = sourceDirPath.contains("/")
|
||||
? sourceDirPath.substring(sourceDirPath.lastIndexOf('/') + 1)
|
||||
: sourceDirPath;
|
||||
String targetDirPath = targetParentPath.endsWith("/")
|
||||
? targetParentPath + dirName
|
||||
: targetParentPath + "/" + dirName;
|
||||
|
||||
mkdirRecursive(target, targetDirPath);
|
||||
|
||||
Vector<?> entries = source.getChannel().ls(sourceDirPath);
|
||||
for (Object obj : entries) {
|
||||
ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) obj;
|
||||
String name = entry.getFilename();
|
||||
if (".".equals(name) || "..".equals(name)) continue;
|
||||
String childSourcePath = sourceDirPath.endsWith("/")
|
||||
? sourceDirPath + name
|
||||
: sourceDirPath + "/" + name;
|
||||
if (entry.getAttrs().isDir()) {
|
||||
transferDirectoryRemote(source, childSourcePath, target, targetDirPath,
|
||||
totalBytes, transferred, progressListener);
|
||||
} else {
|
||||
String childTargetPath = targetDirPath + "/" + name;
|
||||
transferSingleFileAccumulating(source, childSourcePath,
|
||||
target, childTargetPath, totalBytes, transferred, progressListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Stream a single file and accumulate progress into a shared counter (used during directory transfer). */
|
||||
private void transferSingleFileAccumulating(SftpSession source,
|
||||
String sourcePath,
|
||||
SftpSession target,
|
||||
String targetFilePath,
|
||||
long totalBytes,
|
||||
AtomicLong transferred,
|
||||
TransferProgressListener progressListener) throws Exception {
|
||||
final int pipeBufferSize = 65536;
|
||||
PipedOutputStream pos = new PipedOutputStream();
|
||||
PipedInputStream pis = new PipedInputStream(pos, pipeBufferSize);
|
||||
|
||||
Future<?> putFuture = executorService.submit(() -> {
|
||||
try {
|
||||
target.getChannel().put(pis, targetFilePath, new SftpProgressMonitor() {
|
||||
@Override public void init(int op, String src, String dest, long max) {}
|
||||
|
||||
@Override
|
||||
public boolean count(long count) {
|
||||
long current = transferred.addAndGet(count);
|
||||
if (progressListener != null) {
|
||||
progressListener.onProgress(current, totalBytes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public void end() {}
|
||||
}, ChannelSftp.OVERWRITE);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
try { pis.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
});
|
||||
try {
|
||||
source.getChannel().get(sourcePath, pos);
|
||||
} finally {
|
||||
try { pos.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
try {
|
||||
putFuture.get();
|
||||
} finally {
|
||||
try { pis.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
/** Transfer a single file with its own independent progress tracking. */
|
||||
private void transferSingleFileRemote(SftpSession source,
|
||||
String sourcePath,
|
||||
SftpSession target,
|
||||
String targetPath,
|
||||
TransferProgressListener progressListener) throws Exception {
|
||||
SftpATTRS attrs = source.getChannel().stat(sourcePath);
|
||||
final long totalBytes = attrs.getSize();
|
||||
final int pipeBufferSize = 65536;
|
||||
PipedOutputStream pos = new PipedOutputStream();
|
||||
@@ -263,46 +417,36 @@ public class SftpService {
|
||||
target.getChannel().put(pis, targetPath, new SftpProgressMonitor() {
|
||||
@Override
|
||||
public void init(int op, String src, String dest, long max) {
|
||||
if (progressListener != null) {
|
||||
progressListener.onStart(totalBytes);
|
||||
}
|
||||
if (progressListener != null) progressListener.onStart(totalBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean count(long count) {
|
||||
long current = transferredBytes.addAndGet(count);
|
||||
if (progressListener != null) {
|
||||
progressListener.onProgress(current, totalBytes);
|
||||
}
|
||||
if (progressListener != null) progressListener.onProgress(current, totalBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
if (progressListener != null) {
|
||||
progressListener.onProgress(totalBytes, totalBytes);
|
||||
}
|
||||
if (progressListener != null) progressListener.onProgress(totalBytes, totalBytes);
|
||||
}
|
||||
}, ChannelSftp.OVERWRITE);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
try { pis.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
});
|
||||
try {
|
||||
source.getChannel().get(sourcePath, pos);
|
||||
} finally {
|
||||
try {
|
||||
pos.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
try { pos.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
try {
|
||||
putFuture.get();
|
||||
} finally {
|
||||
try {
|
||||
pis.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
try { pis.close(); } catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user