Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions ManagedCode.Storage.ApiClient/Controllers/DownloadController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using ManagedCode.Storage.Core;
using ManagedCode.Storage.Server;
using Microsoft.AspNetCore.Mvc;

namespace ManagedCode.Storage.ApiClient.Controllers
{
/// <summary>
/// Controller for file downloads
/// </summary>
[ApiController]
[Route("api/download")]
public class DownloadController : ControllerBase
{
private readonly IStorage _storage;

/// <summary>
/// Constructor for DownloadController
/// </summary>
/// <param name="storage"> Storage service used for file downloads </param>
public DownloadController(IStorage storage)
{
_storage = storage;
}

/// <summary>
/// Method for downloading a file by its name.
/// </summary>
/// <param name="fileName"> The name of the file to download </param>
/// <returns>The result of the file download as a <see cref="FileResult"/></returns>
[HttpGet("{fileName}")]
public async Task<FileResult> DownloadFileAsync(string fileName)
{
// Call the file download method from the storage.
var result = await _storage.DownloadAsFileResult(fileName);

// Check the operation result for any errors.
result.ThrowIfFail();

// Return the operation result as a FileResult.
return result.Value!;
}
}
}
76 changes: 76 additions & 0 deletions ManagedCode.Storage.ApiClient/Controllers/UploadController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using ManagedCode.Communication;
using ManagedCode.Storage.Core;
using ManagedCode.Storage.Core.Models;
using ManagedCode.Storage.Server;
using Microsoft.AspNetCore.Mvc;

namespace ManagedCode.Storage.ApiClient.Controllers
{
/// <summary>
/// Controller for file uploads.
/// </summary>
[ApiController]
[Route("api/upload")]
public class UploadController : ControllerBase
{
private readonly IStorage _storage;

/// <summary>
/// Constructor for UploadController.
/// </summary>
/// <param name="storage">Storage service used for file uploads.</param>
public UploadController(IStorage storage)
{
_storage = storage;
}

/// <summary>
/// Method for uploading a file.
/// </summary>
/// <param name="file">The file to upload.</param>
/// <returns>The result of the file upload as a <see cref="Result{BlobMetadata}"/>.</returns>
[HttpPost]
public async Task<Result<BlobMetadata>> UploadFileAsync(IFormFile file)
{
if (Request.HasFormContentType is false)
{
return Result<BlobMetadata>.Fail("invalid body");
}

return await _storage.UploadAsync(file.OpenReadStream());
}

/// <summary>
/// Method for uploading a large file in chunks.
/// </summary>
/// <param name="file">The file upload payload containing chunk information.</param>
/// <returns>The result of the upload operation as a <see cref="Result"/>.</returns>
[HttpPost("upload-chunks/upload")]
public async Task<Result> UploadLargeFile(FileUploadPayload file)
{
try
{
// Create a temporary path for storing the chunk.
string newpath = Path.Combine(Path.GetTempPath(), $"{file.File.FileName}_{file.Payload.ChunkIndex}");

// Write the chunk data to a temporary file.
await using (FileStream fs = System.IO.File.Create(newpath))
{
byte[] bytes = new byte[file.Payload.ChunkSize];
int bytesRead = 0;
var fileStream = file.File.OpenReadStream();
while ((bytesRead = await fileStream.ReadAsync(bytes, 0, bytes.Length)) > 0)
{
await fs.WriteAsync(bytes, 0, bytesRead);
}
}
}
catch (Exception ex)
{
return Result.Fail(ex.Message);
}

return Result.Succeed();
}
}
}
22 changes: 22 additions & 0 deletions ManagedCode.Storage.ApiClient/ManagedCode.Storage.ApiClient.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ManagedCode.Storage.Aws\ManagedCode.Storage.Aws.csproj" />
<ProjectReference Include="..\ManagedCode.Storage.Azure\ManagedCode.Storage.Azure.csproj" />
<ProjectReference Include="..\ManagedCode.Storage.Client\ManagedCode.Storage.Client.csproj" />
<ProjectReference Include="..\ManagedCode.Storage.FileSystem\ManagedCode.Storage.FileSystem.csproj" />
<ProjectReference Include="..\ManagedCode.Storage.Google\ManagedCode.Storage.Google.csproj" />
<ProjectReference Include="..\ManagedCode.Storage.Server\ManagedCode.Storage.Server.csproj" />
</ItemGroup>

</Project>
53 changes: 53 additions & 0 deletions ManagedCode.Storage.ApiClient/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using ManagedCode.Storage.Core;
using ManagedCode.Storage.FileSystem;
using ManagedCode.Storage.FileSystem.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Load configuration settings from appsettings.json
builder.Configuration.AddJsonFile("appsettings.json");

// Create a folder path for storing files
string storageFolderPath = Path.Combine(Environment.CurrentDirectory, "Files_Storage");

// Check if the storage folder exists, if not, create it
if (!Directory.Exists(storageFolderPath))
{
Directory.CreateDirectory(storageFolderPath);
}

// Configure storage options to use the created folder
builder.Services.AddFileSystemStorageAsDefault(opt =>
{
opt.BaseFolder = storageFolderPath;
});

// Add services to the container.

// Add HttpClient service
builder.Services.AddHttpClient();

// Register the FileSystemStorage implementation of the IStorage interface
builder.Services.AddTransient<IStorage, FileSystemStorage>();

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
41 changes: 41 additions & 0 deletions ManagedCode.Storage.ApiClient/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:20938",
"sslPort": 44376
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5214",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7108;http://localhost:5214",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
8 changes: 8 additions & 0 deletions ManagedCode.Storage.ApiClient/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
14 changes: 14 additions & 0 deletions ManagedCode.Storage.ApiClient/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"StorageOptions": {
"FileSystem": {
"BaseFolder": "Files_Storage"
}
}
}
10 changes: 8 additions & 2 deletions ManagedCode.Storage.Client/IStorageClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -20,8 +20,14 @@ public interface IStorageClient : IUploader, IDownloader
/// This includes the file name, progress percentage, total bytes, transferred bytes, elapsed time, remaining time, speed, and any error message.
/// </remarks>
event EventHandler<ProgressStatus> OnProgressStatusChanged;
}

Task<Result<BlobMetadata>> UploadFile(Stream stream, string apiUrl, string contentName, CancellationToken cancellationToken = default);
Task<Result<BlobMetadata>> UploadFile(FileInfo fileInfo, string apiUrl, string contentName, CancellationToken cancellationToken = default);
Task<Result<BlobMetadata>> UploadFile(byte[] bytes, string apiUrl, string contentName, CancellationToken cancellationToken = default);
Task<Result<BlobMetadata>> UploadFile(string base64, string apiUrl, string contentName, CancellationToken cancellationToken = default);
Task<Result<uint>> UploadLargeFile(Stream file, string uploadApiUrl, string completeApiUrl,Action<double>? onProgressChanged,CancellationToken cancellationToken = default);
Task<Result<LocalFile>> DownloadFile(string fileName, string apiUrl, string? path = null, CancellationToken cancellationToken = default);
}

