关于ftp和sftp对文件处理

最近分到一个需求做一个关于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;
	}

 

至此,大致功能实现,不同点是根据公司业务要求不同,在做相应的处理,核心功能是不会变的。

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值