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

代码中SQLite备份文件被锁定,无法上传该如何排查?

SQLite备份文件被锁定导致上传失败的原因及解决办法

我通过以下代码备份SQLite数据库并上传至Azure Blob容器,但执行上传时总是报错文件被其他进程占用,直到应用关闭才解锁。

备份与上传代码:

public static async Task<string> CreateBackupAsync()
{
    // 生成带时间戳的备份文件名
    var timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
    var backupFileName = $"backup_{timestamp}.db";
    var backupFilePath = Path.Combine("C:\\Backups", backupFileName);

    // 确保备份目录存在
    Directory.CreateDirectory("C:\\Backups");

    using (var location = new SqliteConnection($"Data Source={LocalDbPath}"))
    using (var destination = new SqliteConnection($"Data Source={backupFilePath}"))
    {
        await location.OpenAsync();
        await destination.OpenAsync();
        location.BackupDatabase(destination);
    }

    return Path.GetFullPath(backupFilePath);
}

public static async Task UploadFileAsync(string filePath)
{
    var containerClient = new BlobContainerClient(new Uri(ConnectionString));
    var blobClient = containerClient.GetBlobClient($"database/backup/{Path.GetFileName(filePath)}");

    try
    {
        // 将文件上传至Azure Blob存储
        await using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None);
        await blobClient.UploadAsync(fs, true);
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"Exception: {ex.Message}");
        Debug.WriteLine($"Stack Trace: {ex.StackTrace}");
    }
}

执行逻辑:

var backup = await DatabaseSynchronizer.CreateBackupAsync();
await DatabaseSynchronizer.UploadFileAsync(backup);

报错信息:

System.IO.IOException: 'The process cannot access the file 'C:...\Debug\net8.0-windows10.0.19041.0\backup_20240702114910.db' because it is being used by another process.'

问题原因

查看BackupDatabase的底层实现后发现,连接关闭逻辑存在漏洞:

  • 该方法仅在自身主动打开目标连接时(即flag为true),才会在finally块中关闭连接。
  • 但在CreateBackupAsync中,我们提前手动调用了await destination.OpenAsync();,导致flag为false,最终目标连接(指向备份文件的SqliteConnection)没有被关闭,SQLite因此一直持有备份文件的锁,后续FileStream无法访问。

解决办法

方案一:移除手动打开目标连接的代码

让BackupDatabase方法自行处理目标连接的打开与关闭:

public static async Task<string> CreateBackupAsync()
{
    // 生成带时间戳的备份文件名
    var timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
    var backupFileName = $"backup_{timestamp}.db";
    var backupFilePath = Path.Combine("C:\\Backups", backupFileName);

    // 确保备份目录存在
    Directory.CreateDirectory("C:\\Backups");

    using (var location = new SqliteConnection($"Data Source={LocalDbPath}"))
    using (var destination = new SqliteConnection($"Data Source={backupFilePath}"))
    {
        await location.OpenAsync();
        // 移除手动打开目标连接的代码
        location.BackupDatabase(destination);
    }

    return Path.GetFullPath(backupFilePath);
}

此时BackupDatabase会检测到目标连接未打开,自动打开并在finally中关闭,确保备份文件的锁被释放。

方案二:手动确保目标连接关闭

如果需要保留手动打开连接的逻辑,在备份完成后主动关闭目标连接:

public static async Task<string> CreateBackupAsync()
{
    // 生成带时间戳的备份文件名
    var timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss");
    var backupFileName = $"backup_{timestamp}.db";
    var backupFilePath = Path.Combine("C:\\Backups", backupFileName);

    // 确保备份目录存在
    Directory.CreateDirectory("C:\\Backups");

    using (var location = new SqliteConnection($"Data Source={LocalDbPath}"))
    using (var destination = new SqliteConnection($"Data Source={backupFilePath}"))
    {
        await location.OpenAsync();
        await destination.OpenAsync();
        try
        {
            location.BackupDatabase(destination);
        }
        finally
        {
            // 手动关闭目标连接
            await destination.CloseAsync();
        }
    }

    return Path.GetFullPath(backupFilePath);
}

可选优化:放宽文件共享权限

上传文件时,将FileShare.None改为FileShare.Read,允许其他进程同时读取文件,减少锁冲突概率:

await using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 14:41:02