1. FTP
FTP通讯协议采用客户机 / 服务器(Client / Server )架构。用户可以通过各种不同的FTP客户端程序,借助FTP协议,来连接FTP服务器,以上传或者下载文件。
默认端口: 21
- 在此协议中SSH2服务器起了一个桥梁作用,把数据在客户端和ftp之间转发。ftp协议本身包括两个通道,一个是控制通道,另一个是数据通道。
2. SFTP
Sftp是Secure File Transfer Protocol的缩写,安全文件传送协议。可以为传输文件提供一种安全的加密方法。
- sftp是ssh内含的协议,只要sshd服务器启动了,它就可用,它本身不需要ftp服务器启动
- 它是作为SSH2的一个子服务工作的
3. SFTP 和FTPS的区别
1、安全通道
FTP 不提供任何安全通道来在主机之间传输文件;而SFTP协议提供了一个安全通道,用于在网络上的主机之间传输文件。
2、使用的协议
FTP使用TCP / IP协议。而,SFTP是SSH协议的一部分,它是一种远程登录信息。
3、链接方式
FTP使用TCP端口21上的控制连接建立连接。而,SFTP是在客户端和服务器之间通过SSH协议(TCP端口22)建立的安全连接来传输文件。
4、安全性
FTP密码和数据以纯文本格式发送,大多数情况下是不加密的,安全性不高。而,SFTP会在发送之前加密数据,二进制的形式传递,是无法“按原样”阅读的,安全性较高。
4. JAVA下针对于FTP实现上传下载工具类
import java.io.*;
import org.apache.commons.io.IOUtils;
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;
/**
* @Author: xiezb
* @Date: 2019/9/23 10:14
* @Description: ftp上传下载工具类
*/
@SuppressWarnings("ALL")
public class FtpUtil {
/**文件编码*/
private static String CODE = "UTF-8";
/** ftp服务编码*/
private static String SERVER_CODE = "ISO-8859-1";
/**
* 获取FTP连接 <br>
* @param host FTP服务器hostname <br>
* @param port FTP服务器端口 <br>
* @param username FTP登录账号 <br>
* @param password FTP登录密码 <br>
* @return FTPClient对象 <br>
*/
public static FTPClient getFTPConnect(String host, int port, String username, String password) {
FTPClient ftp = new FTPClient();
try {
int reply;
//1.连接服务器
ftp.connect(host, port);
//2.登录服务器 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
ftp.login(username, password);
//3.判断登陆是否成功
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return null;
}
//ftp.setFileType(FTPClient.BINARY_FILE_TYPE);//@fix 设置二进制解决偶有图图片传输失真问题
return ftp;
} catch (IOException e) {
e.printStackTrace();
closeFTP(ftp);
}
return null;
}
/**
* 关闭FTP连接,释放资源 <br>
* @param ftpClient 连接对象 <br>
*/
public static void closeFTP(FTPClient ftpClient) {
if (!ObjectUtil.isEmptyObject(ftpClient) && ftpClient.isConnected()) {
try {
ftpClient.logout();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Description: 向FTP服务器上传文件 <br>
* @param ftp 连接对象 <br>
* @param basePath FTP服务器基础目录 <br>
* @param filePath FTP服务器文件存放路径。文件的路径为basePath+filePath <br>
* @param filename 上传到FTP服务器上的文件名 <br>
* @param input 输入流 <br>
* @return 成功返回true,否则返回false <br>
*/
public static boolean uploadFile(FTPClient ftp, String basePath,
String filePath, String filename, InputStream input) {
try {
if (ObjectUtil.isEmptyObject(ftp)) {
return false;
}
if (!ObjectUtil.isEmptyObject(basePath)) {
basePath = basePath.replace("\\", "/"); // 配置中可能配置的是windows的,换Linux "/"
basePath = basePath.endsWith("/") ? basePath.substring(0, basePath.length() - 1) : basePath;
}
String tempPath = basePath;
if (!ObjectUtil.isEmptyObject(filePath)) {
filePath = filePath.replace("\\", "/"); // 配置中可能配置的是windows的,换Linux "/"
if (filePath.startsWith("/")) {
tempPath += filePath;
} else {
if (ObjectUtil.isEmptyObject(tempPath)) {
tempPath = filePath;
} else {
tempPath = tempPath + "/" + filePath;
}
}
}
//切换到上传目录
if (!ftp.changeWorkingDirectory(tempPath)) {
//如果目录不存在创建目录
String[] dirs = tempPath.split("/");
String path = "";
for (String dir : dirs) {
if (ObjectUtil.isEmptyObject(dir)) continue;
if (tempPath.startsWith("/")) {
path += "/" + dir;
} else {
if (ObjectUtil.isEmptyObject(path)) {
path += dir;
} else {
path += "/" + dir;
}
}
if (!ftp.changeWorkingDirectory(path)) { //进不去目录,说明该目录不存在
if (!ftp.makeDirectory(path)) { //创建目录
//如果创建文件目录失败,则返回
System.out.println("创建文件目录"+path+"失败");
return false;
} else {
//目录存在,则直接进入该目录
ftp.changeWorkingDirectory(path);
}
}
}
}
//设置上传文件的类型为二进制类型
ftp.setFileType(FTP.BINARY_FILE_TYPE);
//上传文件
filename = new String(filename.getBytes(CODE), SERVER_CODE);
if (!ftp.storeFile(filename, input)) {
return false;
}
return true;
} catch (IOException e) {
e.printStackTrace();
} finally {
closeFTP(ftp);
}
return false;
}
/**
* Description: 从FTP服务器下载文件 <br>
* @param ftp 连接对象 <br>
* @param remotePath FTP服务器上的相对路径 <br>
* @param fileName 要下载的文件名 <br>
* @param localPath 下载后保存到本地的路径 <br>
* @return 结果: <br>
* true: 成功; false: 失败 <br>
*/
public static boolean downloadFile(FTPClient ftp, String remotePath,
String fileName, String localPath) {
try {
if (ObjectUtil.isEmptyObject(ftp)) {
return false;
}
ftp.setControlEncoding(CODE);
ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs) {
if (ff.getName().equals(fileName)) {
localPath = localPath.replace("\\", File.separator);
localPath = localPath + (localPath.endsWith(File.separator) ? fileName : "/" + fileName);
File file = file = new File(localPath);
if (file.exists()) {
file.delete();
}
file.createNewFile();
file = new File(localPath);
OutputStream is = new FileOutputStream(file);
ftp.retrieveFile(ff.getName(), is);
is.close();
}
}
return true;
} catch (IOException e) {
e.printStackTrace();
} finally {
closeFTP(ftp);
}
return false;
}
/**
* Description: 从FTP服务器下载文件 <br>
* @param ftp 连接对象 <br>
* @param remotePath FTP服务器上的相对路径 <br>
* @param fileName 要下载的文件名 <br>
* @return 结果: 文件的二进制数组 <br>
*
*/
public static byte[] downloadFile(FTPClient ftp, String remotePath, String fileName) {
byte[] bytes = null;
try {
if (ObjectUtil.isEmptyObject(ftp)) {
return null;
}
ftp.setControlEncoding(CODE);
ftp.setFileType(ftp.BINARY_FILE_TYPE);
ftp.setBufferSize(1024);
ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs) {
if (ff.getName().equals(fileName)) {
String remoteAbsoluteFile = ff.getName();
remoteAbsoluteFile = new String(remoteAbsoluteFile.getBytes(CODE), SERVER_CODE);
InputStream in = ftp.retrieveFileStream(remoteAbsoluteFile);
bytes = IOUtils.toByteArray(in);
System.out.println("下载成功!" + bytes.length);
in.close();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
closeFTP(ftp);
}
return bytes;
}
/**
* 文件转成 byte[]
* @param inStream
* @return
* @throws IOException
*/
public static byte[] input2byte(InputStream inStream) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
byte[] b = new byte[1024];
int len = -1;
while((len = inStream.read(b)) != -1) {
bos.write(b, 0, len);
}
bos.flush();
byte[] in2b = bos.toByteArray();
bos.close();
return in2b;
}
/**
* 删除文件-FTP方式 <br>
*
* @param ftp FTPClient对象 <br>
* @param ftpPath FTP服务器上传地址 <br>
* @param ftpFileName FTP服务器上要删除的文件名 <br>
* @return
*/
public static void deleteFile(FTPClient ftp, String ftpPath, String ftpFileName) throws Exception{
try {
ftp.changeWorkingDirectory(ftpPath);//转移到指定FTP服务器目录
ftpFileName = new String(ftpFileName.getBytes(CODE), SERVER_CODE);
ftp.deleteFile(ftpFileName);
} catch (Exception e){
e.printStackTrace();
throw e;
} finally {
closeFTP(ftp);
}
}
//ftp上传文件测试main函数
public static void main(String[] args) {
try {
FileInputStream in=new FileInputStream(new File("F:\\abc\\201909\\哈哈.txt"));
FTPClient ftp = getFTPConnect("ip",21,"用户名","密码");
// 上传
boolean flag = uploadFile(ftp, "","上传路径目录", "哈哈.txt", in);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5. Java操作SFTP 工具类
import com.jcraft.jsch.*;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.Properties;
import java.util.Vector;
/**
* @Author: xiezb
* @Date: 2019/9/23 13:13
* @Description: 类说明 sftp工具类
*/
@SuppressWarnings("ALL")
public class SFTPUtil {
private transient Logger log = LoggerFactory.getLogger(this.getClass());
private ChannelSftp sftp;
private Session session;
/** SFTP 登录用户名*/
private String username;
/** SFTP 登录密码*/
private String password;
/** 私钥 */
private String privateKey;
/** SFTP 服务器地址IP地址*/
private String host;
/** SFTP 端口*/
private int port;
/**默认连接超时*/
private static Integer timeOut = 20000;
/**文件编码*/
private static String FILE_CODE = "UTF-8";
/**
* 构造基于密码认证的sftp对象 <br>
* @param host 主机 <br>
* @param port 端口号 <br>
* @param username 用户名 <br>
* @param password 密码 <br>
*/
public SFTPUtil(String host, int port, String username, String password) {
this.username = username;
this.password = password;
this.host = host;
this.port = port;
}
/**
* 构造基于秘钥认证的sftp对象 <br>
* @param username 用户名 <br>
* @param host 主机 <br>
* @param port 端口 <br>
* @param privateKey 私钥 <br>
*/
public SFTPUtil(String username, String host, int port, String privateKey) {
this.username = username;
this.host = host;
this.port = port;
this.privateKey = privateKey;
}
public SFTPUtil(){}
/**
* 连接sftp服务器
*/
public void login(){
try {
JSch jsch = new JSch();
if (privateKey != null) {
jsch.addIdentity(privateKey);// 设置私钥
}
session = jsch.getSession(username, host, port);
if (password != null) {
session.setPassword(password);
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.setTimeout(timeOut);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
sftp.setFilenameEncoding(FILE_CODE);
} catch (Exception e) {
e.printStackTrace();
this.logout();
}
}
/**
* 关闭连接 server
*/
public void logout(){
if (!ObjectUtil.isEmptyObject(sftp)) {
if (sftp.isConnected()) {
sftp.disconnect();
}
}
if (!ObjectUtil.isEmptyObject(session)) {
if (session.isConnected()) {
session.disconnect();
}
}
}
/**
* 将输入流的数据上传到sftp作为文件。文件完整路径=basePath+directory <br>
* @param basePath 服务器的基础路径 <br>
* @param directory 上传到该目录 <br>
* @param sftpFileName sftp端文件名 <br>
* @param input 输入流 <br>
*/
public boolean uploadFile(String basePath,String directory, String sftpFileName, InputStream input) throws SftpException{
String tempPath = "";
try {
if (!ObjectUtil.isEmptyObject(basePath)) {
basePath = basePath.replace("\\", "/"); // 配置中可能配置的是windows的,换Linux "/"
basePath = basePath.endsWith("/") ? basePath.substring(0, basePath.length() - 1) : basePath;
}
tempPath = basePath;
if (!ObjectUtil.isEmptyObject(directory)) {
directory = directory.replace("\\", "/"); // 配置中可能配置的是windows的,换Linux "/"
if (directory.startsWith("/")) {
tempPath += directory;
} else {
if (ObjectUtil.isEmptyObject(tempPath)) {
tempPath = directory;
} else {
tempPath = tempPath + "/" + directory;
}
}
}
sftp.cd(tempPath);
} catch (SftpException e) {
//目录不存在,则创建文件夹
String [] dirs= tempPath.split("/");
String path = "";
for(String dir : dirs){
if(ObjectUtil.isEmptyObject(dir)) continue;
if (tempPath.startsWith("/")) {
path += "/" + dir;
} else {
if (ObjectUtil.isEmptyObject(path)) {
path += dir;
} else {
path += "/" + dir;
}
}
try{
sftp.cd(path);
}catch(SftpException ex){
sftp.mkdir(path);
sftp.cd(path);
}
}
}
sftp.put(input, sftpFileName); //上传文件
this.logout();
return true;
}
/**
* 下载文件 到 指定目录 <br>
* @param directory 下载目录 <br>
* @param downloadFile 下载的文件 <br>
* @param saveFile 存在本地的路径 <br>
*/
public boolean downloadFile(String directory, String downloadFile, String saveFile) throws SftpException, IOException {
try {
if (!ObjectUtil.isEmptyObject(directory)) {
sftp.cd(directory);
}
saveFile = saveFile.replace("\\", File.separator);
saveFile = saveFile + (saveFile.endsWith(File.separator) ? downloadFile : "/" + downloadFile);
File file = file = new File(saveFile);
if (file.exists()) {
file.delete();
}
file.createNewFile();
file = new File(saveFile);
sftp.get(downloadFile, new FileOutputStream(file));
return true;
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
this.logout();
}
}
/**
* 下载文件,获取二进制数组 <br>
* @param directory 下载目录 <br>
* @param downloadFile 下载的文件名 <br>
* @return 字节数组 <br>
*/
public byte[] downloadFile(String directory, String downloadFile) throws SftpException, IOException{
try {
if (!ObjectUtil.isEmptyObject(directory)) {
sftp.cd(directory);
}
InputStream is = sftp.get(downloadFile);
byte[] fileData = IOUtils.toByteArray(is);
return fileData;
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
this.logout();
}
}
/**
* 删除文件 <br>
* @param directory 要删除文件所在目录 <br>
* @param deleteFile 要删除的文件 <br>
*/
public void deleteFile(String directory, String deleteFile) throws SftpException{
try {
sftp.cd(directory);
sftp.rm(deleteFile);
} catch (SftpException e) {
e.printStackTrace();
throw e;
} finally {
this.logout();
}
}
/**
* 列出目录下的文件 <br>
* @param directory 要列出的目录 <br>
*/
public Vector<?> listFiles(String directory) throws SftpException {
try {
return sftp.ls(directory);
} catch (SftpException e) {
e.printStackTrace();
throw e;
} finally {
this.logout();
}
}
//上传文件测试
public static void main(String[] args) throws SftpException, IOException {
SFTPUtil sftp = new SFTPUtil("ip",22,"用户名","密码");
sftp.login();
FileInputStream is =new FileInputStream(new File("F:\\abc\\哈哈.txt"));
// sftp.uploadFile("","文件上传目录", "哈哈.txt", is);
// sftp.downloadFile("文件所在目录", "哈哈.txt","F:/abc");
sftp.logout();
}
6. 判断空工具
import java.util.*;
/**
* 字符串工具类
*/
public class ObjectUtil {
/**
* 判断对象是否为空
* @Author: xiezb
* @param obj 对象
* @return 是否为空
*/
public static boolean isEmptyObject(Object obj) {
return obj == null;
}
/**
* 判断哈希是否为空
* @Author: xiezb
* @param map 哈希
* @return 是否为空
*/
public static boolean isEmptyObject(Map<?, ?> map) {
return (map == null || map.size() == 0);
}
/**
* 判断字符是否为空
* @Author: xiezb
* @param str 字符
* @return 是否为空
*/
public static boolean isEmptyObject(String str) {
return (str == null || "".equals(str) || "null".equalsIgnoreCase(str) || str.trim().length() == 0);
}
/**
* 判断数组是否为空
* @Author: xiezb
* @param arr 数组
* @return 是否为空
*/
public static boolean isEmptyObject(Arrays[] arr) {
return (arr == null || arr.length == 0);
}
/**
* 判断列表是否为空
* @Author: xiezb
* @param lis 列表
* @return 是否为空
*/
public static boolean isEmptyObject(List<?> lis) {
return (lis == null || lis.size() == 0);
}
/**
* 判断集合是否为空
* @Author: xiezb
* @param ita 集合
* @return 是否为空
*/
public static boolean isEmptyObject(Iterator<?> ita) {
return (ita == null || !ita.hasNext());
}
/**
* 判断缓存是否为空
* @Author: xiezb
* @param sbf 缓存
* @return 是否为空
*/
public static boolean isEmptyObject(StringBuffer sbf) {
return (sbf == null || sbf.length() == 0);
}
}
- 到此, 可通过上面代码自行测试, 快动手试试吧