项目中,需要使用ftp服务器上传下载文件,之前做过sftp的文件上传下载,以为是一个东西,迅速的把之前的工具类拿过来使用,发现文件为空,特此记录一下二者的区别和工具类。
SFTP(Secure File Transfer Protocol)和FTP(File Transfer Protocol)是两种用于文件传输的协议,它们之间有几个主要区别:
-
安全性:SFTP是基于SSH(Secure Shell)的协议,通过加密传输数据,提供更高的安全性。而FTP在传输过程中不加密数据,安全性较低。
-
端口:SFTP使用SSH协议的默认端口22进行通信,而FTP使用默认端口21。由于SFTP使用SSH协议,它可以通过SSH隧道进行访问,绕过防火墙限制。
-
支持的功能:SFTP支持文件和目录的列表、上传、下载、删除等基本操作,还支持文件和目录的权限设置。而FTP支持更多的功能,如文件重命名、目录创建和删除、文件夹递归传输等。
-
连接方式:SFTP建立在SSH连接之上,需要使用SSH服务器的用户名和密码进行身份验证。而FTP可以使用用户名和密码进行身份验证,也可以使用匿名访问。
总的来说,SFTP提供了更高的安全性和更简单的配置,适用于需要保护数据传输的场景。而FTP则更加灵活,支持更多的功能,适用于需要更多文件传输选项的场景。
相关工具类:
pom.xml中分别引入
<!-- sftp --> <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.55</version> </dependency> <!-- ftp --> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.6</version> </dependency>
sftpUtils:
@Slf4j
public class SftpUtils {
private ChannelSftp sftp;
private Session session;
/**
* FTP 登录用户名
*/
private String username;
/**
* FTP 登录密码
*/
private String password;
/**
* 私钥
*/
private String privateKey;
/**
* FTP 服务器地址IP地址
*/
private String host;
/**
* FTP 端口
*/
private int port;
/**
* 构造基于密码认证的sftp对象
*
* @param username
* @param password
* @param host
* @param port
*/
public SftpUtils(String username, String password, String host, int port) {
this.username = username;
this.password = password;
this.host = host;
this.port = port;
}
/**
* 构造基于秘钥认证的sftp对象
*
* @param username
* @param host
* @param port
* @param privateKey
*/
public SftpUtils(String username, String host, int port, String privateKey) {
this.username = username;
this.host = host;
this.port = port;
this.privateKey = privateKey;
}
public SftpUtils() {
}
/**
* 连接sftp服务器
*
* @throws Exception
*/
public void login() {
try {
JSch jsch = new JSch();
if (privateKey != null) {
// 设置私钥
jsch.addIdentity(privateKey);
log.info("sftp connect,path of private key file:{}", privateKey);
}
log.debug("sftp connect by host:{} username:{}", host, username);
session = jsch.getSession(username, host, port);
log.info("Session is build");
if (password != null) {
session.setPassword(password);
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
log.info("Session is connected");
Channel channel = session.openChannel("sftp");
channel.connect();
log.info("channel is connected");
sftp = (ChannelSftp) channel;
log.info(String.format("sftp server host:[%s] port:[%s] is connect successfull", host, port));
} catch (JSchException e) {
log.error("Cannot connect to specified sftp server : {}:{} Exception message is: {}", host, port, e.getMessage());
}
}
/**
* 关闭连接 server
*/
public void logout() {
if (sftp != null) {
if (sftp.isConnected()) {
sftp.disconnect();
log.info("sftp is closed already");
}
}
if (session != null) {
if (session.isConnected()) {
session.disconnect();
log.info("sshSession is closed already");
}
}
}
/**
* 将输入流的数据上传到sftp作为文件
*
* @param directory 上传到该目录
* @param sftpFileName sftp端文件名
* @param input 输入流
* @throws SftpException
* @throws Exception
*/
public void upload(String directory, String sftpFileName, InputStream input) throws SftpException {
try {
// 目录不存在则创建目录
createDir(directory);
sftp.cd(directory);
sftp.put(input, sftpFileName);
} catch (SftpException e) {
log.warn("directory is not exist");
sftp.mkdir(directory);
sftp.cd(directory);
sftp.put(input, sftpFileName);
} finally {
try {
if (input != null) {
input.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
log.info("file:{} is upload successful", sftpFileName);
}
/**
* 创建一个文件目录
*/
public void createDir(String createpath) {
try {
if (isDirExist(createpath)) {
return;
}
String[] pathArry = createpath.split("/");
StringBuilder filePath = new StringBuilder("/");
for (String path : pathArry) {
if (path.equals("")) {
continue;
}
filePath.append(path).append("/");
if (isDirExist(filePath.toString())) {
} else {
// 建立目录
sftp.mkdir(filePath.toString());
}
}
} catch (SftpException e) {
log.error("创建文件失败");
}
}
/**
* 判断目录是否存在
*/
public boolean isDirExist(String directory) {
boolean isDirExistFlag = false;
try {
SftpATTRS sftpATTRS = sftp.lstat(directory);
isDirExistFlag = true;
return sftpATTRS.isDir();
} catch (Exception e) {
if (e.getMessage().toLowerCase().equals("no such file")) {
isDirExistFlag = false;
}
}
return isDirExistFlag;
}
/**
* 上传单个文件
*
* @param directory 上传到sftp目录
* @param uploadFile 要上传的文件,包括路径
* @throws FileNotFoundException
* @throws SftpException
* @throws Exception
*/
public void upload(String directory, String uploadFile) throws FileNotFoundException, SftpException {
File file = new File(uploadFile);
upload(directory, file.getName(), new FileInputStream(file));
}
/**
* 将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 SftpException {
upload(directory, sftpFileName, new ByteArrayInputStream(byteArr));
}
/**
* 将字符串按照指定的字符编码上传到sftp
*
* @param directory 上传到sftp目录
* @param sftpFileName 文件在sftp端的命名
* @param dataStr 待上传的数据
* @param charsetName sftp上的文件,按该字符编码保存
* @throws UnsupportedEncodingException
* @throws SftpException
* @throws Exception
*/
public void upload(String directory, String sftpFileName, String dataStr, String charsetName) throws UnsupportedEncodingException, SftpException {
upload(directory, sftpFileName, new ByteArrayInputStream(dataStr.getBytes(charsetName)));
}
/**
* 下载文件
*
* @param directory 下载目录
* @param downloadFile 下载的文件
* @param outputStream 输出流
* @throws SftpException
* @throws FileNotFoundException
* @throws Exception
*/
public void download(String directory, String downloadFile, ByteArrayOutputStream outputStream) throws SftpException, FileNotFoundException {
if (directory != null && !"".equals(directory)) {
sftp.cd(directory);
}
sftp.get(downloadFile, outputStream);
log.info("file:{} is download successful", downloadFile);
}
/**
* 下载文件
* @param directory 下载目录
* @param downloadFile 下载的文件名
* @return 字节数组
* @throws SftpException
* @throws IOException
* @throws Exception
*/
public byte[] download(String directory, String downloadFile) throws SftpException, IOException {
if (directory != null && !"".equals(directory)) {
sftp.cd(directory);
}
InputStream is = sftp.get(downloadFile);
byte[] fileData = IOUtils.toByteArray(is);
log.info("file:{} is download successful", downloadFile);
return fileData;
}
/**
* 删除文件
*
* @param directory 要删除文件所在目录
* @param deleteFile 要删除的文件
* @throws SftpException
* @throws Exception
*/
public void delete(String directory, String deleteFile) throws SftpException {
sftp.cd(directory);
sftp.rm(deleteFile);
}
/**
* 列出目录下的文件
*
* @param directory 要列出的目录
* @return
* @throws SftpException
*/
public Vector<?> listFiles(String directory) throws SftpException {
return sftp.ls(directory);
}
}
FTP:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
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.io.OutputStream;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;
/**
* FTP工具类
*/
public class FtpUtil {
private static Logger log = Logger.getLogger(FtpUtil.class);
private FTPClient ftp;
public FtpUtil() {
ftp = new FTPClient();
ftp.setControlEncoding("UTF-8"); //解决上传文件时文件名乱码
}
public FtpUtil(String controlEncoding) {
ftp = new FTPClient();
ftp.setControlEncoding(controlEncoding); //解决上传文件时文件名乱码
}
public void setTimeOut(int defaultTimeoutSecond, int connectTimeoutSecond, int dataTimeoutSecond){
try {
ftp.setDefaultTimeout(defaultTimeoutSecond * 1000);
ftp.setSoTimeout(connectTimeoutSecond * 1000);
ftp.setDataTimeout(dataTimeoutSecond * 1000);
} catch (SocketException e) {
log.error("setTimeout Exception:", e);
}
}
public FTPClient getFTPClient(){
return ftp;
}
public void setControlEncoding(String charset){
ftp.setControlEncoding(charset);
}
public void setFileType(int fileType) throws IOException {
ftp.setFileType(fileType);
}
/**
* Connect to FTP server.
*
* @param host
* FTP server address or name
* @param port
* FTP server port
* @param user
* user name
* @param password
* user password
* @throws IOException
* on I/O errors
*/
public FTPClient connect(String host, int port, String user, String password) throws IOException {
// Connect to server.
try {
ftp.connect(host, port);
} catch (UnknownHostException ex) {
throw new IOException("Can't find FTP server '" + host + "'");
}
// Check rsponse after connection attempt.
int reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
disconnect();
throw new IOException("Can't connect to server '" + host + "'");
}
if ("".equals(user)) {
user = "anonymous";
}
// Login.
if (!ftp.login(user, password)) {
disconnect();
throw new IOException("Can't login to server '" + host + "'");
}
// Set data transfer mode.
ftp.setFileType(FTP.BINARY_FILE_TYPE);
//ftp.setFileType(FTP.ASCII_FILE_TYPE);
// Use passive mode to pass firewalls.
ftp.enterLocalPassiveMode();
return ftp;
}
/**
* Test connection to ftp server
*
* @return true, if connected
*/
public boolean isConnected() {
return ftp.isConnected();
}
/**
* Disconnect from the FTP server
*
* @throws IOException
* on I/O errors
*/
public void disconnect() throws IOException {
if (ftp.isConnected()) {
try {
ftp.logout();
ftp.disconnect();
} catch (IOException ex) {
}
}
}
/**
* Get file from ftp server into given output stream
*
* @param ftpFileName
* file name on ftp server
* @param out
* OutputStream
* @throws IOException
*/
public void retrieveFile(String ftpFileName, OutputStream out) throws IOException {
try {
// Get file info.
FTPFile[] fileInfoArray = ftp.listFiles(ftpFileName);
if (fileInfoArray == null || fileInfoArray.length == 0) {
throw new FileNotFoundException("File '" + ftpFileName + "' was not found on FTP server.");
}
// Check file size.
FTPFile fileInfo = fileInfoArray[0];
long size = fileInfo.getSize();
if (size > Integer.MAX_VALUE) {
throw new IOException("File '" + ftpFileName + "' is too large.");
}
// Download file.
if (!ftp.retrieveFile(ftpFileName, out)) {
throw new IOException("Error loading file '" + ftpFileName + "' from FTP server. Check FTP permissions and path.");
}
out.flush();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException ex) {
}
}
}
}
/**
* Put file on ftp server from given input stream
*
* @param ftpFileName
* file name on ftp server
* @param in
* InputStream
* @throws IOException
*/
public void storeFile(String ftpFileName, InputStream in) throws IOException {
try {
if (!ftp.storeFile(ftpFileName, in)) {
throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");
}
} finally {
try {
in.close();
} catch (IOException ex) {
}
}
}
/**
* 修改名称
* @param from
* @param to
* @throws IOException
*/
public boolean rename(String from, String to) throws IOException {
return ftp.rename(from, to);
}
/**
* Delete the file from the FTP server.
*
* @param ftpFileName
* server file name (with absolute path)
* @throws IOException
* on I/O errors
*/
public void deleteFile(String ftpFileName) throws IOException {
if (!ftp.deleteFile(ftpFileName)) {
throw new IOException("Can't remove file '" + ftpFileName + "' from FTP server.");
}
}
/**
* Upload the file to the FTP server.
*
* @param ftpFileName
* server file name (with absolute path)
* @param localFile
* local file to upload
* @throws IOException
* on I/O errors
*/
public void upload(String ftpFileName, File localFile) throws IOException {
// File check.
if (!localFile.exists()) {
throw new IOException("Can't upload '" + localFile.getAbsolutePath() + "'. This file doesn't exist.");
}
// Upload.
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(localFile));
if (!ftp.storeFile(ftpFileName, in)) {
throw new IOException("Can't upload file '" + ftpFileName + "' to FTP server. Check FTP permissions and path.");
}
} finally {
try {
in.close();
} catch (IOException ex) {
}
}
}
/**
* 上传目录(会覆盖)
* @param remotePath 远程目录 /home/test/a
* @param localPath 本地目录 D:/test/a
* @throws IOException
*/
public void uploadDir(String remotePath, String localPath) throws IOException {
File file = new File(localPath);
if (file.exists()) {
if(!ftp.changeWorkingDirectory(remotePath)){
ftp.makeDirectory(remotePath); //创建成功返回true,失败(已存在)返回false
ftp.changeWorkingDirectory(remotePath); //切换成返回true,失败(不存在)返回false
}
File[] files = file.listFiles();
for (File f : files) {
if (f.isDirectory() && !f.getName().equals(".") && !f.getName().equals("..")) {
uploadDir(remotePath + "/" + f.getName(), f.getPath());
} else if (f.isFile()) {
upload(remotePath + "/" + f.getName(), f);
}
}
}
}
/**
* Download the file from the FTP server.
*
* @param ftpFileName
* server file name (with absolute path)
* @param localFile
* local file to download into
* @throws IOException
* on I/O errors
*/
public void download(String ftpFileName, File localFile) throws IOException {
// Download.
OutputStream out = null;
try {
// Get file info.
FTPFile[] fileInfoArray = ftp.listFiles(ftpFileName);
if (fileInfoArray == null || fileInfoArray.length == 0) {
throw new FileNotFoundException("File " + ftpFileName + " was not found on FTP server.");
}
// Check file size.
FTPFile fileInfo = fileInfoArray[0];
long size = fileInfo.getSize();
if (size > Integer.MAX_VALUE) {
throw new IOException("File " + ftpFileName + " is too large.");
}
// Download file.
out = new BufferedOutputStream(new FileOutputStream(localFile));
if (!ftp.retrieveFile(ftpFileName, out)) {
throw new IOException("Error loading file " + ftpFileName + " from FTP server. Check FTP permissions and path.");
}
out.flush();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException ex) {
}
}
}
}
/**
* 下载目录(会覆盖)
* @param remotePath 远程目录 /home/test/a
* @param localPath 本地目录 D:/test/a
* @return
* @throws IOException
*/
public void downloadDir(String remotePath, String localPath) throws IOException {
File file = new File(localPath);
if(!file.exists()){
file.mkdirs();
}
FTPFile[] ftpFiles = ftp.listFiles(remotePath);
for (int i = 0; ftpFiles!=null && i<ftpFiles.length; i++) {
FTPFile ftpFile = ftpFiles[i];
if (ftpFile.isDirectory() && !ftpFile.getName().equals(".") && !ftpFile.getName().equals("..")) {
downloadDir(remotePath + "/" + ftpFile.getName(), localPath + "/" + ftpFile.getName());
} else {
download(remotePath + "/" + ftpFile.getName(), new File(localPath + "/" + ftpFile.getName()));
}
}
}
/**
* List the file name in the given FTP directory.
*
* @param filePath
* absolute path on the server
* @return files relative names list
* @throws IOException
* on I/O errors
*/
public List<String> listFileNames(String filePath) throws IOException {
List<String> fileList = new ArrayList<String>();
FTPFile[] ftpFiles = ftp.listFiles(filePath);
for (int i = 0; ftpFiles!=null && i<ftpFiles.length; i++) {
FTPFile ftpFile = ftpFiles[i];
if (ftpFile.isFile()) {
fileList.add(ftpFile.getName());
}
}
return fileList;
}
/**
* List the files in the given FTP directory.
*
* @param filePath
* directory
* @return list
* @throws IOException
*/
public List<FTPFile> listFiles(String filePath) throws IOException {
List<FTPFile> fileList = new ArrayList<FTPFile>();
FTPFile[] ftpFiles = ftp.listFiles(filePath);
for (int i = 0; ftpFiles!=null && i<ftpFiles.length; i++) {
FTPFile ftpFile = ftpFiles[i];
// FfpFileInfo fi = new FfpFileInfo();
// fi.setName(ftpFile.getName());
// fi.setSize(ftpFile.getSize());
// fi.setTimestamp(ftpFile.getTimestamp());
// fi.setType(ftpFile.isDirectory());
fileList.add(ftpFile);
}
return fileList;
}
/**
* Send an FTP Server site specific command
*
* @param args
* site command arguments
* @throws IOException
* on I/O errors
*/
public void sendSiteCommand(String args) throws IOException {
if (ftp.isConnected()) {
try {
ftp.sendSiteCommand(args);
} catch (IOException ex) {
}
}
}
/**
* Get current directory on ftp server
*
* @return current directory
*/
public String printWorkingDirectory() {
if (!ftp.isConnected()) {
return "";
}
try {
return ftp.printWorkingDirectory();
} catch (IOException e) {
}
return "";
}
/**
* Set working directory on ftp server
*
* @param dir
* new working directory
* @return true, if working directory changed
*/
public boolean changeWorkingDirectory(String dir) {
if (!ftp.isConnected()) {
return false;
}
try {
return ftp.changeWorkingDirectory(dir);
} catch (IOException e) {
}
return false;
}
/**
* Change working directory on ftp server to parent directory
*
* @return true, if working directory changed
*/
public boolean changeToParentDirectory() {
if (!ftp.isConnected()) {
return false;
}
try {
return ftp.changeToParentDirectory();
} catch (IOException e) {
}
return false;
}
/**
* Get parent directory name on ftp server
*
* @return parent directory
*/
public String printParentDirectory() {
if (!ftp.isConnected()) {
return "";
}
String w = printWorkingDirectory();
changeToParentDirectory();
String p = printWorkingDirectory();
changeWorkingDirectory(w);
return p;
}
/**
* 创建目录
* @param pathname
* @throws IOException
*/
public boolean makeDirectory(String pathname) throws IOException {
return ftp.makeDirectory(pathname);
}
public static void main(String[] args) throws Exception {
FtpUtil ftpUtil = new FtpUtil("UTF-8");
ftpUtil.connect("1.2.3.4", 21, "testuser", "testuser");
//ftpUtil.setTimeOut(60, 60, 60);
ftpUtil.upload("/home/testuser/文件1.txt", new File("E:/image/FTPClient/FTPClient测试/文件1.txt"));
ftpUtil.download("/home/testuser/文件1.txt", new File("E:/image/FTPClient/FTPClient测试/文件1.txt"));
ftpUtil.uploadDir("/home/testuser/FTPClient测试", "E:/image/FTPClient/FTPClient测试");
ftpUtil.downloadDir("/home/testuser/FTPClient测试", "E:/image/FTPClient/FTPClient测试");
ByteArrayOutputStream bos = new ByteArrayOutputStream(); //自动增长
ftpUtil.retrieveFile("/home/testuser/文件1.txt", bos);
System.out.println(bos.size());
String contentStr = new String(bos.toByteArray(),"GBK");
System.out.println(contentStr);
ftpUtil.disconnect();
}
}