概述
最近需要从sftp上下载文件。我看到网上大部分都不支持多线程下载很多都会卡死,或者是排队下载。我也从网上copy了一份代码,看了一下。发现了原因。
- 卡死原因
因为很多都是一个session对应一个channel,但是没有做并发处理,导致有的session未被关闭。会话一直存在,它可以被垃圾回收,但是要等到下次gc时才会关闭该session。 - 排队下载
这就是对上面进行了并发处理,让其同步创建session,然后session创建channel,并且同步关闭channel和session。这样也就下载完成了。但是他们是同步下载,所以需要排队。 - 一个session个channel
如果是一个session多个channel,那么当所有channel下载完成关闭时,如果当前session没被关闭,那么所有的数据将停留在内存中,有时候你会发现,你结束掉java进程的时候,它所有数据都被flush到文件了。也就是关闭session。
我拿着改进了一下,将其每个下载对应一个session和一个channel。那么意味着每个session->channel是独立存在的,不存在冲突问题。下面是代码,可以参考下:
import com.jcraft.jsch.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.io.*;
import java.util.Properties;
/**
* @author xoxo
* @description sftp支持多线程下载
*/
@Slf4j
@Data
public class SftpClient {
private static String host = "192.168.10.130";
private static String username = "foo";
private static String password = "pass";
protected static String privateKey;// 密钥文件路径
protected static String passphrase;// 密钥口令
private static int port = 2222;
@Data
@AllArgsConstructor
private static class SftpObject{
private Session session;
private ChannelSftp channelSftp;
}
public static SftpObject connect() {
JSch jsch = new JSch();
ChannelSftp sftp = null;
Channel channel = null;
Session session = null;
try {
if (!StringUtils.isEmpty(privateKey)) {
// 使用密钥验证方式,密钥可以使有口令的密钥,也可以是没有口令的密钥
if (!StringUtils.isEmpty(passphrase)) {
jsch.addIdentity(privateKey, passphrase);
} else {
jsch.addIdentity(privateKey);
}
}
session = jsch.getSession(username, host, port);
if (!StringUtils.isEmpty(password)) {
session.setPassword(password);
}
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");// do not verify host
// key
session.setConfig(sshConfig);
// session.setTimeout(timeout);
// session.setServerAliveInterval(92000);
session.connect();
// 参数sftp指明要打开的连接是sftp连接
channel = session.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
} catch (JSchException e) {
log.error("连接【" + host + ":" + port + "】异常", e);
}
return new SftpObject(session,sftp);
}
/**
* 关闭资源
*/
public static void disconnect(SftpObject sftpObject) {
if(sftpObject==null){
return;
}
ChannelSftp channelSftp = sftpObject.getChannelSftp();
Session session = sftpObject.getSession();
if (channelSftp != null) {
if (channelSftp.isConnected()) {
channelSftp.disconnect();
}
}
if (session != null) {
if (session.isConnected()) {
session.disconnect();
}
}
}
/**
* 下载单个文件
*
* @param remoteFileName
* 下载文件名
* @param localPath
* 本地保存目录(以路径符号结束)
* @param localFileName
* 保存文件名
* @return
*/
public static boolean downloadFile(String remotePath, String remoteFileName, String localPath, String localFileName) {
log.info(remotePath + "/" + remoteFileName + "/" + localPath + "/" + localFileName);
SftpObject sftpObject = null;
try {
sftpObject = connect();
ChannelSftp channelSftp = sftpObject.getChannelSftp();
channelSftp.cd(remotePath);
File file = new File(localPath + localFileName);
mkdirs(localPath + localFileName);
channelSftp.get(remoteFileName, new FileOutputStream(file));
return true;
} catch (FileNotFoundException e) {
log.error("不存在文件,Path:" + remotePath + ",file:" + remoteFileName, e);
} catch (SftpException e) {
log.error("下载文件处理异常,Path:" + remotePath + ",file:" + remoteFileName, e);
} finally {
disconnect(sftpObject);
}
return false;
}
/**
* 如果目录不存在就创建目录
*
* @param path
*/
private static void mkdirs(String path) {
File f = new File(path);
String fs = f.getParent();
f = new File(fs);
if (!f.exists()) {
f.mkdirs();
}
}
public static void main(String[] args){
new Thread(()->{
downloadFile("/upload","java.pdf","D:/work/","xoxo1.pdf");
}).start();
new Thread(()->{
downloadFile("/upload","java.pdf","D:/work/","xoxo2.pdf");
}).start();
new Thread(()->{
downloadFile("/upload","java.pdf","D:/work/","xoxo3.pdf");
}).start();
}
}
最后
以上就是包容绿草为你收集整理的java sftp工具类(支持多线程下载)的全部内容,希望文章能够帮你解决java sftp工具类(支持多线程下载)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复