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

读取Zip文件时进程无异常突然退出,finally块未执行问题排查

问题描述

我尝试从SFTP服务器读取一系列Zip文件,每个文件中包含一个固定宽度的数据文件。将这些文件下载到临时文件后尝试解压,但最后一步始终无法成功:进程没有报错、没有日志输出,直接突然退出,绕过了try...catch...finally块。已尝试使用多个不同的Zip库,但均出现相同行为。临时文件可通过Windows资源管理器正常打开。

相关C#代码如下:

public async Task ProcessFiles(CommandLineOptions options)
{
    try
    {
        var privateKey = new PrivateKeyFile(options.PrivateKeyPath, options.PrivateKeyPassword);
        _logger.LogDebug("Loaded up the private key for {username}.", options.LoginUsername);

        var connection = new ConnectionInfo(options.Host, options.LoginUsername,
            new PrivateKeyAuthenticationMethod(options.LoginUsername, privateKey));
        _logger.LogDebug("Created a connection object for SFTP to {host}.", options.Host);

        using var client = new SftpClient(connection);
        client.Connect();
        _logger.LogDebug("Connected to {host}.", options.Host);

        var files = client.ListDirectory(".").ToList();
        _logger.LogTrace("{numFiles} file(s) found in the directory.", files.Count);

        foreach (var file in files.Where(fn =>
                     fn.FullName.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)))
        {
            string localFile = null;
            try
            {
                _logger.LogDebug("Processing file {fileName}", file.FullName);
                localFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.ZIP");

                _logger.LogTrace("Writing to {tempPath}", localFile);
                await using var downStream = File.OpenWrite(localFile);
                client.DownloadFile(file.Name, downStream);
                _logger.LogDebug("File {fileName} downloaded.", localFile);
                await downStream.DisposeAsync();

                _logger.LogTrace("Opening {fileName} for read access...", localFile);
                await using var upStream = File.Open(localFile,FileMode.OpenOrCreate);

                _logger.LogTrace("File has been opened for streaming, reading in ZipArchive...");
                
                var archive = ZipArchive.Open(upStream, new ReaderOptions
                {
                    LeaveStreamOpen = true,
                    LookForHeader = true,
                    DisableCheckIncomplete = false
                });
                _logger.LogTrace("Archive file successfully opened.");

                var reader = archive.ExtractAllEntries();

                foreach (var entry in archive.Entries)
                {
                    _logger.LogTrace("Opening file {compressedFile} in the ZIP file {fileName}...", entry.Key,
                        file.FullName);
                    await using var inStream = reader.OpenEntryStream();
                    foreach (var record in _recordLoader.GetRecords(inStream))
                    {
                        _logger.LogTrace("Record parsed for file type {type} sequence {sequence}...", record.Type,
                            record.SequenceNumber);
                        record.TenantId = options.TenantId;
                        await _dbContext.Trades.AddAsync(record);
                        _logger.LogTrace("Adding trade {sequence} in file {zippedFile} to Trades...",
                            record.SequenceNumber, entry.Key);
                    }

                    var added = await _dbContext.SaveChangesAsync();
                    _logger.LogInformation("Loaded {recordsAdded} records from the zipped file {zippedFile}.",
                        added,
                        entry.Key);
                }

            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failure downloading file {fileName}.", file.FullName);
            }
            finally
            {
                _logger.LogDebug("Deleting local file {localFile}", localFile);
                if (File.Exists(localFile)) File.Delete(localFile);
                _logger.LogDebug("File {localFile} deleted.");
            }
        }

        _logger.LogInformation("Pershing file processing is complete.");
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Process to load Pershing files failed. The exception is included.");
    }
}

运行日志如下:

dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401]
      An 'IServiceProvider' was created for internal use by Entity Framework.
dbug: AppRunner[0]
      Loaded up the private key for mycompanysftp.pershing.importer.
dbug: AppRunner[0]
      Created a connection object for SFTP to mycompanysftp.blob.core.windows.net.
dbug: AppRunner[0]
      Connected to mycompanysftp.blob.core.windows.net.
trce: AppRunner[0]
      5 file(s) found in the directory.
dbug: AppRunner[0]
      Processing file /GSDE20221011.ZIP
trce: AppRunner[0]
      Writing to C:\Users\jholo\AppData\Local\Temp\10826b52fba94b7db7844958dd3c2e9e.ZIP
dbug: AppRunner[0]
      File C:\Users\jholo\AppData\Local\Temp\10826b52fba94b7db7844958dd3c2e9e.ZIP downloaded.
trce: AppRunner[0]
      Opening C:\Users\jholo\AppData\Local\Temp\10826b52fba94b7db7844958dd3c2e9e.ZIP for read access...
trce: AppRunner[0]
      File has been opened for streaming, reading in ZipArchive...

D:\Repos\jholovacs\mycompany\mycompany.Data.Pershing.TestApp\bin\Debug\net6.0\mycompany.Data.Pershing.Importer.exe (process 50628) exited with code 0.

请问这是什么原因?为何finally块会被绕过?为何没有抛出异常?我哪里操作错了?


分析与解决

核心原因

进程无预警退出且跳过异常处理逻辑,大概率是非托管代码抛出的致命异常(如AccessViolationException),这类异常不受CLR托管异常机制控制,会直接终止进程,不会进入try/catch/finally块。结合你的场景,触发点集中在Zip文件读取阶段,可能和文件流状态异常、Zip格式兼容问题有关。

具体修复步骤

1. 修正文件流操作的错误

(1) 确保下载文件完全写入磁盘

client.DownloadFile是同步方法,写入流后需强制刷新缓冲区,再释放流,避免文件未完全写入就被读取:

client.DownloadFile(file.Name, downStream);
downStream.Flush(); // 强制将缓冲区内容写入磁盘
await downStream.DisposeAsync();

(2) 正确打开Zip文件流

文件已下载完成,无需使用FileMode.OpenOrCreate,改用FileMode.Open并指定只读权限,避免流状态异常:

await using var upStream = File.Open(localFile, FileMode.Open, FileAccess.Read);

(3) 正确释放ZipArchive资源

ZipArchive实现了IDisposable,需用using包裹,确保非托管资源被正确释放:

using var archive = ZipArchive.Open(upStream, new ReaderOptions
{
    LeaveStreamOpen = true,
    LookForHeader = true,
    DisableCheckIncomplete = false
});

2. 确保异步任务被完整等待

如果是控制台程序,主入口必须等待ProcessFiles任务完成,否则进程会提前退出:

static async Task Main(string[] args)
{
    var options = ParseCommandLineOptions(args);
    var appRunner = new AppRunner();
    await appRunner.ProcessFiles(options);
}

3. 调试捕获致命异常

添加全局异常处理,捕获非托管致命异常:

// 在程序入口处添加
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
    var ex = e.ExceptionObject as Exception;
    _logger.LogCritical(ex, "未处理的致命异常,进程将终止");
    Console.WriteLine($"致命异常:{ex?.Message}");
};

同时在Visual Studio调试时,开启捕获所有异常(调试→窗口→异常设置,勾选"Common Language Runtime Exceptions"下的所有选项),可直接定位触发进程终止的异常点。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 12:40:50