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

Delphi调用TDirectory.Copy未触发EInOutError,寻求可靠目录复制方案

问题核心原因

Delphi 内置的TDirectory.Copy方法对系统底层异常的封装存在缺陷,磁盘已满(Windows下错误码112、Linux下错误码28)的场景不会抛出EInOutError,部分版本甚至会静默终止复制流程,不向上抛出任何可捕获的有效异常,这是你遇到问题的根本原因。

可靠实现方案

推荐自行实现递归目录复制逻辑,逐文件/目录处理操作并捕获系统级错误,可精准识别磁盘满状态,无需事后全量校验目录树。

代码实现

首先定义返回状态枚举区分错误类型,再实现递归复制函数:

uses
  System.SysUtils, System.IOUtils;

type
  // 复制操作返回状态
  TCopyDirStatus = (
    cdsSuccess,    // 复制成功
    cdsDiskFull,   // 磁盘已满
    cdsAccessDenied, // 权限不足
    cdsOtherError   // 其他错误
  );

function CopyDirRecursive(const SrcPath, DstPath: string; out ErrorDetail: string): TCopyDirStatus;
const
  // 跨平台磁盘满错误码
  WIN_ERROR_DISK_FULL = 112;
  LINUX_ERROR_ENOSPC = 28;
var
  SrcFullPath, DstFullPath: string;
  SearchItem: TSearchRec;
begin
  Result := cdsSuccess;
  ErrorDetail := '';

  // 统一路径格式,补全末尾分隔符
  SrcFullPath := IncludeTrailingPathDelimiter(SrcPath);
  DstFullPath := IncludeTrailingPathDelimiter(DstPath);

  // 源目录合法性校验
  if not TDirectory.Exists(SrcFullPath) then
  begin
    ErrorDetail := '源目录不存在';
    Result := cdsOtherError;
    Exit;
  end;

  // 创建目标目录,捕获创建阶段的磁盘满异常
  try
    TDirectory.CreateDirectory(DstFullPath);
  except
    on E: EOSError do
    begin
      if (E.ErrorCode = WIN_ERROR_DISK_FULL) or (E.ErrorCode = LINUX_ERROR_ENOSPC) then
      begin
        Result := cdsDiskFull;
        ErrorDetail := '创建目标目录失败:磁盘空间不足';
        Exit;
      end;
      ErrorDetail := E.Message;
      Result := cdsOtherError;
      Exit;
    end;
    on E: Exception do
    begin
      ErrorDetail := E.Message;
      Result := cdsOtherError;
      Exit;
    end;
  end;

  // 遍历源目录下所有文件/子目录
  if FindFirst(SrcFullPath + '*.*', faAnyFile, SearchItem) = 0 then
  try
    repeat
      // 跳过当前目录和上级目录标记
      if (SearchItem.Name = '.') or (SearchItem.Name = '..') then
        Continue;

      if (SearchItem.Attr and faDirectory) <> 0 then
      begin
        // 递归复制子目录
        Result := CopyDirRecursive(SrcFullPath + SearchItem.Name, DstFullPath + SearchItem.Name, ErrorDetail);
        if Result <> cdsSuccess then Exit;
      end
      else
      begin
        // 复制单个文件,捕获写入阶段异常
        try
          TFile.Copy(SrcFullPath + SearchItem.Name, DstFullPath + SearchItem.Name, True);
        except
          on E: EOSError do
          begin
            if (E.ErrorCode = WIN_ERROR_DISK_FULL) or (E.ErrorCode = LINUX_ERROR_ENOSPC) then
            begin
              Result := cdsDiskFull;
              ErrorDetail := Format('复制文件%s失败:磁盘空间不足', [SrcFullPath + SearchItem.Name]);
              Exit;
            end;
            ErrorDetail := E.Message;
            Result := cdsOtherError;
            Exit;
          end;
          on E: Exception do
          begin
            ErrorDetail := E.Message;
            Result := cdsOtherError;
            Exit;
          end;
        end;
      end;
    until FindNext(SearchItem) <> 0;
  finally
    FindClose(SearchItem);
  end;
end;

调用示例

procedure CopyBigDirWithSubdirs;
var
  ErrInfo: string;
  CopyResult: TCopyDirStatus;
begin
  CopyResult := CopyDirRecursive(SrcPath, DstPath, ErrInfo);
  case CopyResult of
    cdsSuccess:
      begin
        // 处理复制成功逻辑
      end;
    cdsDiskFull:
      begin
        // 精准处理磁盘满场景的业务逻辑
      end;
    cdsAccessDenied:
      begin
        // 处理权限不足逻辑
      end;
    cdsOtherError:
      begin
        // 处理其他错误,可输出ErrInfo排查问题
      end;
  end;
end;

可选优化方案(Windows平台专属)

如果仅在Windows环境运行,可以直接调用系统SHFileOperationAPI实现目录复制,这是内核级实现,大目录复制效率比自行实现的递归逻辑更高,也能准确返回磁盘满的错误码。


内容的提问来源于stack exchange,提问作者Yurii

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 19:06:03