SpringBoot使用hutool操作FTP的详细过程
作者:涛哥是个大帅比
在使用SpringBoot结合hutool操作FTP时,遇到防火墙导致上传文件大小为0kb的问题,通过设置FTP为被动模式解决,本文详细解析了FTP的主动模式和被动模式的工作原理、安全性及适用场景,帮助理解FTP的连接方式和解决网络限制问题
项目场景:
<dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.9.0</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.15</version> </dependency>
实现步骤:
1、引入依赖
<dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.9.0</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.15</version> </dependency>
2、yml配置
ftp: # 服务器地址 host: 127.0.0.1 # 端口号 port: 21 # 用户名 userName: test # 密码 password: test
3、Config配置类
我这里用的是static修饰的变量,方便工具类调用。
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; /** * ftp配置 */ @Configuration public class FtpConfig { /** * 服务器地址 */ private static String host; /** * 端口 */ private static Integer port; /** * 用户名 */ private static String userName; /** * 密码 */ private static String password; @Value("${ftp.host}") public void setHost(String host) { FtpConfig.host = host; } public static String getHost() { return host; } @Value("${ftp.port}") public void setPort(Integer port) { FtpConfig.port = port; } public static Integer getPort() { return port; } @Value("${ftp.userName}") public void setUserName(String userName) { FtpConfig.userName = userName; } public static String getUserName() { return userName; } @Value("${ftp.password}") public void setPassword(String password) { FtpConfig.password = password; } public static String getPassword() { return password; } }
4、 FtpUtil工具类
import cn.hutool.core.io.FileUtil; import cn.hutool.extra.ftp.Ftp; import cn.hutool.extra.ftp.FtpMode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.net.ftp.FTPFile; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * FTP服务工具类 */ @Slf4j public class FtpUtil { /** * 获取 FTPClient对象 */ private static Ftp getFTPClient() { try { if(StringUtils.isBlank(FtpConfig.getHost()) || FtpConfig.getPort() == null || StringUtils.isBlank(FtpConfig.getUserName()) || StringUtils.isBlank(FtpConfig.getPassword())) { throw new RuntimeException("ftp配置信息不能为空"); } Ftp ftp = new Ftp(FtpConfig.getHost(),FtpConfig.getPort(),FtpConfig.getUserName(),FtpConfig.getPassword()); //设置为被动模式,防止防火墙拦截 ftp.setMode(FtpMode.Passive); return ftp; } catch (Exception e) { e.printStackTrace(); log.error("获取ftp客户端异常",e); throw new RuntimeException("获取ftp客户端异常:"+e.getMessage()); } } /** * 下载ftp服务器上的文件到本地 * @param remoteFile ftp上的文件路径 * @param localFile 输出的目录,使用服务端的文件名 */ public static void download(String remoteFile, String localPath) { if(StringUtils.isBlank(remoteFile) || StringUtils.isBlank(localPath)) { return; } Ftp ftp = getFTPClient(); try { if(!FileUtil.exist(localPath)){ FileUtil.mkdir(localPath); } File lFile = FileUtil.file(localPath); ftp.download(remoteFile, lFile); } catch (Exception e) { e.printStackTrace(); log.error("FTP文件下载异常",e); } finally { //关闭连接 try { if(ftp != null) ftp.close(); } catch (IOException e) { throw new RuntimeException(e); } } } /** * 本地文件上传到ftp服务器上 * @param remoteDir 上传的ftp目录 * @param remoteFileName 保存到ftp服务器上的名称 * @param localFile 本地文件全名称 */ public static boolean upload(String remoteDir, String remoteFileName, String localFile) { if(StringUtils.isBlank(remoteDir) || StringUtils.isBlank(remoteFileName) || StringUtils.isBlank(localFile)) { return false; } Ftp ftp = getFTPClient(); try { File lFile = FileUtil.file(localFile); if(!lFile.exists()) { log.error("本地文件不存在"); return false; } if(StringUtils.isBlank(remoteFileName)) { return ftp.upload(remoteDir, lFile); } else { return ftp.upload(remoteDir, remoteFileName, lFile); } } catch (Exception e) { e.printStackTrace(); log.error("文件上传FTP异常",e); return false; } finally { //关闭连接 try { if(ftp != null) ftp.close(); } catch (IOException e) { throw new RuntimeException(e); } } } /** * 删除FTP服务器中的文件 * @param remoteFile ftp上的文件路径 */ public static boolean delFile(String remoteFile) { if(StringUtils.isBlank(remoteFile)) { return false; } Ftp ftp = getFTPClient(); try { return ftp.delFile(remoteFile); } catch (Exception e) { e.printStackTrace(); log.error("删除FTP服务器中的文件异常",e); return false; } finally { //关闭连接 try { if(ftp != null) ftp.close(); } catch (IOException e) { throw new RuntimeException(e); } } } /** * 遍历某个目录下所有文件,不会递归遍历 * @param path 目录 */ public static List<String> listFile(String path) { List<String> listFile = new ArrayList<>(); Ftp ftp = getFTPClient(); try { FTPFile[] ftpFiles = ftp.lsFiles(path); for (int i = 0; i < ftpFiles.length; i++) { FTPFile ftpFile = ftpFiles[i]; if(ftpFile.isFile()){ listFile.add(ftpFile.getName()); } } return listFile; } catch (Exception e) { e.printStackTrace(); log.error("遍历某个目录下所有文件异常",e); return null; } finally { //关闭连接 try { if(ftp != null) ftp.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
5、测试
@RestController @RequestMapping("/test") public class TestController { @RequestMapping(value = "ftpTest", method = RequestMethod.GET) public void ftpTest() { //上传文件到ftp FtpUtil.upload("opt/upload","APP_RELATION.sql", "F:/APP_RELATION.sql"); //下载远程文件 FtpUtil.download("opt/upload/APP_RELATION.sql", "D:/"); //删除远程文件 FtpUtil.delFile("opt/upload/APP_RELATION.sql"); } }
总结:
上传的时候碰到一个问题,就是本地开启防火墙时,上传的文件大小是0kb,必须要关了防火墙才正常,后来FTP模式设置为“被动模式”解决。
//设置为被动模式,防止防火墙拦截 ftp.setMode(FtpMode.Passive);
主动模式(Active Mode):
- 工作原理:客户端在本地打开一个非特权端口(通常大于1023),并通过这个端口发送PORT命令给服务器,告诉服务器客户端用于数据传输的端口号。然后,服务器使用其20端口(数据端口)主动连接到客户端指定的端口进行数据传输。
- 安全性:由于服务器需要主动连接到客户端的端口,这可能引发一些安全问题,特别是当客户端位于防火墙或NAT设备后面时。
- 适用场景:适用于客户端位于可以接受入站连接的网络环境,且没有防火墙或NAT设备限制的场景。
被动模式(Passive Mode):
- 工作原理:客户端发送PASV命令给服务器,服务器在本地打开一个端口(通常是高位的非特权端口),并通过PASV命令的响应告诉客户端这个端口号。然后,客户端主动连接到服务器指定的这个端口进行数据传输。
- 安全性:由于客户端主动连接到服务器,这种模式更适合于客户端位于防火墙或NAT设备后面的场景,因为这些设备通常允许出站连接但限制入站连接。
- 适用场景: 特别适用于网络环境不稳定、存在防火墙或NAT设备的场景。
到此这篇关于SpringBoot使用hutool操作FTP的文章就介绍到这了,更多相关SpringBoot使用hutool内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!