最近分到一个需求做一个关于ftp和sftp对文件的处理 ,但是我们只是简单做一个转发,并不需要具体下载到某个具体路径,其中最难处理的就是中文乱码这一块,下面记录下,以便日后可以复用。
FTP工具类
package com.yaic.utils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.yaic.servicelayer.charset.StandardCharset;
/**
*
*
*/
public class FtpFileUtil
{
private static final Logger logger = LoggerFactory.getLogger(FtpFileUtil.class);
/**
* 服务器地址
*/
private String hostName;
/**
* 服务器端口
*/
private int port;
/**
* 服务器用户名
*/
private String userName;
/**
* 服务器密码
*/
private String password;
/**
* 服务器文件路径
*/
private String filePath;
/**
* 设置PassiveMode传输
*/
private Integer type = null;
private FTPClient ftpClient = null;
/**
* 设置默认超时时间
*/
private int defaultTimeoutSecond = 5 * 60 * 1000;
/**
* 设置读取数据时阻塞的超时时间
*/
private int soTimeoutSecond = 10 * 60 * 1000;
/**
* 设置数据超时时间
*/
private int dataTimeoutSecond = 2 * 60 * 1000;
/**
* 设置传输模式
*/
private int FILE_TYPE = FTP.BINARY_FILE_TYPE;
/**
* 默认字符集
*/
private String LOCAL_CHARSET = StandardCharset.UTF_8.name();
/**
* 连接到服务器
*
* @return true 连接服务器成功,false 连接服务器失败
*/
private boolean connectServer()
{
final boolean flag = true;
if (ftpClient == null)
{
return login();
}
return flag;
}
/**
* 登录
*
* @return 登录成功true,失败false
*/
public boolean login()
{
boolean flag = true;
int reply;
try
{
ftpClient = new FTPClient();
ftpClient.setDefaultPort(port);
ftpClient.connect(hostName);
ftpClient.login(userName, password);
ftpClient.setDefaultTimeout(defaultTimeoutSecond);
ftpClient.setSoTimeout(soTimeoutSecond);
ftpClient.setDataTimeout(dataTimeoutSecond);
reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply))
{
ftpClient.disconnect();
logger.error("FTP server refused connection");
flag = false;
}
}catch (final SocketException e){
flag = false;
logger.error("登录失败,连接超时!hostName:{},原因:{}", hostName, e.getMessage(), e);
}catch (final IOException e){
flag = false;
logger.error("登录失败,FTP服务器无法打开!hostName:{},原因:{}", hostName, e.getMessage(), e);
}
return flag;
}
/**
* 关闭连接
*/
public void closeConnect()
{
if (ftpClient != null)
{
try
{
ftpClient.logout();
}catch (final IOException e){
logger.error("退出出现异常:{}", e.getMessage(), e);
}
try
{
ftpClient.disconnect();
}catch (final IOException e){
logger.error("关闭连接出现异常:{}", e.getMessage(), e);
}
}
}
/**
*
* 上传文件至FTP服务器 如果目录不存在创建目录
*
* @param filePath 服务器文件路径
* @param fileName 文件名字
* @param input 输入流
* @return 上传成功true,失败false
*/
public synchronized boolean uploadFile(final String filePath, final String fileName, final InputStream input)
{
return uploadFile(filePath, fileName, input, LOCAL_CHARSET);
}
/**
*
* 上传文件至FTP服务器 如果目录不存在创建目录
*
* @param filePath 服务器文件路径
* @param fileName 文件名字
* @param input 输入流
* @param charset 自定义字符集
* @return 上传成功true,失败false
*/
public synchronized boolean uploadFile(final String filePath, final String fileName, final InputStream input,
final String charset)
{
boolean flag = true;
String newchar = charset.toUpperCase();
try
{
connectServer();
if (newchar.equals(LOCAL_CHARSET))
{
if (!FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON")))
{// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
throw new Exception("FTP服务器不支持UTF-8编码");
}
}
ftpClient.setControlEncoding(newchar);
ftpClient.setFileType(FILE_TYPE);
if (type == null || type == 1)
{
ftpClient.enterLocalPassiveMode(); //本地被动模式
}else if (2 == type){
ftpClient.enterRemotePassiveMode();//远程被动模式
}else if (3 == type){
ftpClient.enterLocalActiveMode();//本地主动模式
}
ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE); // 设置传输方式为流方式
String newFileName = new String(fileName.getBytes(newchar), "iso-8859-1");
boolean isChangeWork = ftpClient.changeWorkingDirectory(filePath);
//如果目录不存在,则创建目录
if (!isChangeWork)
{
String[] dirAry = filePath.split("/");
if (dirAry.length > 0)
{
String dirPath = "";
for (int i = 1; i < dirAry.length; i++)
{
if ("".equals(dirPath))
{
dirPath = "/" + dirAry[i];
}else{
dirPath = dirPath + "/" + dirAry[i];
}
if (!ftpClient.changeWorkingDirectory(dirPath))
{
boolean isMade = ftpClient.makeDirectory(dirPath);
if (!isMade)
{
throw new IOException("ftp 上传文件创建目录失败");
}
}
}
}
isChangeWork = ftpClient.changeWorkingDirectory(filePath);
if (!isChangeWork)
{
logger.info("切换路径失败!");
}
}
if (input == null)
{
logger.info("文件不存在!");
}
flag = ftpClient.storeFile(newFileName, input);
}catch (final IOException e){
flag = false;
logger.error("文件上传失败!原因:{}", e.getMessage(), e);
}catch (final Exception e){
flag = false;
logger.error("文件上传失败!原因:{}", e.getMessage(), e);
}
finally
{
try
{
if (input != null)
{
input.close();
}
}catch (final Exception e){
}
}
return flag;
}
/**
* 下载文件
*
* @param remoteFileName 远程文件的名称
* @param out 输出流
* @return 下载成功true,失败false
*/
public boolean downloadFile(final String remoteFileName, final OutputStream out)
{
boolean flag = true;
try
{
connectServer();
String localFileName = new String(remoteFileName.getBytes("GBK"), "ISO-8859-1");
ftpClient.setFileType(FILE_TYPE);
flag = ftpClient.retrieveFile(localFileName, out);
}catch (final Exception e){
flag = false;
logger.error("文件下载失败!", e.getMessage(), e);
}
finally
{
try
{
if (out != null)
{
out.close();
}
}catch (final Exception e){
}
}
return flag;
}
/**
* 在服务器上创建一个文件夹
*
* @param dir 文件夹名称
* @return 创建成功true,失败false
*/
public boolean makeDirectory(final String dir)
{
boolean flag = true;
try
{
connectServer();
flag = ftpClient.makeDirectory(dir);
}catch (final Exception e){
flag = false;
logger.error("创建文件夹异常!", e.getMessage(), e);
}
return flag;
}
/**
* 列出Ftp服务器上的所有文件和目录
*
* @param pathname 路径名称
* @return fileLists 文件列表
*/
public List<String> listRemoteAllFiles(final String pathname)
{
List<String> fileLists = new ArrayList<String>();
String[] names = null;
try
{
connectServer();
names = ftpClient.listNames(pathname);
for (int i = 0, len = names.length; i < len; i++)
{
String file = names[i];
byte[] bytes = file.getBytes("iso-8859-1");
String str = new String(bytes, "GBK");
fileLists.add(str);
}
}catch (final Exception e){
logger.error("出现异常!", e.getMessage(), e);
}
return fileLists;
}
/**
* 删除一个文件
*
* @param filename 文件名称
* @return 删除成功true,失败false
*/
public boolean deleteFile(final String filename)
{
boolean flag = true;
try
{
connectServer();
flag = ftpClient.deleteFile(new String(filename.getBytes("GBK"), "iso-8859-1"));
}catch (final IOException ioe){
flag = false;
logger.error("删除异常:{}", ioe.getMessage(), ioe);
}
return flag;
}
/**
* 删除目录
*
* @param pathname 目录名称
*/
public void deleteDirectory(final String pathname)
{
try
{
connectServer();
final File file = new File(pathname);
if (file.isDirectory())
{
file.listFiles();
}else{
deleteFile(pathname);
}
ftpClient.removeDirectory(pathname);
}catch (final IOException ioe){
logger.error("删除异常:{}", ioe.getMessage(), ioe);
}
}
/**
* 获取指定目录下的文件列表
*
* @param filePath 服务器文件路径
* @return 文件列表
*/
public List<String> getFileList(final String filePath)
{
FTPFile[] fs = null;
ArrayList<String> list = new ArrayList<>();
try
{
connectServer();
ftpClient.enterLocalPassiveMode();
ftpClient.changeWorkingDirectory(filePath);
fs = ftpClient.listFiles();
for (FTPFile ff : fs)
{
byte[] bytes = ff.getName().getBytes("iso-8859-1");
ff.setName(new String(bytes, "GBK"));
list.add(ff.getName());
}
}catch (final IOException e){
logger.error("获取指定目录下的文件列表失败!原因:{}", e.getMessage(), e);
}
return list;
}
/**
* ftpClient切换到指定目录
*
* @param filePath
*/
public void changeWorkingDirectory(String filePath)
{
try
{
ftpClient.changeWorkingDirectory(filePath);
}catch (IOException e){
logger.error("切换到指定目录失败!原因:{}", e.getMessage(), e);
}
}
/**
* 无参构造
*/
public FtpFileUtil()
{
super();
}
/**
* 有参构造
*
* @param hostName 服务器名称
* @param port 服务器端口
* @param userName 服务器用户名
* @param password 服务器密码
*/
public FtpFileUtil(final String hostName, final int port, final String userName, final String password)
{
super();
this.hostName = hostName;
this.port = port;
this.userName = userName;
this.password = password;
}
/**
* 有参构造
*
* @param hostName 服务器名称
* @param port 服务器端口
* @param userName 服务器用户名
* @param password 服务器密码
* @param filePath 服务器文件路径
*/
public FtpFileUtil(final String hostName, final int port, final String userName, final String password, final String filePath)
{
super();
this.hostName = hostName;
this.port = port;
this.userName = userName;
this.password = password;
this.filePath = filePath;
}
/**
* 有参构造
*
* @param hostName 服务器名称
* @param port 服务器端口
* @param userName 服务器用户名
* @param password 服务器密码
* @param filePath 服务器文件路径
* @param type PassiveMode传输类型
*/
public FtpFileUtil(String hostName, int port, String userName, String password, String filePath, Integer type)
{
super();
this.hostName = hostName;
this.port = port;
this.userName = userName;
this.password = password;
this.filePath = filePath;
this.type = type;
}
/**
* @return 获取服务器地址
*/
public String getHostName()
{
return hostName;
}
/**
* @param hostName 服务器地址
*/
public void setHostName(final String hostName)
{
this.hostName = hostName;
}
/**
* @return 获取服务器端口
*/
public int getPort()
{
return port;
}
/**
* @param port 服务器端口
*/
public void setPort(final int port)
{
this.port = port;
}
/**
* @return 获取服务器用户名
*/
public String getUserName()
{
return userName;
}
/**
* @param userName 服务器用户名
*/
public void setUserName(final String userName)
{
this.userName = userName;
}
/**
* @return 获取服务器密码
*/
public String getPassword()
{
return password;
}
/**
* @param password 服务器密码
*/
public void setPassword(final String password)
{
this.password = password;
}
/**
* @return 获取服务器文件路径
*/
public String getFilePath()
{
return filePath;
}
/**
* @param filePath 服务器文件路径
*/
public void setFilePath(String filePath)
{
this.filePath = filePath;
}
/**
* @return 获取传输模式
*/
public int getFILE_TYPE()
{
return FILE_TYPE;
}
/**
* @param fILE_TYPE 传输模式
*/
public void setFILE_TYPE(final int fILE_TYPE)
{
FILE_TYPE = fILE_TYPE;
}
/**
* @param defaultTimeoutSecond 默认超时时间
*/
public void setDefaultTimeoutSecond(final int defaultTimeoutSecond)
{
this.defaultTimeoutSecond = defaultTimeoutSecond;
}
/**
* @param soTimeoutSecond 读取数据时阻塞的超时时间
*/
public void setSoTimeoutSecond(final int soTimeoutSecond)
{
this.soTimeoutSecond = soTimeoutSecond;
}
/**
* @param dataTimeoutSecond 数据超时时间
*/
public void setDataTimeoutSecond(final int dataTimeoutSecond)
{
this.dataTimeoutSecond = dataTimeoutSecond;
}
}
SFTP工具类
package com.yaic.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
/**
*
*/
public class SftpFileUtil
{
private static final Logger logger = LoggerFactory.getLogger(SftpFileUtil.class);
private ChannelSftp sftp = null;
private Session sshSession = null;
/**
* 连接sftp服务器
* @param host
* @param port
* @param username
* @param password
* @param key
*/
public void connect(final String host, final int port, final String username, final String password, final String key)
{
try
{
JSch jsch = new JSch();
sshSession = jsch.getSession(username, host, port);
if (logger.isInfoEnabled())
{
logger.info("Session created");
}
sshSession.setPassword(password);
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", key);
sshSession.setConfig(sshConfig);
sshSession.connect();
if (logger.isInfoEnabled())
{
logger.info("Session connected");
}
Channel channel = sshSession.openChannel("sftp");
channel.connect();
if (logger.isInfoEnabled())
{
logger.info("opening channel");
}
sftp = (ChannelSftp) channel;
if (logger.isInfoEnabled())
{
logger.info("connected to" + host);
}
}catch (final Exception e){
logger.error("连接失败,SFTP服务器无法打开!host:{},原因:{}", host, e.getMessage(), e);
}
}
/**
* 断开连接
*/
public void closeConnect()
{
if (this.sftp != null)
{
if (this.sftp.isConnected())
{
this.sftp.disconnect();
if (logger.isInfoEnabled())
{
logger.info("sftp is closed already");
}
}
}
if (this.sshSession != null)
{
if (this.sshSession.isConnected())
{
this.sshSession.disconnect();
if (logger.isInfoEnabled())
{
logger.info("sshSesion is closed already");
}
}
}
}
/**
* 上传单一文件
* @param remotePath 远程ftp文件目录位置
* @param fileName 文件名称
* @param input 输入流
* @return true if successfully
*/
public boolean uploadFile(final String remotePath, final String fileName, final InputStream input)
{
try
{
createDir(remotePath);
sftp.put(input, fileName);
return true;
}catch (final Exception e){
logger.error("文件上传失败!原因:{}", e.getMessage(), e);
}finally{
if (input != null)
{
try
{
input.close();
}catch (final IOException e){
}
}
}
return false;
}
/**
* 下载指定目录下的单一文件
* @param remotePath 服务器远程目录位置
* @param out 输出流
* @return true if successfully
*/
public boolean downloadFile(final String remotePath, final OutputStream out)
{
try
{
sftp.get(remotePath, out);
return true;
}catch (Exception e){
logger.error("文件下载失败!原因:{}", e.getMessage(), e);
}finally{
if (out != null)
{
try
{
out.flush();
out.close();
}catch (final IOException e){
}
}
}
return false;
}
/**
* 删除指定目录下的文件
* @param directory 服务器上的文件目录
* @param deleteFile 删除的文件
* @return true if successfully
*/
public boolean deleteSFTPFile(final String directory, final String deleteFile)
{
try
{
sftp.cd(directory);
sftp.rm(deleteFile);
return true;
}catch (final Exception e){
logger.error("文件删除失败!原因:{}", e.getMessage(), e);
return false;
}
}
/**
* 删除指定目录下的所有文件
* @param remotePath 远程服务器上的文件目录
* @return true if successfully
*/
public boolean deleteSFTPFiles(final String remotePath)
{
try
{
sftp.cd(remotePath);
Vector vector = sftp.ls(remotePath);
for (int i = 0; i < vector.size(); i++)
{
ChannelSftp.LsEntry entry = (LsEntry) vector.get(i);
if (".".equals(entry.getFilename()) || "..".equals(entry.getFilename()))
{
continue;
}
sftp.rm(remotePath + entry.getFilename());
}
return true;
}catch (final Exception e){
logger.error("批量删除文件失败!原因:{}", e.getMessage(), e);
return false;
}
}
/**
* 获取文件列表
* @param remotePath
* @return listFiles
*/
public List<String> listFiles(final String remotePath)
{
Vector vector = null;
List<String> list = new ArrayList<>();
try
{
vector = sftp.ls(remotePath);
Iterator iter = vector.iterator();
while (iter.hasNext())
{
LsEntry ls = (LsEntry) iter.next();
String filename = ls.getFilename();
list.add(filename);
}
}catch (final Exception e){
logger.error("获取指定目录下的文件列表失败!原因:{}", e.getMessage(), e);
}
return list;
}
/**
* 创建文件夹
* @param remotePath
*/
private boolean createDir(String remotePath)
{
try
{
if (isDirExist(remotePath))
{
this.sftp.cd(remotePath);
return true;
}
String pathArray[] = remotePath.split("/");
StringBuffer filePath = new StringBuffer("/");
for (String path : pathArray)
{
if ("".equals(path))
{
continue;
}
filePath.append(path).append("/");
if (isDirExist(filePath.toString()))
{
sftp.cd(filePath.toString());
}else{
sftp.mkdir(filePath.toString());
sftp.cd(filePath.toString());
}
this.sftp.cd(remotePath);
return true;
}
}catch (Exception e){
logger.error("创建文件夹失败!原因:{}", e.getMessage(), e);
}
return false;
}
/**
* 判断是否是文件夹
* @param remotePath
*/
private boolean isDirExist(final String remotePath)
{
boolean flag = false;
try
{
SftpATTRS sftpATTRS = sftp.lstat(remotePath);
flag = true;
return sftpATTRS.isDir();
}catch (Exception e){
if (e.getMessage().toLowerCase().equals("no such file"))
{
flag = false;
}
}
return flag;
}
}
在service层去直接调用时候,注意一点,就是下载时候需要设置请求头,如果没有设置好,容易出现下载的文件名是乱码情况(sftp和ftp都用这个就可以),当时解决办法是:
/**
* 下载设置请求头信息
* @param httpRequest
* @param response
*/
private String AddHeaderMessage(HttpRequest<FtpFileReq> httpRequest, HttpServletResponse response) {
String filePath = httpRequest.getBody().getFilePath();
response.setContentType("application/octet-stream;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
String fileName = new File(filePath).getName();
try
{
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + fileName);
}catch (Exception e){
logger.error("获取文件名失败,原因:{}", e.getMessage(), e);
}
return filePath;
}
至此,大致功能实现,不同点是根据公司业务要求不同,在做相应的处理,核心功能是不会变的。