183 lines
5.3 KiB
C#
183 lines
5.3 KiB
C#
using System.Text;
|
|
using ICSharpCode.SharpZipLib.GZip;
|
|
using ICSharpCode.SharpZipLib.Tar;
|
|
using ICSharpCode.SharpZipLib.Zip;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using MoonCore.Helpers;
|
|
using Moonlight.ApiServer.Helpers;
|
|
using Moonlight.Shared.Http.Requests.Admin.Sys.Files;
|
|
|
|
namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys.Files;
|
|
|
|
[ApiController]
|
|
[Route("api/admin/system/files")]
|
|
[Authorize(Policy = "permissions:admin.system.files")]
|
|
public class CompressController : Controller
|
|
{
|
|
private const string BaseDirectory = "storage";
|
|
|
|
[HttpPost("compress")]
|
|
public async Task<IResult> CompressAsync([FromBody] CompressRequest request)
|
|
{
|
|
// Validate item length
|
|
if (request.Items.Length == 0)
|
|
{
|
|
return Results.Problem(
|
|
"At least one item is required",
|
|
statusCode: 400
|
|
);
|
|
}
|
|
|
|
// Build paths
|
|
var destinationPath = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(request.Destination));
|
|
var rootPath = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(request.Root));
|
|
|
|
// Resolve the relative to the root item paths to absolute paths
|
|
var itemsPaths = request.Items.Select(item =>
|
|
Path.Combine(
|
|
BaseDirectory,
|
|
FilePathHelper.SanitizePath(
|
|
UnixPath.Combine(request.Root, item)
|
|
)
|
|
)
|
|
);
|
|
|
|
// Execute request
|
|
switch (request.Format)
|
|
{
|
|
case "tar.gz":
|
|
await CompressTarGzAsync(destinationPath, itemsPaths, rootPath);
|
|
break;
|
|
|
|
case "zip":
|
|
await CompressZipAsync(destinationPath, itemsPaths, rootPath);
|
|
break;
|
|
|
|
default:
|
|
return Results.Problem("Unsupported archive format specified", statusCode: 400);
|
|
}
|
|
|
|
return Results.Ok();
|
|
}
|
|
|
|
|
|
|
|
#region Tar Gz
|
|
|
|
private async Task CompressTarGzAsync(string destination, IEnumerable<string> items, string root)
|
|
{
|
|
await using var outStream = System.IO.File.Create(destination);
|
|
await using var gzoStream = new GZipOutputStream(outStream);
|
|
await using var tarStream = new TarOutputStream(gzoStream, Encoding.UTF8);
|
|
|
|
foreach (var item in items)
|
|
await CompressItemToTarGzAsync(tarStream, item, root);
|
|
|
|
await tarStream.FlushAsync();
|
|
await gzoStream.FlushAsync();
|
|
await outStream.FlushAsync();
|
|
|
|
tarStream.Close();
|
|
gzoStream.Close();
|
|
outStream.Close();
|
|
}
|
|
|
|
private async Task CompressItemToTarGzAsync(TarOutputStream tarOutputStream, string item, string root)
|
|
{
|
|
if (System.IO.File.Exists(item))
|
|
{
|
|
// Open file stream
|
|
var fs = System.IO.File.Open(item, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
|
|
|
// Meta
|
|
var entry = TarEntry.CreateTarEntry(
|
|
Formatter
|
|
.ReplaceStart(item, root, "")
|
|
.TrimStart('/')
|
|
);
|
|
|
|
// Set size
|
|
entry.Size = fs.Length;
|
|
|
|
// Write entry
|
|
await tarOutputStream.PutNextEntryAsync(entry, CancellationToken.None);
|
|
|
|
// Copy file content to tar stream
|
|
await fs.CopyToAsync(tarOutputStream);
|
|
fs.Close();
|
|
|
|
// Close the entry
|
|
tarOutputStream.CloseEntry();
|
|
|
|
return;
|
|
}
|
|
|
|
if (Directory.Exists(item))
|
|
{
|
|
foreach (var fsEntry in Directory.EnumerateFileSystemEntries(item))
|
|
await CompressItemToTarGzAsync(tarOutputStream, fsEntry, root);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ZIP
|
|
|
|
private async Task CompressZipAsync(string destination, IEnumerable<string> items, string root)
|
|
{
|
|
await using var outStream = System.IO.File.Create(destination);
|
|
await using var zipOutputStream = new ZipOutputStream(outStream);
|
|
|
|
foreach (var item in items)
|
|
await AddItemToZipAsync(zipOutputStream, item, root);
|
|
|
|
await zipOutputStream.FlushAsync();
|
|
await outStream.FlushAsync();
|
|
|
|
zipOutputStream.Close();
|
|
outStream.Close();
|
|
}
|
|
|
|
private async Task AddItemToZipAsync(ZipOutputStream outputStream, string item, string root)
|
|
{
|
|
if (System.IO.File.Exists(item))
|
|
{
|
|
// Open file stream
|
|
var fs = System.IO.File.Open(item, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
|
|
|
// Meta
|
|
var entry = new ZipEntry(
|
|
Formatter
|
|
.ReplaceStart(item, root, "")
|
|
.TrimStart('/')
|
|
);
|
|
|
|
entry.Size = fs.Length;
|
|
|
|
// Write entry
|
|
await outputStream.PutNextEntryAsync(entry, CancellationToken.None);
|
|
|
|
// Copy file content to tar stream
|
|
await fs.CopyToAsync(outputStream);
|
|
fs.Close();
|
|
|
|
// Close the entry
|
|
outputStream.CloseEntry();
|
|
|
|
// Flush caches
|
|
await outputStream.FlushAsync();
|
|
|
|
return;
|
|
}
|
|
|
|
if (Directory.Exists(item))
|
|
{
|
|
foreach (var subItem in Directory.EnumerateFileSystemEntries(item))
|
|
await AddItemToZipAsync(outputStream, subItem, root);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
} |