Spring FTP上传文件报错450:请求的文件操作未执行咨询
问题现象
通过Java应用连接远程FTP服务器上传文件时,触发如下错误:
Failed to write to 'testfile.txt'. Server replied with: 450 Requested file action not taken
本地使用FTP命令可正常上传,但Java应用无法完成操作。
相关代码
DynamicSessionFactory.java
public class DynamicFtpSessionFactory { private final ApplicationContext context; public DynamicFtpSessionFactory(@NonNull final ApplicationContext context) { this.context = context; } public void registerFtpSessionFactory(@NonNull final String beanName, @NonNull final String host, final int port, @NonNull final String username, @NonNull final String password) { final BeanDefinitionRegistry registry = (BeanDefinitionRegistry) ((AnnotationConfigApplicationContext) context).getBeanFactory(); final BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(DefaultFtpSessionFactory.class); builder.addPropertyValue("host", host); builder.addPropertyValue("port", port); builder.addPropertyValue("username", username); builder.addPropertyValue("password", password); builder.addPropertyValue("fileType", FTP.ASCII_FILE_TYPE); registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); // Optionally wrap in a CachingSessionFactory final DefaultFtpSessionFactory ftpSessionFactory = (DefaultFtpSessionFactory) context.getBean(beanName); registry.registerBeanDefinition(beanName, BeanDefinitionBuilder.genericBeanDefinition(CachingSessionFactory.class) .addConstructorArgValue(ftpSessionFactory) .getBeanDefinition()); } }
Service代码
public void uploadFileForGroupId(@NonNull final String groupId, @NonNull final Resource reportFile) throws IOException { final CachingSessionFactory session = getFtpSessionFactory(groupId); // Upload report file to FTP server try (InputStream inputStream = reportFile.getInputStream()) { session.getSession().write(inputStream, reportFile.getFilename()); log.info("File {} uploaded successfully for group Id {}.", reportFile.getFilename(), groupId); } catch (IOException e) { log.error("Failed to upload file: ", e); } finally { session.getSession().close(); } } private CachingSessionFactory getFtpSessionFactory(@NonNull final String groupId) { final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.refresh(); // Initialize the context without any configuration class final String ftpBeanName = groupId + "Caching"; if (context.containsBean(ftpBeanName)) { return context.getBean(ftpBeanName, CachingSessionFactory.class); } final DynamicFtpSessionFactory dynamicFtpSessionFactory = new DynamicFtpSessionFactory(context); final AnfDirectoryEntity anfDirectory = getFtpConnectionDetails(groupId); dynamicFtpSessionFactory.registerFtpSessionFactory(ftpBeanName, anfDirectory.getSftpConnection().getSftpServer(), FTP_PORT, anfDirectory.getSftpConnection().getSftpUser(), anfDirectory.getSftpConnection().getSftpPassword()); return context.getBean(ftpBeanName, CachingSessionFactory.class); }
问题分析与解决
1. 文件传输模式不匹配
代码中设置fileType为FTP.ASCII_FILE_TYPE,但如果上传的是二进制文件(如图片、压缩包、非纯文本文件),ASCII模式会破坏文件结构,服务器可能因此拒绝写入。本地FTP命令默认通常使用二进制模式,这是核心差异点。
修复:
将文件类型改为二进制模式:
builder.addPropertyValue("fileType", FTP.BINARY_FILE_TYPE);
2. Spring上下文重复创建导致Bean丢失
getFtpSessionFactory方法每次都会新建AnnotationConfigApplicationContext并刷新,这会导致之前注册的Bean全部丢失——你判断context.containsBean(ftpBeanName)时用的是空上下文,所以每次都会重复创建SessionFactory,引发连接管理异常。
修复:
复用Spring上下文,将其作为Service类的成员变量初始化一次:
// Service类中定义成员变量 private final AnnotationConfigApplicationContext context; // 构造方法初始化 public YourService() { this.context = new AnnotationConfigApplicationContext(); this.context.refresh(); } private CachingSessionFactory getFtpSessionFactory(@NonNull final String groupId) { final String ftpBeanName = groupId + "Caching"; if (context.containsBean(ftpBeanName)) { return context.getBean(ftpBeanName, CachingSessionFactory.class); } // 后续注册逻辑保持不变 }
3. 会话关闭方式破坏缓存机制
finally块中直接调用session.getSession().close(),但CachingSessionFactory返回的会话属于连接池管理,手动关闭会破坏缓存逻辑,导致连接泄漏或无效会话复用。
修复:
使用try-with-resources自动管理会话,让缓存池正确回收连接:
try (FtpSession ftpSession = session.getSession()) { ftpSession.write(inputStream, reportFile.getFilename()); log.info("File {} uploaded successfully for group Id {}.", reportFile.getFilename(), groupId); } catch (IOException e) { log.error("Failed to upload file: ", e); }
4. 工作目录权限验证
本地FTP命令可能默认进入了有权限的特定目录,但Java代码可能停留在无写入权限的根目录。
验证修复:
上传前切换到目标目录:
ftpSession.changeWorkingDirectory("/指定上传目录");
也可先调用ftpSession.listNames()查看当前目录文件,确认路径是否正确。
内容的提问来源于stack exchange,提问作者pri05

