package com.walker.file.ftp;
|
|
import com.jcraft.jsch.Channel;
|
import com.jcraft.jsch.ChannelSftp;
|
import com.jcraft.jsch.JSch;
|
import com.jcraft.jsch.JSchException;
|
import com.jcraft.jsch.Session;
|
import com.jcraft.jsch.SftpException;
|
import com.walker.infrastructure.utils.FileCopyUtils;
|
import com.walker.infrastructure.utils.StringUtils;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
|
import java.io.ByteArrayInputStream;
|
import java.io.File;
|
import java.io.FileInputStream;
|
import java.io.FileNotFoundException;
|
import java.io.FileOutputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.util.Properties;
|
import java.util.Vector;
|
|
/**
|
* FTP连接器,执行上传,下载等操作。<p></p>
|
* 1、该对象为"有状态对象",必须创建多例使用,因为并发环境中每个连接只能操作一个文件。<br>
|
* 2、必须由业务决定执行哪些方法,例如: 打开一个通道后,可以连续执行,切换目录、创建文件夹、上传文件等。
|
* @author 时克英
|
* @date 2023-02-12
|
*/
|
public class FtpConnector {
|
|
protected transient Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
private FtpConfig ftpConfig = null;
|
|
private ChannelSftp sftp;
|
private Session session = null;
|
|
public FtpConnector(FtpConfig ftpConfig){
|
if(ftpConfig == null){
|
throw new IllegalArgumentException("FtpConfig 必须设置!");
|
}
|
this.ftpConfig = ftpConfig;
|
}
|
|
/**
|
* 是否还在连接。
|
* @return
|
*/
|
public boolean isConnected(){
|
if(this.sftp == null){
|
return false;
|
}
|
return this.sftp.isConnected();
|
}
|
|
/**
|
* 将输入流的数据上传到sftp作为文件
|
*
|
* @param directory 上传到该目录
|
* @param sftpFileName sftp端文件名
|
* @param input 输入流
|
* @throws SftpException
|
* @throws Exception
|
*/
|
public void upload(String directory, String sftpFileName, InputStream input) throws FtpUploadException {
|
if(input == null){
|
throw new IllegalArgumentException("InputStream 必须设置!");
|
}
|
try {
|
sftp.cd(directory);
|
} catch (SftpException e) {
|
logger.warn("directory is not exist");
|
try{
|
sftp.mkdir(directory);
|
sftp.cd(directory);
|
} catch (SftpException ex){
|
throw new FtpUploadException(sftpFileName, "ftp目录不存在或无法创建", ex);
|
}
|
}
|
|
try{
|
sftp.put(input, sftpFileName);
|
} catch (SftpException ex){
|
throw new FtpUploadException(sftpFileName, "上传文件出现错误:", ex);
|
}
|
logger.info("{} 上传成功", sftpFileName);
|
}
|
|
/**
|
* 上传单个文件
|
*
|
* @param directory 上传到sftp目录
|
* @param uploadFile 要上传的文件,包括路径
|
* @throws FileNotFoundException
|
* @throws SftpException
|
* @throws Exception
|
*/
|
public void upload(String directory, String uploadFile) throws FtpUploadException {
|
File file = new File(uploadFile);
|
if(!file.isFile()){
|
throw new FtpUploadException(uploadFile, "不能上传文件夹", null);
|
}
|
if(!file.exists()){
|
throw new FtpUploadException(uploadFile, "文件不存在", null);
|
}
|
try {
|
upload(directory, file.getName(), new FileInputStream(file));
|
} catch (FileNotFoundException e) {
|
e.printStackTrace();
|
}
|
}
|
|
/**
|
* 将byte[]上传到sftp,作为文件。注意:从String生成byte[]是,要指定字符集。
|
*
|
* @param directory 上传到sftp目录
|
* @param sftpFileName 文件在sftp端的命名
|
* @param byteArr 要上传的字节数组
|
* @throws SftpException
|
* @throws Exception
|
*/
|
public void upload(String directory, String sftpFileName, byte[] byteArr) throws FtpUploadException {
|
upload(directory, sftpFileName, new ByteArrayInputStream(byteArr));
|
}
|
|
/**
|
* 下载文件
|
*
|
* @param directory 下载目录
|
* @param downloadFile 下载的文件
|
* @param saveFile 存在本地的路径
|
* @throws SftpException
|
* @throws FileNotFoundException
|
* @throws Exception
|
*/
|
public void download(String directory, String downloadFile, String saveFile) throws FtpUploadException {
|
logger.info("下载文件:{}/{}到{}", directory, downloadFile, saveFile);
|
if (StringUtils.isNotEmpty(directory)) {
|
try {
|
sftp.cd(directory);
|
} catch (SftpException e) {
|
throw new FtpUploadException(downloadFile, "下载文件的目录不存在:" + directory, e);
|
}
|
}
|
File file = new File(saveFile);
|
File parentFile = file.getParentFile();
|
if (!parentFile.exists()) {
|
parentFile.mkdirs();
|
}
|
if (isExistSftp(downloadFile)) {
|
try {
|
sftp.get(downloadFile, new FileOutputStream(file));
|
} catch (Exception e) {
|
throw new FtpUploadException(downloadFile, "FTP下载文件错误:" + e.getMessage(), e);
|
}
|
logger.info("file:{} is download successful", downloadFile);
|
} else {
|
throw new FtpUploadException(downloadFile, "ftp不存在该文件:" + downloadFile, null);
|
}
|
}
|
|
// 判断文件是否存在
|
public boolean isExistSftp(String filePach) {
|
try {
|
sftp.lstat(filePach);
|
} catch (SftpException e) {
|
logger.info("isExistSftp()==> file:{} is not found", filePach);
|
return false;
|
}
|
return true;
|
}
|
|
/**
|
* 下载文件
|
*
|
* @param directory 下载目录
|
* @param downloadFile 下载的文件名
|
* @return 字节数组
|
* @throws SftpException
|
* @throws IOException
|
* @throws Exception
|
*/
|
public byte[] download(String directory, String downloadFile) throws FtpUploadException {
|
InputStream is = null;
|
try{
|
if (StringUtils.isNotEmpty(directory)) {
|
sftp.cd(directory);
|
}
|
is = sftp.get(downloadFile);
|
} catch (SftpException ex){
|
throw new FtpUploadException(downloadFile, "ftp返回文件错误", ex);
|
}
|
|
byte[] fileData = new byte[0];
|
try {
|
fileData = FileCopyUtils.copyToByteArray(is);
|
} catch (IOException e) {
|
throw new FtpUploadException(downloadFile, "ftp已下载,但本地获取文件流错误:" + e.getMessage(), e);
|
}
|
logger.info("file:{} is download successful", downloadFile);
|
return fileData;
|
}
|
|
/**
|
* 删除文件
|
*
|
* @param directory 要删除文件所在目录
|
* @param deleteFile 要删除的文件
|
* @throws SftpException
|
* @throws Exception
|
*/
|
public void delete(String directory, String deleteFile) throws FtpUploadException {
|
try {
|
sftp.cd(directory);
|
sftp.rm(deleteFile);
|
} catch (SftpException ex){
|
throw new FtpUploadException(deleteFile, "ftp删除文件异常,目录:" + directory, ex);
|
}
|
}
|
|
public void deleteDir(String directory) throws FtpUploadException {
|
try {
|
sftp.rmdir(directory);
|
} catch (SftpException e) {
|
throw new FtpUploadException(directory, "ftp删除目录异常:" + directory, e);
|
}
|
}
|
|
/**
|
* 列出目录下的文件
|
*
|
* @param directory 要列出的目录
|
* @return
|
* @throws SftpException
|
*/
|
public Vector<?> listFiles(String directory) throws FtpUploadException {
|
try {
|
return sftp.ls(directory);
|
} catch (SftpException e) {
|
throw new FtpUploadException(directory, "ftp列出目录文件异常:" + directory, e);
|
}
|
}
|
|
/**
|
* 创建目录,如果存就不会创建
|
*
|
* @param path 开头必须带/,结尾不能带/,否则后果自负
|
* @throws SftpException
|
* @throws IOException
|
*/
|
public void mkdir(String path) throws FtpUploadException {
|
String pathArr[] = path.split("/");
|
String p = StringUtils.EMPTY_STRING;
|
for (int i = 1; i < pathArr.length; i++) {//0是空字符串,直接跳过
|
p += "/" + pathArr[i];
|
System.out.println(p);
|
try {
|
sftp.cd(p);
|
} catch (SftpException e) {
|
try {
|
sftp.mkdir(p);
|
} catch (SftpException ex) {
|
throw new FtpUploadException(path, "ftp创建目录异常:" + path, ex);
|
}
|
}
|
}
|
}
|
|
/**
|
* Ftp 连接登录
|
* @return
|
* @throws Exception
|
*/
|
public boolean connect() throws Exception{
|
if(sftp != null && sftp.isConnected()){
|
logger.warn("FtpConnector 已经连接,无需重复调用: connect()方法!");
|
return true;
|
}
|
this.sftp = null;
|
|
try{
|
JSch jsch = new JSch();
|
if (StringUtils.isNotEmpty(this.ftpConfig.getPrivateKey())) {
|
jsch.addIdentity(this.ftpConfig.getPrivateKey());// 设置私钥
|
logger.info("sftp connect,path of private key file:{}", this.ftpConfig.getPrivateKey());
|
}
|
logger.info("sftp connect by host:{} username:{}", this.ftpConfig.getIp(), this.ftpConfig.getUserName());
|
session = jsch.getSession(ftpConfig.getUserName(), ftpConfig.getIp(), Integer.parseInt(ftpConfig.getPort()));
|
if (StringUtils.isNotEmpty(this.ftpConfig.getPassword())) {
|
session.setPassword(this.ftpConfig.getPassword());
|
}
|
|
Properties config = new Properties();
|
config.put("StrictHostKeyChecking", "no");
|
session.setConfig(config);
|
session.connect();
|
|
Channel channel = session.openChannel("sftp");
|
channel.connect();
|
sftp = (ChannelSftp) channel;
|
logger.info("FtpConnector is connected");
|
return true;
|
|
} catch (Exception ex){
|
if(ex instanceof JSchException){
|
logger.error("ftp连接异常:" + ex.getMessage(), ex);
|
} else {
|
logger.error("创建 FtpConnector 登录异常:" + ex.getMessage(), ex);
|
}
|
throw ex;
|
} finally {
|
// if (session != null) {
|
// if (session.isConnected()) {
|
// session.disconnect();
|
// logger.info("sshSession is closed already");
|
// }
|
// }
|
}
|
}
|
|
public void logout() {
|
if (sftp != null) {
|
if (sftp.isConnected()) {
|
sftp.disconnect();
|
logger.info("sftp is closed already");
|
}
|
}
|
if (session != null) {
|
if (session.isConnected()) {
|
session.disconnect();
|
logger.info("sshSession is closed already");
|
}
|
}
|
logger.info("FtpConnector 已关闭");
|
}
|
}
|