Files
Radicade/Server/Storage/StudioAssetStorage.cs
T
2026-10-16 23:33:13 -07:00

55 lines
2.0 KiB
C#

using System.Numerics;
using System.Security.Cryptography;
namespace RadicadeServer.Storage;
public sealed record StudioAssetUpload(string Filename, string Hash, long Length);
public static class StudioAssetStorage
{
public static string Filename(byte[] digest)
{
var value = new BigInteger(digest, isUnsigned: true, isBigEndian: false);
const string alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
var result = "";
while (value > 0)
{
value = BigInteger.DivRem(value, 36, out var remainder);
result = alphabet[(int)remainder] + result;
}
return result.PadLeft(50, '0')[..40];
}
public static async Task<StudioAssetUpload> Save(IFormFile file, CancellationToken cancellation)
{
await using var input = file.OpenReadStream();
return await Save(input, cancellation);
}
public static async Task<StudioAssetUpload> Save(Stream input, CancellationToken cancellation)
{
Directory.CreateDirectory(DBConfig.CDNStorage);
var temporary = Path.Combine(DBConfig.CDNStorage, Guid.NewGuid() + ".uploading");
try
{
await using (var output = System.IO.File.Create(temporary))
await input.CopyToAsync(output, cancellation);
var length = new FileInfo(temporary).Length;
byte[] hash;
await using (var storedFile = System.IO.File.OpenRead(temporary))
hash = await SHA256.HashDataAsync(storedFile, cancellation);
var filename = Filename(hash);
var destination = Path.Combine(DBConfig.CDNStorage, filename);
try { System.IO.File.Move(temporary, destination); }
catch (IOException) when (System.IO.File.Exists(destination)) { }
return new StudioAssetUpload(filename, Convert.ToBase64String(hash), length);
}
finally
{
if (System.IO.File.Exists(temporary)) System.IO.File.Delete(temporary);
}
}
}