FTP与SFTP的区别
FTP是文件传输协议。在网站上,如果你想把文件和人共享,最便捷的方式莫过于把文件上传到FTP服务器上,其他人通过FTP客户端程序来下载所需要的文件。
FTP进行文件传输需要通过端口进行。一般所需端口为:
- 控制链路—TCP端口21。控制器端。用于发送指令给服务器以及等待服务器响应。
2.数据链路---TCP端口20。数据传输端口。用来建立数据传输通道的。主要用来从客户向服务器发送一个文件、从服务器向客户发送一个文件、从服务器向客户发送文件或目录列表。
FTP为了适应不同的网络环境,支持主动连接和被动连接两种模式。这两种模式都主要针对数据链路进行的,跟控制链路无关。
FTP的安全隐患:
一、FTP服务器软件漏洞。
二、明文口令。
三、FTP旗标。
四、通过FTP服务器进行端口扫描。
五、数据劫持。
FTP的安全策略:
一、使用较比安全的系统和FTP服务软件。
二、使用密文传输用户名和口令。
三、更改服务软件的旗标。
四、加强协议安全性。
SFTP是Secure File Transfer Protocol的缩写,是安全文件传送协议。可以为传输文件提供一种安全的加密方法。跟ftp几乎语法功能一样。
SFTP是SSH的一部分,是一种传输档案至Blogger伺服器的安全方式。它本身没有单独的守护进程,必须使用sshd守护进程来完成相应的连接操作,所以从某种意义上来说,SFTP并不像一个服务器程序,而更像是一个客户端程序。SFTP同样是使用加密传输认证信息和传输的数据,所以使用SFTP是十分安全的。但由于这种传输方式使用了加密/解密技术,所以传输效率比普通的FTP要低得多。在对网络安全性要求更高时,代替FTP使用。
服务器连接方式:
一、FTP连接
1、 ftp ip (eg:ftp 127.0.0.1)
2、输入账号密码
二、SFTP连接
1、 sftp ip (eg:ftp 127.0.0.1)
2、输入账号密码
JAVA代码连接方式:
FTP连接工具类:
导入pom依赖:
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.2</version>
</dependency>
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
public class FtpFileUploadUtils {
private static Logger logger = LoggerFactory.getLogger(FtpFileUploadUtils.class);
private static final String ip = "";
private static final String userName = "";
private static final String password = "";
private static final String localPath = "";
private static final String remotePath = "";
private FTPClient ftp = null;
/**
* Ftp初始化
*
* @param ip
* @param username
* @param password
* @throws SocketException
* @throws IOException
*/
public boolean initFtp(String ip, String username, String password) {
FTPClient ftp = new FTPClient();
FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
try {
ftp.configure(conf);
ftp.connect(ip);
log.info("-Connecting-");
ftp.login(username, password);
log.info("-Connetcted-");
} catch (SocketException e) {
log.error("连接ftp服务器异常:" + e);
return false;
} catch (IOException e) {
log.error("连接ftp服务器异常:" + e);
return false;
}
this.ftp = ftp;
return true;
}
/**
* 关闭ftp连接
*/
private void closeFtp() {
try {
if (ftp != null) {
ftp.logout();
ftp.disconnect();
log.info("========关闭ftp连接========");
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 列出远程目录所有文件名
*
* @param ip
* @param username
* @param password
* @param remoteDirectory
* @return 此处返回的文件名格式为:/xx/xx/fileName
返回的是远程目录+具体文件名。
*/
public String[] listNames(String ip, String username, String password, String remoteDirectory) {
try {
initFtp(ip, username, password);
log.info("list " + remoteDirectory + "success.");
ftp.enterLocalPassiveMode();
return ftp.listNames(remoteDirectory);
} catch (IOException e) {
log.info("listNames fail");
return null;
} finally {
closeFtp();
}
}
/**
* @Description 删除远程ftp文件
* @param ip 需要连接的远程ftp地址
* @param username 需要连接的远程ftp用户名
* @param password 需要连接的远程ftp密码
* @param remotePath ftp服务器路径 /xx/xx
* @param remoteFile 文件名称
*/
private boolean delete(String ip, String username, String password,String remotePath, String remoteFile) {
FtpFileUpload ftp = new FtpFileUpload();
return ftp.deleteDownload(ip, username, password,remotePath, remoteFile);
}
public boolean deleteDownload(String ip, String username, String password,String remotePath, String remoteFile) {
try {
initFtp(ip, username, password);
ftp.changeWorkingDirectory(remotePath);
ftp.enterLocalPassiveMode();
int reply = ftp.dele(remoteFile);
checkFtpReply(reply);
log.info("delete file " + remoteFile + " success.");
return true;
} catch (IOException e) {
log.info("delete file " + remoteFile + " failure." + e.getMessage());
return false;
} finally {
closeFtp();
}
}
/**
* @Description ftp下载文件到本地
* @param ip 需要连接的远程ftp地址
* @param username 需要连接的远程ftp用户名
* @param password 需要连接的远程ftp密码
* @param localPath 本地服务器路径 /xxx/xx/
* @param remotePath ftp服务器路径 /xx/xx
* @param fileName 文件名称
* @param initTag 是否需要初始化连接ftp服务器
*
*/
public boolean ftpDownload(String ip, String username, String password, String localPath,
String remotePath,String fileName, boolean initTag) {
FileOutputStream fout = null;
try {
// 通过FTP的方式连接目标机
if (initTag) {
initFtp(ip, username, password);
}
// 通过文件输出流下载文件
File file = new File(localPath + fileName);
fout = new FileOutputStream(file);
ftp.changeWorkingDirectory(remotePath);
ftp.enterLocalPassiveMode();
if (!ftp.retrieveFile(fileName, fout)) {
log.info("get " + fileName + " fail.");
fout.close();
fout = null;
if (!file.delete()) {
log.info("空文件删除失败");
}
return false;
}
log.info("get file " + fileName + " to " + localPath + " success.");
return true;
} catch (FileNotFoundException fe) {
log.warn("get fail, can not create local file:" + fe);
return false;
} catch (IOException ioe) {
log.warn("get fail, IOException:" + ioe);
return false;
} finally {
IOUtils.closeQuietly(fout);
closeFtp();
}
}
/**
* 校验获取的文件是否符合格式
*/
public boolean checkFileName(String fileName) {
String regex = "^需要匹配的名称_*";
Pattern p = Pattern.compile(regex);
java.util.regex.Matcher m = p.matcher(fileName);
while (m.find()) {
logger.info("有符合的文件");
return true;
}
logger.info("无符合的文件");
return false;
}
/**
* 校验文件是否成功下载在本地
*/
public boolean fileExists(String fileName){
File file = new File(localPath + fileName);
if (file.exists()) {
logger.info("文件{}不存在", fileName);
return true;
}
return false;
}
/**
* 解析文件 此处的fileName 应该是localPath + fileName
也就是取下载到本地服务器的具体目录的文件
*/
public void verifyFile(String fileName){
FileInputStream fis = null;
InputStreamReader reader = null;
BufferedReader in = null;
String line = "";
//此处注意 需要与对方约定好存储文件的格式 是UTF-8 还是GBK亦或是其他
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream(new File(fileName)),"UTF-8"));
for (; (line = in.readLine()) != null; lineNo++) {
//从line中取数据
line = line.trim();
logger.info("第" + lineNo + "行内容为{}", lineArr);
//走处理逻辑
}
} catch (Exception e) {
logger.info("文件 " + fileName + " 格式校验失败", e);
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(reader);
IOUtils.closeQuietly(fis);
}
}
/**
* 重命名远程FTP文件
*
* @param ip
* @param username
* @param password
* @param oldfile
* @param newfile
* @return
*/
public boolean renameFile(String ip, String username, String password, String oldfile,
String newfile) {
try {
initFtp(ip, username, password);
int reply = ftp.rnfr(oldfile);
if (!FTPReply.isPositiveIntermediate(reply)) {
log.info("rename file " + oldfile + " to " + newfile + " failure: " + reply);
}
reply = ftp.rnto(newfile);
if (!FTPReply.isPositiveIntermediate(reply)) {
if (reply != 250) {
log.info("rename file " + oldfile + " to " + newfile + " failure: " + reply);
}
}
log.info("rename file " + oldfile + " to " + newfile + " success.");
return true;
} catch (IOException e) {
return false;
} finally {
closeFtp();
}
}
}
可根据具体逻辑调用方法。自己常用的方法:
1、调用listNames()方法,获取远程ftp服务器上所有的文件(最好与对方约定某个目录)。
2、遍历获取到的所有文件,调用checkFileName()方法匹配符合约定的文件(需要与对方约定好文件取名格式)。
3、调用downFile() ,delete(),fileExists() 方法逐个下载文件,并且删除对方ftp服务器上的文件,并校验本地是否成功下载。
4、解析成功下载到本地服务器的文件,调用verifyFile()方法。
SFTP连接工具类:
导入pom依赖:
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.53</version>
</dependency>
public class FtpFileUploadUtils {
private static Logger logger = LoggerFactory.getLogger(FtpFileUploadUtils.class);
private static final String ip = "";
private static final String userName = "";
private static final String password = "";
private static final String localPath = "";
private static final String remotePath = "";
private ChannelSftp sftp = null;
private Session sshSession = null;
public boolean initSftp(String ip, String username, String password, int port) {
ChannelSftp sftp = null;
try {
JSch jsch = new JSch();
jsch.getSession(username, ip, port);
sshSession = jsch.getSession(username, ip, port);
log.info("Session created... ");
sshSession.setPassword(password);
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
sshSession.setConfig(sshConfig);
sshSession.connect();
log.info("Session connected...");
log.info("Opening Channel...");
Channel channel = sshSession.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
log.info("Connected to " + ip + ".");
} catch (Exception e) {
log.error("连接ftp服务器异常:ip=" + ip + ",异常信息:" + e);
return false;
}
this.sftp = sftp;
return true;
}
/**
* 使用sftp获取远程文件并且保存至本机路径
*
* @param ip
* @param username
* @param password
* @param localPath
* @param remotePath
* @param port
* @return
*/
public DownloadRes getSftp(String ip, String username, String password, String localPath,
String remotePath, int port, boolean initTag) {
// 通过SFTP的方式连接目标机
if (initTag) {
initSftp(ip, username, password, port);
}
FileOutputStream fout = null;
DownloadRes res = new DownloadRes();
String directory = remotePath.substring(0, remotePath.lastIndexOf("/") + 1);
String fileName = remotePath.substring(remotePath.lastIndexOf("/") + 1);
List<String> remoFileName = getFileNameList(directory);
boolean haveFlag = false;
if (remoFileName.size() == 0) {
res.setSuccess(false);
res.setExist(false);
closeSftp();
return res;
}
for (String s : remoFileName) {
if (s.equals(fileName)) {
haveFlag = true;
}
}
if (!haveFlag) {
res.setSuccess(false);
res.setExist(false);
closeSftp();
return res;
}
try {
sftp.cd(directory);
log.info("所在目录:" + directory);
// 通过文件输出流下载文件
File file = new File(localPath);
fout = new FileOutputStream(file);
sftp.get(remotePath, fout);
fout.flush();
// fout.close();
log.info("get file " + remotePath + " to " + localPath + " success.");
res.setSuccess(true);
res.setExist(true);
return res;
} catch (FileNotFoundException fe) {
fe.printStackTrace();
log.warn("get fail, can not create local file:" + fe);
res.setSuccess(false);
res.setExist(true);
return res;
} catch (IOException ioe) {
ioe.printStackTrace();
log.warn("get fail, IOException:" + ioe);
res.setSuccess(false);
res.setExist(true);
return res;
} catch (SftpException se) {
se.printStackTrace();
log.warn("get fail, SftpException:" + se);
res.setSuccess(false);
res.setExist(true);
return res;
} finally {
IOUtils.closeQuietly(fout);
closeSftp();
}
}
}