Make subsequent archive uploads patch the first uploaded archive

dhcp-server v0.0.2
Pat Hartl 2023-04-20 00:26:08 -05:00
parent 28e7de2670
commit f95d348818
3 changed files with 113 additions and 7 deletions

View File

@ -0,0 +1,22 @@
using LANCommander.Services;
namespace LANCommander.Jobs.Background
{
public class PatchArchiveBackgroundJob
{
private readonly ArchiveService ArchiveService;
public PatchArchiveBackgroundJob(ArchiveService archiveService)
{
ArchiveService = archiveService;
}
public async Task Execute(Guid originalArchiveId, Guid alteredArchiveId)
{
var originalArchive = await ArchiveService.Get(originalArchiveId);
var alteredArchive = await ArchiveService.Get(alteredArchiveId);
await ArchiveService.PatchArchive(originalArchive, alteredArchive);
}
}
}

View File

@ -1,5 +1,7 @@
@using System.Net;
@using System.Diagnostics;
@using Hangfire;
@using LANCommander.Jobs.Background;
@inject HttpClient HttpClient
@inject NavigationManager Navigator
@inject ArchiveService ArchiveService
@ -177,10 +179,17 @@
Archive.ObjectKey = objectKey.ToString();
Archive.CompressedSize = File.Size;
await ArchiveService.Add(Archive);
var originalArchive = Game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault();
Archive = await ArchiveService.Add(Archive);
ModalVisible = false;
await MessageService.Success("Archive uploaded!");
if (originalArchive != null)
{
BackgroundJob.Enqueue<PatchArchiveBackgroundJob>(x => x.Execute(originalArchive.Id, Archive.Id));
}
}
}

View File

@ -16,16 +16,26 @@ namespace LANCommander.Services
{
}
public override Task Delete(Archive entity)
public static string GetArchiveFileLocation(Archive archive)
{
FileHelpers.DeleteIfExists($"Upload/{entity.ObjectKey}".ToPath());
return GetArchiveFileLocation(archive.ObjectKey);
}
return base.Delete(entity);
public static string GetArchiveFileLocation(string objectKey)
{
return $"Upload/{objectKey}".ToPath();
}
public override Task Delete(Archive archive)
{
FileHelpers.DeleteIfExists(GetArchiveFileLocation(archive));
return base.Delete(archive);
}
public static GameManifest ReadManifest(string objectKey)
{
var upload = $"Upload/{objectKey}".ToPath();
var upload = GetArchiveFileLocation(objectKey);
string manifestContents = String.Empty;
@ -57,7 +67,7 @@ namespace LANCommander.Services
public static byte[] ReadFile(string objectKey, string path)
{
var upload = $"Upload/{objectKey}".ToPath();
var upload = GetArchiveFileLocation(objectKey);
if (!File.Exists(upload))
throw new FileNotFoundException(upload);
@ -82,12 +92,77 @@ namespace LANCommander.Services
{
var archive = await Get(archiveId);
var upload = $"Upload/{archive.ObjectKey}".ToPath();
var upload = GetArchiveFileLocation(archive);
using (ZipArchive zip = ZipFile.OpenRead(upload))
{
return zip.Entries;
}
}
public async Task PatchArchive(Archive originalArchive, Archive alteredArchive, CompressionLevel compressionLevel = CompressionLevel.Optimal)
{
var alteredZipPath = GetArchiveFileLocation(alteredArchive);
var patchZipPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
ZipArchive originalZip = ZipFile.Open(GetArchiveFileLocation(originalArchive), ZipArchiveMode.Update);
ZipArchive alteredZip = ZipFile.OpenRead(alteredZipPath);
ZipArchive patchZip = ZipFile.Open(patchZipPath, ZipArchiveMode.Create);
int i = 0;
foreach (var entry in alteredZip.Entries)
{
var originalEntry = originalZip.GetEntry(entry.FullName);
if (originalEntry == null || originalEntry.Crc32 != entry.Crc32)
{
originalEntry?.Delete();
var updatedEntry = originalZip.CreateEntry(entry.FullName, compressionLevel);
var patchEntry = patchZip.CreateEntry(entry.FullName, compressionLevel);
// Copy the contents of the entry from the altered archive to the original archive
using (var updatedStream = updatedEntry.Open())
using (var alteredStream = entry.Open())
{
await alteredStream.CopyToAsync(updatedStream);
Logger.Info("Added {EntryFullName} to base archive {ArchiveId} and new patch archive", entry.FullName, originalArchive.Id.ToString());
}
// Copy the contents of the entry from the altered archive to the patch archive
using (var patchStream = patchEntry.Open())
using (var alteredStream = entry.Open())
{
await alteredStream.CopyToAsync(patchStream);
Logger.Info("Updated {EntryFullName} in base archive {ArchiveId} and added to new patch archive", entry.FullName, originalArchive.Id.ToString());
}
}
i++;
Logger.Info("Finished processing entry {EntryIndex}/{TotalEntries} for original archive {ArchiveId}", i.ToString(), originalZip.Entries.Count.ToString(), originalArchive.Id.ToString());
}
originalZip.Dispose();
alteredZip.Dispose();
patchZip.Dispose();
// Replace the uploaded altered ZIP with the new patch ZIP
if (File.Exists(alteredZipPath))
File.Delete(alteredZipPath);
File.Move(patchZipPath, alteredZipPath);
alteredArchive.CompressedSize = new FileInfo(GetArchiveFileLocation(alteredArchive)).Length;
originalArchive.CompressedSize = new FileInfo(GetArchiveFileLocation(originalArchive)).Length;
await Update(alteredArchive);
await Update(originalArchive);
Logger.Info("Finished merging original archive {ArchiveId} and rebuilt patch archive {PatchArchivePath}", originalArchive.Id.ToString(), alteredZipPath);
}
}
}