You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring FTP上传文件报错450:请求的文件操作未执行咨询

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 17:16:10