网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
using System.Buffers;
using System.IO.Pipelines;
using System.Security.Cryptography;
using Dpz.Core.MongodbAccess;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;

namespace Dpz.Core.Backup;

/// <summary>
/// 数据还原
/// </summary>
public class RestoreManager
{
    private readonly string _zipPassword;
    private readonly IRepositoryAny _repositoryAny;
    private readonly string _backupFilePath;
    private readonly string _restorePath;
    private readonly ILogger<RestoreManager> _logger;
    private readonly DirectoryInfo _restoreDirectory;

    /// <summary>
    /// 数据还原构造函数
    /// </summary>
    /// <param name="connectionString">要还原数据库的连接字符串</param>
    /// <param name="zipPassword">备份文件的解压密码</param>
    /// <param name="backupFilePath">备份文件的路径</param>
    /// <param name="loggerFactory">logger factory</param>
    /// <exception cref="Exception"></exception>
    public RestoreManager(
        string? connectionString,
        string zipPassword,
        string backupFilePath,
        ILoggerFactory loggerFactory
    )
    {
        if (string.IsNullOrEmpty(connectionString))
        {
            throw new Exception("连接字符串不能为空");
        }

        if (string.IsNullOrEmpty(zipPassword))
        {
            throw new Exception("解压缩密码不能为空");
        }

        if (string.IsNullOrEmpty(backupFilePath) || !File.Exists(backupFilePath))
        {
            throw new Exception("备份文件不存在");
        }
        
        _zipPassword = zipPassword;
        _repositoryAny = new RepositoryAny(connectionString);
        _logger = loggerFactory.CreateLogger<RestoreManager>();
        var database = _repositoryAny.Database.DatabaseNamespace.DatabaseName;

        _logger.LogInformation("当前还原数据库:{Database}", database);

        var folderName = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        var restorePath = Path.Combine("restore", database, folderName);
        
        var directoryInfo = new DirectoryInfo(backupFilePath);
        _backupFilePath = directoryInfo.FullName;
        _logger.LogInformation("当前还原文件:{BackupFilePath}", backupFilePath);
        _restoreDirectory = new DirectoryInfo(restorePath);
        if (!_restoreDirectory.Exists)
        {
            _restoreDirectory.Create();
            _logger.LogInformation("创建还原目录:{BackupPath}", restorePath);
        }
        
        _restorePath = _restoreDirectory.FullName;
    }

    
    /// <summary>
    /// 还原
    /// </summary>
    public async Task RestoreAsync()
    {
        ExtractZipFile(_backupFilePath, _restorePath, _zipPassword);

        var files = _restoreDirectory.GetFiles("*.bson");
        // await RestoreCollectionAsync(files[0]);
        // return;

        await Parallel.ForEachAsync(files, async (file, _) => { await RestoreCollectionAsync(file); });

        _logger.LogInformation("数据库还原完毕");
    }

    private async Task RestoreCollectionAsync(FileInfo file)
    {
        var data = await ReadBackupDataAsync(file);
        if (data.Count == 0)
            return;
        var collectionName = Path.GetFileNameWithoutExtension(file.FullName);
        await _repositoryAny.DeleteAllAsync(collectionName);
        await _repositoryAny.InsertAsync(collectionName, data);
    }

    private async Task<List<BsonDocument>> ReadBackupDataAsync(FileInfo fileInfo)
    {
        if (!fileInfo.Exists)
        {
            return [];
        }

        var data = new List<BsonDocument>();
        _logger.LogInformation("读取备份数据文件:{FilePath}", fileInfo.FullName);

        try
        {
            await using var fileStream = fileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
            using var reader = new BsonBinaryReader(fileStream);
            while (!reader.IsAtEndOfFile())
            {
                var bsonType = reader.ReadBsonType();
                if (bsonType == BsonType.EndOfDocument)
                {
                    break;
                }

                var document = ReadBsonDocument(reader);
                data.Add(document);
            }
        }
        catch (Exception e)
        {
            _logger.LogError(e, "读取备份数据文件失败:{FilePath}", fileInfo.FullName);
            return data;
        }

        _logger.LogDebug("读取备份数据文件完成:{@Data}", data);

        return data;
    }

    private static BsonDocument ReadBsonDocument(BsonBinaryReader reader)
    {
        var context = BsonDeserializationContext.CreateRoot(reader);
        var bsonDocumentSerializer = BsonSerializer.LookupSerializer<BsonDocument>();
        return bsonDocumentSerializer.Deserialize(context);
    }

    /// <summary>
    /// 解压缩zip
    /// </summary>
    /// <param name="archivePath">压缩文件路径</param>
    /// <param name="outFolder"></param>
    /// <param name="password"></param>
    private static void ExtractZipFile(string archivePath, string outFolder, string password)
    {
        using var fsInput = File.OpenRead(archivePath);
        using var zf = new ZipFile(fsInput);
        if (!string.IsNullOrEmpty(password))
        {
            zf.Password = password;
        }

        foreach (ZipEntry zipEntry in zf)
        {
            var entryFileName = zipEntry.Name;

            var fullZipToPath = Path.Combine(outFolder, entryFileName);
            var directoryName = Path.GetDirectoryName(fullZipToPath);
            if (directoryName is { Length: > 0 })
            {
                Directory.CreateDirectory(directoryName);
            }

            var buffer = new byte[4096];

            using var zipStream = zf.GetInputStream(zipEntry);
            using Stream fsOutput = File.Create(fullZipToPath);
            StreamUtils.Copy(zipStream, fsOutput, buffer);
        }
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

上述代码实现了一个数据还原管理器 RestoreManager,用于从压缩的备份文件中恢复 MongoDB 数据库的内容。以下是代码的主要功能和结构的详细解释:

1. 类和构造函数

  • RestoreManager 类: 该类负责处理数据库的还原操作。
  • 构造函数:
    • 接受连接字符串、压缩文件的解压密码、备份文件路径和日志工厂作为参数。
    • 验证输入参数的有效性(如连接字符串、密码和备份文件是否存在)。
    • 初始化数据库连接、日志记录器,并设置还原目录的路径。

2. 还原操作

  • RestoreAsync 方法:
    • 该方法是还原操作的入口点。
    • 首先调用 ExtractZipFile 方法解压缩备份文件到指定的还原目录。
    • 获取还原目录中所有的 BSON 文件(MongoDB 数据的备份格式)。
    • 使用 Parallel.ForEachAsync 并行处理每个 BSON 文件,调用 RestoreCollectionAsync 方法进行还原。

3. 还原集合

  • RestoreCollectionAsync 方法:
    • 读取指定 BSON 文件中的数据。
    • 如果数据不为空,获取集合名称(文件名去掉扩展名),并删除该集合中的所有现有数据。
    • 将读取到的数据插入到 MongoDB 中。

4. 读取备份数据

  • ReadBackupDataAsync 方法:
    • 读取指定的 BSON 文件并将其内容解析为 BsonDocument 对象的列表。
    • 使用 BsonBinaryReader 逐个读取 BSON 文档,直到文件结束。
    • 处理读取过程中可能出现的异常,并记录日志。

5. BSON 文档读取

  • ReadBsonDocument 方法:
    • 使用 BsonSerializerBsonBinaryReader 中的内容反序列化为 BsonDocument 对象。

6. 解压缩 ZIP 文件

  • ExtractZipFile 方法:
    • 使用 ICSharpCode.SharpZipLib 库解压缩 ZIP 文件。
    • 读取 ZIP 文件中的每个条目,并将其写入到指定的输出目录。
    • 支持使用密码解压缩。

7. 日志记录

  • 使用 ILogger<RestoreManager> 记录操作的详细信息,包括还原的数据库名称、备份文件路径、读取的文件等。

总结

整体上,RestoreManager 类提供了一种结构化的方式来从压缩的备份文件中恢复 MongoDB 数据库。它通过解压缩文件、读取 BSON 数据并将其插入到数据库中来实现这一功能,同时提供了详细的日志记录以便于调试和监控。

loading