网站首页 网站源码
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);
}
}
}
上述代码实现了一个数据还原管理器 RestoreManager
,用于从压缩的备份文件中恢复 MongoDB 数据库的内容。以下是代码的主要功能和结构的详细解释:
ExtractZipFile
方法解压缩备份文件到指定的还原目录。Parallel.ForEachAsync
并行处理每个 BSON 文件,调用 RestoreCollectionAsync
方法进行还原。BsonDocument
对象的列表。BsonBinaryReader
逐个读取 BSON 文档,直到文件结束。BsonSerializer
将 BsonBinaryReader
中的内容反序列化为 BsonDocument
对象。ICSharpCode.SharpZipLib
库解压缩 ZIP 文件。ILogger<RestoreManager>
记录操作的详细信息,包括还原的数据库名称、备份文件路径、读取的文件等。整体上,RestoreManager
类提供了一种结构化的方式来从压缩的备份文件中恢复 MongoDB 数据库。它通过解压缩文件、读取 BSON 数据并将其插入到数据库中来实现这一功能,同时提供了详细的日志记录以便于调试和监控。