Skip to content

Instantly share code, notes, and snippets.

@DiracSpace
Created June 26, 2023 23:38
Show Gist options
  • Save DiracSpace/38944d70edcec41e5f05c903321f3d3f to your computer and use it in GitHub Desktop.
Save DiracSpace/38944d70edcec41e5f05c903321f3d3f to your computer and use it in GitHub Desktop.
using Asterias.Application.Providers;
using Microsoft.AspNetCore.Http;
namespace Asterias.Infrastructure.Providers.File.LocalFileProvider
{
public class LocalFileProvider : IFileProvider
{
/// <summary>
/// Used for limiting amount of buffer
/// in memory
/// <see href="https://stackoverflow.com/questions/3033771/file-i-o-with-streams-best-memory-buffer-size">Best memory buffer size for streams</see>
/// </summary>
private const int BUFFER_SIZE = 0x4096;
/// <summary>
/// Create async resource for reading file
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private FileStream OpenForReading(string path)
{
ArgumentNullException.ThrowIfNull(nameof(path));
return new FileStream(
path,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
BUFFER_SIZE,
true
);
}
/// <summary>
/// Create async resource for writing/creating file
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private FileStream OpenForWriting(string path)
{
ArgumentNullException.ThrowIfNull(nameof(path));
return new FileStream(
path,
FileMode.OpenOrCreate,
FileAccess.Write,
FileShare.None,
BUFFER_SIZE,
true
);
}
/// <summary>
/// Creates async resources for copying an existent
/// file from originPath to destinatioPath
/// </summary>
/// <param name="originPath"></param>
/// <param name="destinationPath"></param>
/// <returns></returns>
/// <exception cref="DirectoryNotFoundException"></exception>
/// <exception cref="UnauthorizedAccessException"></exception>
public async Task CopyAsync(string originPath, string destinationPath)
{
ArgumentNullException.ThrowIfNull(nameof(originPath));
ArgumentNullException.ThrowIfNull(nameof(destinationPath));
using var originFileStream = OpenForReading(originPath);
using var destinationFileStream = OpenForWriting(destinationPath);
if (!originFileStream.CanRead)
throw new UnauthorizedAccessException();
if (!destinationFileStream.CanWrite)
throw new UnauthorizedAccessException();
await originFileStream.CopyToAsync(destinationFileStream);
}
/// <summary>
/// Copies an existing file to a new file, removing the
/// original file.
/// </summary>
/// <param name="originPath"></param>
/// <param name="destinationPath"></param>
/// <returns></returns>
public async Task MoveAsync(string originPath, string destinationPath)
{
await CopyAsync(originPath, destinationPath);
Remove(originPath);
}
/// <summary>
/// Reads an entire file content as a byte array
/// </summary>
/// <param name="path">Full file path</param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public async Task<byte[]> ReadAsync(string path)
{
ArgumentNullException.ThrowIfNull(path, nameof(path));
using var fileStream = OpenForReading(path);
byte[] buffer = new byte[fileStream.Length];
await fileStream.ReadAsync(
buffer.AsMemory(0, (int)fileStream.Length)
);
return buffer;
}
/// <summary>
/// Opens an async resource for reading the contents of the file
/// into a string.
/// </summary>
/// <param name="path"></param>
/// <param name="encoding"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
/// <exception cref="ArgumentNullException"></exception>
public async Task<string> ReadAllTextAsync(string path)
{
ArgumentNullException.ThrowIfNull(nameof(path));
using var fileStream = new StreamReader(path);
string fileContents = await fileStream.ReadToEndAsync();
return fileContents;
}
/// <summary>
/// Opens an async resource for reading line by line the contents
/// of the provided file.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public async Task<IEnumerable<string>> ReadAllLinesAsync(string path)
{
ArgumentNullException.ThrowIfNull(nameof(path));
var lines = new List<string>();
using var streamReader = new StreamReader(path);
string? line = default;
while ((line = await streamReader.ReadLineAsync()) is not null)
lines.Add(line);
return lines;
}
/// <summary>
/// Tri
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public void Remove(string path)
{
ArgumentNullException.ThrowIfNull(nameof(path));
System.IO.File.Delete(path);
}
/// <summary>
/// Opens an async resource for copying contents
/// of file.
/// </summary>
/// <param name="file"></param>
/// <param name="path"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="UnauthorizedAccessException"></exception>
/// <exception cref="DirectoryNotFoundException"></exception>
public async Task SaveAsync(IFormFile file, string path)
{
ArgumentNullException.ThrowIfNull(nameof(file));
ArgumentNullException.ThrowIfNull(nameof(path));
using var fileStream = OpenForWriting(path);
// TODO: this isn't working for now, but it's mentioned in file security
// should it be included too, or is this over complicating things?
// https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-7.0#validation
/*string fileName = System.IO.Path.GetFileName(path);
string fileExtension = System.IO.Path.GetExtension(fileName);
var signatureValidations = new FileSignatureValidations();
if (!signatureValidations.IsSignatureValid(fileExtension, fileStream))
throw new InvalidOperationException();*/
if (!fileStream.CanWrite)
throw new UnauthorizedAccessException();
await file.CopyToAsync(fileStream);
}
/// <summary>
/// Saves multiple files asynchronously
/// </summary>
/// <param name="files"></param>
/// <param name="path"></param>
/// <returns></returns>
public async Task SaveAllAsync(IEnumerable<IFormFile> files, string path)
{
foreach (var file in files)
await SaveAsync(file, path);
}
/// <summary>
/// Opens an async binary resource for writing the contents
/// of the binary array.
/// </summary>
/// <param name="content"></param>
/// <param name="path"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="UnauthorizedAccessException"></exception>
/// <exception cref="DirectoryNotFoundException"></exception>
public async Task SaveAllBytesAsync(byte[] content, string path)
{
ArgumentNullException.ThrowIfNull(nameof(content));
ArgumentNullException.ThrowIfNull(nameof(path));
using var fileStream = OpenForWriting(path);
if (!fileStream.CanWrite)
throw new UnauthorizedAccessException();
await fileStream.WriteAsync(content);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment