Implemented zip and tar compressing and decompressing. Implemented chunked file uploading

This commit is contained in:
2025-03-24 22:15:05 +01:00
parent 4046579c42
commit f56f94a03b
18 changed files with 573 additions and 55 deletions

View File

@@ -1,20 +1,41 @@
using MoonCore.Blazor.Tailwind.Fm;
using MoonCore.Blazor.Tailwind.Fm.Models;
using MoonCore.Blazor.Tailwind.Services;
using MoonCore.Helpers;
using MoonlightServers.Frontend.Services;
namespace MoonlightServers.Frontend.Helpers;
public class ServerFileSystemProvider : IFileSystemProvider
public class ServerFileSystemProvider : IFileSystemProvider, ICompressFileSystemProvider
{
private readonly int ServerId;
private readonly DownloadService DownloadService;
private readonly ServerFileSystemService FileSystemService;
public CompressType[] CompressTypes { get; } =
[
new()
{
Extension = "zip",
DisplayName = "ZIP Archive"
},
new()
{
Extension = "tar.gz",
DisplayName = "GZ Compressed Tar Archive"
}
];
private readonly int ServerId;
public ServerFileSystemProvider(
int serverId,
ServerFileSystemService fileSystemService
ServerFileSystemService fileSystemService,
DownloadService downloadService
)
{
ServerId = serverId;
FileSystemService = fileSystemService;
DownloadService = downloadService;
}
public async Task<FileSystemEntry[]> List(string path)
@@ -35,7 +56,7 @@ public class ServerFileSystemProvider : IFileSystemProvider
public async Task Create(string path, Stream stream)
{
await FileSystemService.Upload(ServerId, path, stream);
await Upload(_ => Task.CompletedTask, path, stream);
}
public async Task Move(string oldPath, string newPath)
@@ -55,6 +76,72 @@ public class ServerFileSystemProvider : IFileSystemProvider
public async Task<Stream> Read(string path)
{
return await FileSystemService.Download(ServerId, path);
var downloadSession = await FileSystemService.Download(ServerId, path);
using var httpClient = new HttpClient();
return await httpClient.GetStreamAsync(downloadSession.DownloadUrl);
}
public async Task Download(Func<int, Task> updateProgress, string path, string fileName)
{
var downloadSession = await FileSystemService.Download(ServerId, path);
await DownloadService.DownloadUrl(fileName, downloadSession.DownloadUrl,
async (loaded, total) =>
{
var percent = total == 0 ? 0 : (int)Math.Round((float)loaded / total * 100);
await updateProgress.Invoke(percent);
}
);
}
public async Task Upload(Func<int, Task> updateProgress, string path, Stream stream)
{
using var httpClient = new HttpClient();
var uploadSession = await FileSystemService.Upload(ServerId);
var size = stream.Length;
var chunkSize = ByteConverter.FromMegaBytes(20).Bytes;
var chunks = size / chunkSize;
chunks += size % chunkSize > 0 ? 1 : 0;
for (var chunkId = 0; chunkId < chunks; chunkId++)
{
var percent = (int)Math.Round((chunkId + 1f) / chunks * 100);
await updateProgress.Invoke(percent);
var buffer = new byte[chunkSize];
var bytesRead = await stream.ReadAsync(buffer);
var uploadForm = new MultipartFormDataContent();
uploadForm.Add(new ByteArrayContent(buffer, 0, bytesRead), "file", "file");
await httpClient.PostAsync(
$"{uploadSession.UploadUrl}&totalSize={size}&chunkId={chunkId}&path={path}",
uploadForm
);
}
}
public async Task Compress(CompressType type, string path, string[] itemsToCompress)
{
await FileSystemService.Compress(
ServerId,
type.Extension.Replace(".", ""),
itemsToCompress,
path
);
}
public async Task Decompress(CompressType type, string path, string destination)
{
await FileSystemService.Decompress(
ServerId,
type.Extension.Replace(".", ""),
path,
destination
);
}
}

View File