public record ProgressStatus(
string File,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Net;
using System.Net;
using FluentAssertions;
using ManagedCode.Storage.Core.Helpers;
using ManagedCode.Storage.Core.Models;
Expand All @@ -23,6 +23,7 @@ public async Task DownloadFile_WhenFileExists_SaveToTempStorage_ReturnSuccess()
{
// Arrange
var storageClient = GetStorageClient();

var contentName = "file";

await using var localFile = LocalFile.FromRandomNameWithExtension(".txt");
Expand Down
38 changes: 22 additions & 16 deletions ManagedCode.Storage.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@


Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31729.503
# Visual Studio Version 17
VisualStudioVersion = 17.9.34701.34
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.Core", "ManagedCode.Storage.Core\ManagedCode.Storage.Core.csproj", "{1B494908-A80A-4EEE-97A7-ABDEAC3EC64F}"
EndProject
Expand All @@ -13,25 +13,27 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.Aws", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.Google", "ManagedCode.Storage.Google\ManagedCode.Storage.Google.csproj", "{C3B4FF9C-1C6A-4EA0-9291-E7E0C0EF2BA3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.Storage.FileSystem", "ManagedCode.Storage.FileSystem\ManagedCode.Storage.FileSystem.csproj", "{EDFA1CB7-1721-4447-9C25-AE110821717C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.FileSystem", "ManagedCode.Storage.FileSystem\ManagedCode.Storage.FileSystem.csproj", "{EDFA1CB7-1721-4447-9C25-AE110821717C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.Storage.Server", "ManagedCode.Storage.Server\ManagedCode.Storage.Server.csproj", "{852B0DBD-37F0-4DC0-B966-C284AE03C2F5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.Server", "ManagedCode.Storage.Server\ManagedCode.Storage.Server.csproj", "{852B0DBD-37F0-4DC0-B966-C284AE03C2F5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.Storage.Azure.DataLake", "ManagedCode.Storage.Azure.DataLake\ManagedCode.Storage.Azure.DataLake.csproj", "{4D4D2AC7-923D-4219-9BC9-341FBA7FE690}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.Azure.DataLake", "ManagedCode.Storage.Azure.DataLake\ManagedCode.Storage.Azure.DataLake.csproj", "{4D4D2AC7-923D-4219-9BC9-341FBA7FE690}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.Storage.TestFakes", "ManagedCode.Storage.TestFakes\ManagedCode.Storage.TestFakes.csproj", "{7190B548-4BE9-4EF6-B55F-8432757AEAD5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.TestFakes", "ManagedCode.Storage.TestFakes\ManagedCode.Storage.TestFakes.csproj", "{7190B548-4BE9-4EF6-B55F-8432757AEAD5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Storages", "Storages", "{92201402-E361-440F-95DB-68663D228C2D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNet", "AspNet", "{94DB7354-F5C7-4347-B9EC-FCCA38B86876}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.Storage.Client", "ManagedCode.Storage.Client\ManagedCode.Storage.Client.csproj", "{D5A7D3A7-E6E8-4153-911D-D7C0C5C8B19C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.Client", "ManagedCode.Storage.Client\ManagedCode.Storage.Client.csproj", "{D5A7D3A7-E6E8-4153-911D-D7C0C5C8B19C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.Storage.Client.SignalR", "ManagedCode.Storage.Client.SignalR\ManagedCode.Storage.Client.SignalR.csproj", "{ED216AAD-CBA2-40F2-AA01-63C60E906632}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.Client.SignalR", "ManagedCode.Storage.Client.SignalR\ManagedCode.Storage.Client.SignalR.csproj", "{ED216AAD-CBA2-40F2-AA01-63C60E906632}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{E609A83E-6400-42B0-AD5A-5B006EABC275}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.Storage.IntegrationTests", "ManagedCode.Storage.IntegrationTests\ManagedCode.Storage.IntegrationTests.csproj", "{39EFEB67-4C0F-4FAD-8FE8-06D7A5D02FEE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCode.Storage.IntegrationTests", "ManagedCode.Storage.IntegrationTests\ManagedCode.Storage.IntegrationTests.csproj", "{39EFEB67-4C0F-4FAD-8FE8-06D7A5D02FEE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedCode.Storage.ApiClient", "ManagedCode.Storage.ApiClient\ManagedCode.Storage.ApiClient.csproj", "{22AF4C61-F42C-487E-A0C8-CBFB1CBED53D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -87,23 +89,27 @@ Global
{39EFEB67-4C0F-4FAD-8FE8-06D7A5D02FEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39EFEB67-4C0F-4FAD-8FE8-06D7A5D02FEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39EFEB67-4C0F-4FAD-8FE8-06D7A5D02FEE}.Release|Any CPU.Build.0 = Release|Any CPU
{22AF4C61-F42C-487E-A0C8-CBFB1CBED53D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22AF4C61-F42C-487E-A0C8-CBFB1CBED53D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22AF4C61-F42C-487E-A0C8-CBFB1CBED53D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22AF4C61-F42C-487E-A0C8-CBFB1CBED53D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A594F814-80A8-49D2-B751-B3A58869B30D}
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C3B4FF9C-1C6A-4EA0-9291-E7E0C0EF2BA3} = {92201402-E361-440F-95DB-68663D228C2D}
{4D4D2AC7-923D-4219-9BC9-341FBA7FE690} = {92201402-E361-440F-95DB-68663D228C2D}
{0D6304D1-911D-489E-A716-6CBD5D0FE05D} = {92201402-E361-440F-95DB-68663D228C2D}
{F9DA9E52-2DDF-40E3-B0A4-4EC7B118FE8F} = {E609A83E-6400-42B0-AD5A-5B006EABC275}
{0AFE156D-0DA5-4B23-8262-CA98E4C0FB5F} = {92201402-E361-440F-95DB-68663D228C2D}
{C3B4FF9C-1C6A-4EA0-9291-E7E0C0EF2BA3} = {92201402-E361-440F-95DB-68663D228C2D}
{EDFA1CB7-1721-4447-9C25-AE110821717C} = {92201402-E361-440F-95DB-68663D228C2D}
{852B0DBD-37F0-4DC0-B966-C284AE03C2F5} = {94DB7354-F5C7-4347-B9EC-FCCA38B86876}
{4D4D2AC7-923D-4219-9BC9-341FBA7FE690} = {92201402-E361-440F-95DB-68663D228C2D}
{D5A7D3A7-E6E8-4153-911D-D7C0C5C8B19C} = {94DB7354-F5C7-4347-B9EC-FCCA38B86876}
{ED216AAD-CBA2-40F2-AA01-63C60E906632} = {94DB7354-F5C7-4347-B9EC-FCCA38B86876}
{F9DA9E52-2DDF-40E3-B0A4-4EC7B118FE8F} = {E609A83E-6400-42B0-AD5A-5B006EABC275}
{39EFEB67-4C0F-4FAD-8FE8-06D7A5D02FEE} = {E609A83E-6400-42B0-AD5A-5B006EABC275}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A594F814-80A8-49D2-B751-B3A58869B30D}
EndGlobalSection
EndGlobal