YunaiV/yudao-cloud · error · FtpException
上传文件到目标目录 ({}) 失败
Error message
上传文件到目标目录 ({}) 失败 What it means
FtpFileClient.upload throws hutool FtpException after ftp.upload(dir, fileName, stream) returns false — meaning the FTP store refused the write. Hutool's Ftp returns a boolean instead of throwing, so this is the generic 'upload failed' signal: causes include permission denied on the target dir, missing/invalid basePath in the file client config, disk full, read-only or quota-limited FTP account, or a session that silently died despite reconnectIfTimeout().
Source
Thrown at yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java:56
protected void doInit() {
// 初始化 Ftp 对象:https://gitee.com/zhijiantianya/yudao-cloud/pulls/207/
FtpConfig ftpConfig = new FtpConfig(config.getHost(), config.getPort(), config.getUsername(), config.getPassword(),
CharsetUtil.CHARSET_UTF_8, null, null);
ftpConfig.setConnectionTimeout(CONNECTION_TIMEOUT);
ftpConfig.setSoTimeout(SO_TIMEOUT);
this.ftp = new Ftp(ftpConfig, FtpMode.valueOf(config.getMode()));
}
@Override
public String upload(byte[] content, String path, String type) {
// 执行写入
String filePath = getFilePath(path);
String fileName = FileUtil.getName(filePath);
String dir = StrUtil.removeSuffix(filePath, fileName);
reconnectIfTimeout();
boolean success = ftp.upload(dir, fileName, new ByteArrayInputStream(content)); // 不需要主动创建目录,ftp 内部已经处理(见源码)
if (!success) {
throw new FtpException(StrUtil.format("上传文件到目标目录 ({}) 失败", filePath));
}
// 拼接返回路径
return super.formatFileUrl(config.getDomain(), path);
}
@Override
public void delete(String path) {
String filePath = getFilePath(path);
reconnectIfTimeout();
ftp.delFile(filePath);
}
@Override
public byte[] getContent(String path) {
String filePath = getFilePath(path);
String fileName = FileUtil.getName(filePath);
String dir = StrUtil.removeSuffix(filePath, fileName);
ByteArrayOutputStream out = new ByteArrayOutputStream();View on GitHub (pinned to 477be9dd49)
Solutions
- Verify the FTP account can write to the configured basePath (test with an FTP client or hutool Ftp.store manually)
- Match config mode to the network: Passive behind NAT/firewall, Active only on plain networks
- Check server-side disk space, quotas and SELinux/permission on the target dir
- Confirm config domain/basePath/username/password in infra file config are all correct (re-test connection in the admin UI)
Example fix
# before: active mode behind NAT mode: Active # after mode: Passive # matches firewall/NAT; verify write rights: # lftp -u user,pass host -e "cd /upload && put test.txt; quit"
Defensive patterns
Strategy: retry
Validate before calling
// pre-flight: verify write access to basePath
try (Ftp probe = new Ftp(host, port, user, pass)) {
if (!probe.cd(basePath) ) throw new IllegalStateException("basePath 不存在");
if (!probe.upload(basePath, ".probe", new ByteArrayInputStream(new byte[1])))
throw new IllegalStateException("FTP 目录不可写");
probe.delFile(basePath + "/.probe");
} Try / catch
try {
return ftpFileClient.upload(content, path, type);
} catch (FtpException e) {
log.warn("FTP upload failed for {}, retrying once", path, e);
return ftpFileClient.upload(content, path, type); // transient data-channel failures often clear
} Prevention
- Use Passive mode behind NAT/firewalls; Active only on open networks
- Verify FTP write permissions during file-config save (test upload)
- Monitor disk space on the FTP host
- Keep reconnectIfTimeout paths covered with integration tests against a test FTP server
When it happens
Trigger: Uploading a file through infra file config #N of type FTP where the FTP user lacks write permission on basePath; basePath pointing to a non-writable directory; passive/port mode mismatch (FtpMode Passive/Active not matching the firewall) causing failed data connections; server out of space.
Common situations: Wrong FTP credentials/permissions after server migration; config mode left as Active behind NAT so the data channel fails; basePath with a typo; SELinux blocking writes on the FTP host.
Related errors
AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14).
Data as JSON: /api/errors/1adf1f6619fbbe27.
Report an issue: GitHub.