/** * */ package com.common.file; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.lang.NullArgumentException; import org.apache.commons.lang.StringUtils; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import org.apache.log4j.Logger; import com.common.DateUtils; /** * FTP客户端工具类 * @author luolin * * @version $id:FTPClientUtils.java,v 0.1 2015年11月13日 下午4:18:07 luolin Exp $ */ public class FTPClientUtil { private static final Logger LOGGER = Logger.getLogger(FTPClientUtil.class); /** * 链接文件服务器 * @param addr 文件服务器地址 * @param port 端口 * @param username 用户名 * @param password 密码 * @throws Exception */ public static FTPClient connect(String addr, int port, String username, String password) { LOGGER.info("【链接文件服务器】addr = " + addr + " , port : " + port + " , username = " + username + " , password = " + password); FTPClient ftpClient = new FTPClient(); try { // 链接 ftpClient.connect(addr, port); // 登陆 ftpClient.login(username, password); // 被动模式:每次数据链接以前,ftp client告诉ftp server开通一个端口来传输数据(参考资料:FTP主动/被动模式的解释) ftpClient.enterLocalPassiveMode(); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); } catch (Exception e) { LOGGER.error("【链接文件服务器失败】", e); throw new RuntimeException("链接文件服务器失败"); } // 判断文件服务器是否可用?? if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { closeConnection(ftpClient); } return ftpClient; } /** * 链接文件服务器 * @param addr 文件服务器地址 * @param port 端口 * @param username 用户名 * @param password 密码 * @param workingDirectory 目标链接工做目录 * @throws Exception */ public static FTPClient connect(String addr, int port, String username, String password, String workingDirectory) throws Exception { FTPClient ftpClient = connect(addr, port, username, password); changeWorkingDirectory(workingDirectory, ftpClient); return ftpClient; } /** * 关闭链接,使用完链接以后,必定要关闭链接,不然服务器会抛出 Connection reset by peer的错误 * @throws IOException */ public static void closeConnection(FTPClient ftpClient) { LOGGER.info("【关闭文件服务器链接】"); if (ftpClient == null) { return; } try { ftpClient.disconnect(); } catch (IOException e) { LOGGER.error("【关闭链接失败】", e); throw new RuntimeException("关闭链接失败"); } } /** * 切换工做目录 * @param directory 目标工做目录 * @param ftpClient * @throws IOException */ public static void changeWorkingDirectory(String directory, FTPClient ftpClient) { LOGGER.info("【切换工做目录】directory : " + directory); baseValidate(ftpClient); // 切换到目标工做目录 try { if (!ftpClient.changeWorkingDirectory(directory)) { ftpClient.makeDirectory(directory); ftpClient.changeWorkingDirectory(directory); } } catch (Throwable e) { LOGGER.error("【切换工做目录失败】", e); throw new RuntimeException("切换工做目录失败"); } } /** * 上传文件/文件夹 * @param file 上传的文件或文件夹 * @return 文件存放的路径以及文件名 * @throws Exception */ public static void upload(File file, FTPClient ftpClient) throws Exception { if (file == null) { LOGGER.warn("【存储的文件为空】"); throw new RuntimeException("上传文件为空"); } LOGGER.info("【上传文件/文件夹】file : " + file.getName()); baseValidate(ftpClient); // 是文件,直接上传 if (!file.isDirectory()) { storeFile(new File(file.getPath()), ftpClient); return; } changeWorkingDirectory(file.getName(), ftpClient); // 文件夹,递归上传全部文件 for (File item : file.listFiles()) { if (!item.isDirectory()) { storeFile(item, ftpClient); continue; } upload(item, ftpClient); ftpClient.changeToParentDirectory(); } } /** * 删除文件 * @param fileName 要删除的文件地址 * @return true/false * @throws IOException */ public static boolean delete(String fileName, FTPClient ftpClient) throws IOException { LOGGER.info("【删除文件】fileName : " + fileName); baseValidate(ftpClient); if (StringUtils.isBlank(fileName)) { LOGGER.warn("【参数fileName为空】"); throw new NullArgumentException("fileName"); } return ftpClient.deleteFile(fileName); } /** * 存储文件 * @param file {@link File} * @throws Exception */ public static void storeFile(File file, FTPClient ftpClient) throws Exception { if (file == null) { LOGGER.warn("【存储的文件为空】"); throw new RuntimeException("存储的文件为空"); } LOGGER.info("【存储文件】file : " + file.getName()); baseValidate(ftpClient); FileInputStream input = new FileInputStream(file); ftpClient.storeFile(file.getName(), input); input.close(); } /** * 存储文件 * @param inputStream {@link InputStream} * @param fileName 文件名 * @throws Exception */ public static void storeFile(InputStream inputStream, String fileName, FTPClient ftpClient) throws Exception { LOGGER.info("【存储文件】fileName = " + fileName); if (inputStream == null) { LOGGER.warn("【参数inputStream为空】"); throw new RuntimeException("存储的文件为空"); } baseValidate(ftpClient); ftpClient.storeFile(fileName, inputStream); inputStream.close(); } /** * 下载文件到指定目录 * @param ftpFile 文件服务器上的文件地址 * @param dstFile 输出文件的路径和名称 * @throws Exception */ public static void downLoad(String ftpFile, String dstFile, FTPClient ftpClient) throws Exception { LOGGER.info("【下载文件到指定目录】ftpFile = " + ftpFile + " , dstFile = " + dstFile); if (StringUtils.isBlank(ftpFile)) { LOGGER.warn("【参数ftpFile为空】"); throw new RuntimeException("【参数ftpFile为空】"); } if (StringUtils.isBlank(dstFile)) { LOGGER.warn("【参数dstFile为空】"); throw new RuntimeException("【参数dstFile为空】"); } baseValidate(ftpClient); File file = new File(dstFile); FileOutputStream fos = new FileOutputStream(file); ftpClient.retrieveFile(ftpFile, fos); fos.flush(); fos.close(); } /** * 从文件服务器获取文件流 * @param ftpFile 文件服务器上的文件地址 * @return {@link InputStream} * @throws IOException */ public static InputStream retrieveFileStream(String ftpFile, FTPClient ftpClient) throws IOException { LOGGER.info("【从文件服务器获取文件流】ftpFile : " + ftpFile); if (StringUtils.isBlank(ftpFile)) { LOGGER.warn("【参数ftpFile为空】"); throw new RuntimeException("【参数ftpFile为空】"); } baseValidate(ftpClient); return ftpClient.retrieveFileStream(ftpFile); } /** * 复制文件 * @param ftpClient {@link FTPClient} * @param sourceFile 源文件 * @param targetDir 目标文件夹 * @return 复制后的文件路径及文件名 * @throws Exception */ public static String copy(FTPClient ftpClient, String sourceFile, String targetDir) throws Exception { return copy(ftpClient, sourceFile, targetDir, FileUtil.getFileNameNoPath(sourceFile)); } /** * 复制文件 * @param ftpClient {@link FTPClient} * @param sourceFile 源文件 * @param targetDir 目标文件夹 * @param newName 新的文件名 * @return 复制后的文件路径及文件名 * @throws Exception */ public static String copy(FTPClient ftpClient, String sourceFile, String targetDir, String newName) throws Exception { LOGGER.info("【拷贝文件】sourceFile = " + sourceFile + " , targetDir = " + targetDir + " , newName = " + newName); if (StringUtils.isBlank(sourceFile)) { LOGGER.warn("【参数sourceFile为空】"); throw new NullArgumentException("sourceFile"); } if (StringUtils.isBlank(targetDir)) { LOGGER.warn("【参数targetDir为空】"); throw new NullArgumentException("targetDir"); } if (StringUtils.isBlank(newName)) { LOGGER.warn("【参数newName为空】"); throw new NullArgumentException("newName"); } baseValidate(ftpClient); LOGGER.info("【从文件服务器读取源文件到输入流中】"); InputStream is = ftpClient.retrieveFileStream(sourceFile); // 主动调用一次getReply()把接下来的226消费掉. 这样作是能够解决这个返回null问题 ftpClient.getReply(); if (is == null) { LOGGER.warn("【未找到源文件】"); throw new RuntimeException("未找到源文件"); } LOGGER.info("【将输入流存储到指定的目录】"); changeWorkingDirectory(targetDir, ftpClient); changeWorkingDirectory(DateUtils.getNow(DateUtils.FORMAT_SHORT_FOLDER), ftpClient); storeFile(is, newName, ftpClient); return ftpClient.printWorkingDirectory() + File.separator + newName; } /** * 基本校验 * @param ftpClient {@link FTPClient} */ private static void baseValidate(FTPClient ftpClient) { if (ftpClient == null) { LOGGER.warn("【参数ftpClient为空】"); throw new NullArgumentException("ftpClient"); } } }
FTPManager.java代码:java
/** * */ package com.common.file; import java.io.File; import java.io.IOException; import java.io.InputStream; import javax.annotation.Resource; import org.apache.commons.lang.NullArgumentException; import org.apache.commons.lang.StringUtils; import org.apache.commons.net.ftp.FTPClient; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.common.DateUtils; import com.common.property.FileSystemProperties; /** * FTP文件管理器 * @author luolin * * @version $id:FTPManager.java,v 0.1 2015年11月16日 上午11:08:13 luolin Exp $ */ @Component("ftpManager") public class FTPManager { private static final Logger LOGGER = Logger.getLogger(FTPManager.class); /** file.system.properties文件内容映射类 */ @Resource(name = "fileSystemProperties") private FileSystemProperties fileSystemProperties; /** * 上传文件到FTP服务器 * @param file {@link File} * @return 文件上传到服务器的地址 */ public String upload(File file) { LOGGER.info("【上传文件到FTP服务器】"); if (file == null) { LOGGER.warn("【参数file为空】"); throw new NullArgumentException("file"); } FTPClient ftpClient = connectServer(fileSystemProperties.getProjectWorkingDirectory()); // 切换到子目录,按日期,天天建立一个目录 String subFolder = DateUtils.getNow(DateUtils.FORMAT_SHORT_FOLDER); FTPClientUtil.changeWorkingDirectory(subFolder, ftpClient); try { FTPClientUtil.upload(file, ftpClient); } catch (Exception e) { LOGGER.error("【文件上传失败】", e); throw new RuntimeException("文件上传失败"); } finally { FTPClientUtil.closeConnection(ftpClient); } String uploadedFilePath = fileSystemProperties.getProjectWorkingDirectory() + File.separator + subFolder + File.separator + file.getName(); LOGGER.info("【文件上传完成】uploadedFilePath = " + uploadedFilePath); return uploadedFilePath; } /** * 上传文件到FTP服务器 * @param file {@link File} * @param targetFolder 目标子目录 * @return 文件上传到服务器的地址 */ public String upload(File file, String targetFolder) { LOGGER.info("【上传文件到FTP服务器】targetFolder : " + targetFolder); if (file == null) { LOGGER.warn("【参数file为空】"); throw new NullArgumentException("file"); } FTPClient ftpClient = connectServer(fileSystemProperties.getProjectWorkingDirectory()); // 切换到子目录,按日期,天天建立一个目录 String subFolder = DateUtils.getNow(DateUtils.FORMAT_SHORT_FOLDER); FTPClientUtil.changeWorkingDirectory(targetFolder, ftpClient); FTPClientUtil.changeWorkingDirectory(subFolder, ftpClient); try { FTPClientUtil.upload(file, ftpClient); } catch (Exception e) { LOGGER.error("【文件上传失败】", e); throw new RuntimeException("文件上传失败"); } finally { FTPClientUtil.closeConnection(ftpClient); } String uploadedFilePath = fileSystemProperties.getProjectWorkingDirectory() + File.separator + targetFolder + File.separator + subFolder + File.separator + file.getName(); LOGGER.info("【文件上传完成】uploadedFilePath = " + uploadedFilePath); return uploadedFilePath; } /** * 链接服务器 * @return {@link FTPClient} */ private FTPClient connectServer(String workingDirectory) { FTPClient ftpClient = null; try { // 链接服务器,并切换到对应的项目文件存储目录 ftpClient = FTPClientUtil.connect(fileSystemProperties.getHost(), fileSystemProperties.getPort(), fileSystemProperties.getUsername(), fileSystemProperties.getPwd(), workingDirectory); } catch (Exception e) { LOGGER.error("【链接服务器失败】", e); throw new RuntimeException("链接服务器失败"); } return ftpClient; } /** * 链接服务器 * @return {@link FTPClient} */ private FTPClient connectServer() { FTPClient ftpClient = null; try { // 链接服务器,并切换到对应的项目文件存储目录 ftpClient = FTPClientUtil.connect(fileSystemProperties.getHost(), fileSystemProperties.getPort(), fileSystemProperties.getUsername(), fileSystemProperties.getPwd()); } catch (Exception e) { LOGGER.error("【链接服务器失败】", e); throw new RuntimeException("链接服务器失败"); } return ftpClient; } /** * 从服务器获取文件输入流 * @param fileName 文件路径和名称 * @return {@link InputStream} */ public InputStream getFileInputStream(String fileName) { LOGGER.info("【从服务器获取文件输入流】fileName = " + fileName); if (StringUtils.isBlank(fileName)) { LOGGER.warn("【参数fileName为空】"); throw new NullArgumentException("fileName"); } FTPClient ftpClient = connectServer(); try { return FTPClientUtil.retrieveFileStream(fileName, ftpClient); } catch (IOException e) { LOGGER.error("【获取文件流失败】", e); throw new RuntimeException("获取文件流失败"); } finally { FTPClientUtil.closeConnection(ftpClient); } } /** * 从文件服务器文件下载到应用服务器本地 * @param fileName 文件路径及名称 * @param tmpPath 临时文件存放目录 * @return 存储到临时文件目录的完整路径 */ public String downloadFileToLocal(String fileName, String tmpPath) { LOGGER.info("【从文件服务器文件下载到应用服务器本地】fileName = " + fileName); if (StringUtils.isBlank(fileName)) { LOGGER.warn("【参数fileName为空】"); throw new NullArgumentException("fileName"); } FTPClient ftpClient = connectServer(); String name = FileUtil.getFileNameNoPath(fileName); try { FTPClientUtil.downLoad(fileName, tmpPath + File.separator + name, ftpClient); } catch (Exception e) { LOGGER.error("【下载文件失败】", e); throw new RuntimeException("下载文件失败"); } finally { FTPClientUtil.closeConnection(ftpClient); } return tmpPath + File.separator + name; } /** * 删除文件服务器的文件 * @param fileName 文件在文件服务器的地址 * @throws IOException */ public void delete(String fileName) throws IOException { LOGGER.info("【删除文件服务器的文件】fileName = " + fileName); FTPClientUtil.delete(fileName, connectServer()); } /** * 文件拷贝 * @param sourceFile 源文件地址 * @param targetDir 目标文件夹 * @return 文件拷贝后的完整路径 */ public String copy(String sourceFile, String targetDir) { LOGGER.info("【文件拷贝】sourceFile = " + sourceFile + " , targetDir = " + targetDir); if (StringUtils.isBlank(sourceFile)) { LOGGER.warn("【参数sourceFile为空】"); throw new NullArgumentException("sourceFile"); } if (StringUtils.isBlank(targetDir)) { LOGGER.warn("【参数targetDir为空】"); throw new NullArgumentException("targetDir"); } try { return FTPClientUtil.copy(connectServer(), sourceFile, fileSystemProperties.getProjectWorkingDirectory() + File.separator + targetDir); } catch (Exception e) { LOGGER.error("【文件拷贝失败】", e); throw new RuntimeException("文件拷贝失败"); } } }
下面也给出我测试的时候,写的demo的代码spring
/** * */ package com.eay.ftp; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; /** * FTP客户端工具类示例 * @author ll * * @version $id:FTPClientExample.java,v 0.1 2015年11月13日 上午11:32:24 ll Exp $ */ public class FTPClientExample { /** FTP客户端实例 */ private FTPClient ftpClient; /** * 私有构造器 */ private FTPClientExample() { } /** * 内部类维护单例,防止并发问题 * @author luolin * * @version $id:FTPClientExample.java,v 0.1 2015年11月13日 下午2:34:08 luolin Exp $ */ private static class SingletonFactory { private static FTPClientExample instance = new FTPClientExample(); } /** * 获取实例 * @return {@link FTPClientExample} */ public static FTPClientExample getInstance() { return SingletonFactory.instance; } /** * 链接文件服务器 * @param addr 文件服务器地址 * @param port 端口 * @param username 用户名 * @param password 密码 * @throws Exception */ public void connect(String addr, int port, String username, String password) throws Exception { ftpClient = new FTPClient(); // 链接 ftpClient.connect(addr, port); // 登陆 ftpClient.login(username, password); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 判断文件服务器是否可用?? if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { ftpClient.disconnect(); } } /** * 链接文件服务器 * @param addr 文件服务器地址 * @param port 端口 * @param username 用户名 * @param password 密码 * @param workingDirectory 目标链接工做目录 * @throws Exception */ public void connect(String addr, int port, String username, String password, String workingDirectory) throws Exception { ftpClient = new FTPClient(); // 链接 ftpClient.connect(addr, port); // 登陆 ftpClient.login(username, password); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 判断文件服务器是否可用?? if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { ftpClient.disconnect(); } changeWorkingDirectory(workingDirectory); } /** * 关闭链接,使用完链接以后,必定要关闭链接,不然服务器会抛出 Connection reset by peer的错误 * @throws IOException */ public void closeConnection() throws IOException { ftpClient.disconnect(); } /** * 切换工做目录 * @param directory 目标工做目录 * @throws IOException */ public void changeWorkingDirectory(String directory) throws IOException { // 切换到目标工做目录 if (!ftpClient.changeWorkingDirectory(directory)) { ftpClient.makeDirectory(directory); ftpClient.changeWorkingDirectory(directory); } } /** * @param file 上传的文件或文件夹 * @throws Exception */ public void upload(File file) throws Exception { if (file == null) { throw new RuntimeException("上传文件为空"); } // 是文件,直接上传 if (!file.isDirectory()) { storeFile(new File(file.getPath())); return; } ftpClient.makeDirectory(file.getName()); ftpClient.changeWorkingDirectory(file.getName()); // 文件夹,递归上传全部文件 for (File item : file.listFiles()) { if (!item.isDirectory()) { storeFile(item); continue; } upload(item); ftpClient.changeToParentDirectory(); } } /** * 删除文件 * @param fileName 要删除的文件地址 * @return true/false * @throws Exception */ public boolean delete(String fileName) throws Exception { return ftpClient.deleteFile(fileName); } /** * 存储文件 * @param file {@link File} * @throws Exception */ private void storeFile(File file) throws Exception { FileInputStream input = new FileInputStream(file); ftpClient.storeFile(file.getName(), input); input.close(); } /** * 下载文件 * @param ftpFile 文件服务器上的文件地址 * @param dstFile 输出文件的路径和名称 * @throws Exception */ public void downLoad(String ftpFile, String dstFile) throws Exception { File file = new File(dstFile); FileOutputStream fos = new FileOutputStream(file); ftpClient.retrieveFile(ftpFile, fos); fos.flush(); fos.close(); } /** * 从文件服务器获取文件流 * @param ftpFile 文件服务器上的文件地址 * @return {@link InputStream} * @throws IOException */ public InputStream retrieveFileStream(String ftpFile) throws IOException { return ftpClient.retrieveFileStream(ftpFile); } public static void main(String[] args) throws Exception { FTPClientExample emp = FTPClientExample.getInstance(); String addr = "192.168.111.60"; int port = 21; String username = "admin"; String password = "admin"; emp.connect(addr, port, username, password); // 上传文件 emp.changeWorkingDirectory("bss"); emp.upload(new File("E:\\example.txt")); // 下载文件到指定目录 // emp.downLoad("bss\\example.txt", "E:\\example2.txt"); // 删除文件 // emp.delete("bss\\example.txt"); // 关闭链接,防止文件服务器抛出 Connection reset by peer的错误 emp.closeConnection(); } }
<!--ftp -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.3</version>
</dependency>apache