Extended file manager to support the new interfaces for downloading via url. Improved the handling of compressing and decompressing. Seperated file manager controllers. Updated mooncore versions
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Helpers;
|
||||
using Moonlight.Shared.Http.Responses.Admin.Sys;
|
||||
|
||||
namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys.Files;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/admin/system/files/downloadUrl")]
|
||||
[Authorize(Policy = "permissions:admin.system.files")]
|
||||
public class DownloadUrlController : Controller
|
||||
{
|
||||
private readonly AppConfiguration Configuration;
|
||||
|
||||
private const string BaseDirectory = "storage";
|
||||
|
||||
public DownloadUrlController(AppConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task Get([FromQuery] string path)
|
||||
{
|
||||
var physicalPath = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(path));
|
||||
var name = Path.GetFileName(physicalPath);
|
||||
|
||||
if (System.IO.File.Exists(physicalPath))
|
||||
{
|
||||
await using var fs = System.IO.File.Open(physicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
await Results.File(fs, fileDownloadName: name).ExecuteAsync(HttpContext);
|
||||
}
|
||||
else if(Directory.Exists(physicalPath))
|
||||
{
|
||||
// Without the base directory we would have the full path to the target folder
|
||||
// inside the zip
|
||||
|
||||
var baseDirectory = Path.Combine(
|
||||
BaseDirectory,
|
||||
FilePathHelper.SanitizePath(Path.GetDirectoryName(path) ?? "")
|
||||
);
|
||||
|
||||
Response.StatusCode = 200;
|
||||
Response.ContentType = "application/zip";
|
||||
Response.Headers["Content-Disposition"] = $"attachment; filename=\"{name}.zip\"";
|
||||
|
||||
try
|
||||
{
|
||||
await using var zipStream = new ZipOutputStream(Response.Body);
|
||||
zipStream.IsStreamOwner = false;
|
||||
|
||||
await StreamFolderAsZip(zipStream, physicalPath, baseDirectory, HttpContext.RequestAborted);
|
||||
}
|
||||
catch (ZipException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StreamFolderAsZip(
|
||||
ZipOutputStream zipStream,
|
||||
string path, string rootPath,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
foreach (var file in Directory.EnumerateFiles(path))
|
||||
{
|
||||
if (HttpContext.RequestAborted.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
var fi = new FileInfo(file);
|
||||
|
||||
var filePath = Formatter.ReplaceStart(file, rootPath, "");
|
||||
|
||||
await zipStream.PutNextEntryAsync(new ZipEntry(filePath)
|
||||
{
|
||||
Size = fi.Length,
|
||||
}, cancellationToken);
|
||||
|
||||
await using var fs = System.IO.File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
await fs.CopyToAsync(zipStream, cancellationToken);
|
||||
await fs.FlushAsync(cancellationToken);
|
||||
|
||||
fs.Close();
|
||||
|
||||
await zipStream.FlushAsync(cancellationToken);
|
||||
}
|
||||
|
||||
foreach (var directory in Directory.EnumerateDirectories(path))
|
||||
{
|
||||
if (HttpContext.RequestAborted.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
await StreamFolderAsZip(zipStream, directory, rootPath, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Yes I know we can just create that url on the client as the exist validation is done on both endpoints,
|
||||
// but we leave it here for future modifications. E.g. using a distributed file provider or smth like that
|
||||
[HttpPost]
|
||||
public Task<DownloadUrlResponse> Post([FromQuery] string path)
|
||||
{
|
||||
var safePath = FilePathHelper.SanitizePath(path);
|
||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||
|
||||
if (System.IO.File.Exists(physicalPath) || Directory.Exists(physicalPath))
|
||||
{
|
||||
return Task.FromResult(new DownloadUrlResponse()
|
||||
{
|
||||
Url = $"{Configuration.PublicUrl}/api/admin/system/files/downloadUrl?path={path}"
|
||||
});
|
||||
}
|
||||
|
||||
throw new HttpApiException("No such file or directory found", 404);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user