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
相关产品推荐
相关产品推荐