@@ -1,5 +1,7 @@
using System.Net;
using MoonCore.Attributes;
using MoonCore.Helpers;
using MoonlightServers.Shared.Http.Requests.Client.Servers.Files;
using MoonlightServers.Shared.Http.Responses.Client.Servers.Files;
namespace MoonlightServers.Frontend.Services;
@@ -42,28 +44,43 @@ public class ServerFileSystemService
);
}
public async Task Upload(int serverId, string path, Stream dataStream)
public async Task<ServerFilesUploadResponse> Upload(int serverId)
{
var uploadSession = await ApiClient.GetJson<ServerFilesUploadResponse>(
return await ApiClient.GetJson<ServerFilesUploadResponse>(
$"api/client/servers/{serverId}/files/upload"
);
using var httpClient = new HttpClient();
var content = new MultipartFormDataContent();
content.Add(new StreamContent(dataStream), "file", path);
await httpClient.PostAsync(uploadSession.UploadUrl, content);
}
public async Task<Stream> Download(int serverId, string path)
public async Task<ServerFilesDownloadResponse> Download(int serverId, string path)
{
var downloadSession = await ApiClient.GetJson<ServerFilesDownloadResponse>(
return await ApiClient.GetJson<ServerFilesDownloadResponse>(
$"api/client/servers/{serverId}/files/download?path={path}"
);
}
using var httpClient = new HttpClient();
return await httpClient.GetStreamAsync(downloadSession.DownloadUrl);
public async Task Compress(int serverId, string type, string[] items, string destination)
{
await ApiClient.Post(
$"api/client/servers/{serverId}/files/compress",
new ServerFilesCompressRequest()
{
Type = type,
Items = items,
Destination = destination
}
);
}
public async Task Decompress(int serverId, string type, string path, string destination)
{
await ApiClient.Post(
$"api/client/servers/{serverId}/files/decompress",
new ServerFilesDecompressRequest()
{
Type = type,
Path = path,
Destination = destination
}
);
}
}

View File

@@ -1,19 +1,25 @@
@using MoonlightServers.Frontend.Services
@using MoonCore.Blazor.Tailwind.Fm
@using MoonCore.Blazor.Tailwind.Services
@using MoonlightServers.Frontend.Helpers
@inherits BaseServerTab
@inject ServerFileSystemService FileSystemService
@inject DownloadService DownloadService
<FileManager FileSystemProvider="Provider" />
<FileManager FileSystemProvider="Provider"/>
@code
{
private IFileSystemProvider Provider;
protected override void OnInitialized()
{
Provider = new ServerFileSystemProvider(Server.Id, FileSystemService);
Provider = new ServerFileSystemProvider(
Server.Id,
FileSystemService,
DownloadService
);
}
}

View File

@@ -26,8 +26,10 @@
</PageHeader>
</div>
<DataTable @ref="Table" TItem="ServerDetailResponse" PageSize="15" LoadItemsPaginatedAsync="LoadData">
<DataTable @ref="Table" TItem="ServerDetailResponse">
<Configuration>
<Pagination TItem="ServerDetailResponse" ItemSource="LoadData" />
<DataTableColumn TItem="ServerDetailResponse" Field="@(x => x.Id)" Name="Id"/>
<DataTableColumn TItem="ServerDetailResponse" Field="@(x => x.Name)" Name="Name"/>
<DataTableColumn TItem="ServerDetailResponse" Field="@(x => x.NodeId)" Name="Node">

View File

@@ -27,8 +27,10 @@
</PageHeader>
</div>
<DataTable TItem="NodeDetailResponse" PageSize="15" LoadItemsPaginatedAsync="LoadData">
<DataTable TItem="NodeDetailResponse">
<Configuration>
<Pagination TItem="NodeDetailResponse" ItemSource="LoadData" />
<DataTableColumn TItem="NodeDetailResponse" Field="@(x => x.Id)" Name="Id"/>
<DataTableColumn TItem="NodeDetailResponse" Field="@(x => x.Name)" Name="Name">
<ColumnTemplate>
@@ -103,8 +105,8 @@
</ColumnTemplate>
</DataTableColumn>
<DataTableColumn TItem="NodeDetailResponse"
HeaderCss="p-2 font-semibold text-left hidden xl:table-cell"
ColumnCss="p-2 text-left font-normal hidden xl:table-cell">
HeaderCss="p-2 font-semibold text-left hidden xl:table-cell"
ColumnCss="p-2 text-left font-normal hidden xl:table-cell">
<ColumnTemplate>
<div>
<i class="icon-memory-stick text-lg me-1 align-middle text-primary-500"></i>

View File

@@ -33,9 +33,11 @@
</PageHeader>
</div>
<DataTable @ref="Table" TItem="StarDetailResponse" LoadItemsPaginatedAsync="LoadData">
<DataTable @ref="Table" TItem="StarDetailResponse">
<Configuration>
<DataTableColumn TItem="StarDetailResponse" Field="@(x => x.Id)" Name="Id" />
<Pagination TItem="StarDetailResponse" ItemSource="LoadData" />
<DataTableColumn TItem="StarDetailResponse" Field="@(x => x.Id)" Name="Id"/>
<DataTableColumn TItem="StarDetailResponse" Field="@(x => x.Name)" Name="Name">
<ColumnTemplate>
<a class="text-primary-500" href="/admin/servers/stars/update/@(context.Id)">
@@ -43,8 +45,8 @@
</a>
</ColumnTemplate>
</DataTableColumn>
<DataTableColumn TItem="StarDetailResponse" Field="@(x => x.Version)" Name="Version" />
<DataTableColumn TItem="StarDetailResponse" Field="@(x => x.Author)" Name="Author" />
<DataTableColumn TItem="StarDetailResponse" Field="@(x => x.Version)" Name="Version"/>
<DataTableColumn TItem="StarDetailResponse" Field="@(x => x.Author)" Name="Author"/>
<DataTableColumn TItem="StarDetailResponse">
<ColumnTemplate>
<div class="flex justify-end">
@@ -68,7 +70,7 @@
<i class="icon-download align-middle"></i>
<span class="align-middle">Export</span>
</a>
<a href="/admin/servers/stars/update/@(context.Id)" class="text-primary-500 mr-2 sm:mr-3">
<i class="icon-pencil text-base"></i>
</a>