Commit 5d59866c authored by Alessio Parma's avatar Alessio Parma 🐔

cleanup

parent 2e3cecf6
......@@ -22,17 +22,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using PommaLabs.KVLite;
using PommaLabs.MimeTypes;
using PommaLabs.Thumbnailer.Client;
using PommaLabs.Thumbnailer.Models.Configurations;
namespace PommaLabs.Thumbnailer.Models.DTO
{
......@@ -163,51 +156,9 @@ public TempFileDTO(string contentType, string? extension)
public long Size => (_path != null && _path.IsFile) ? new FileInfo(_path.AbsolutePath).Length : 0L;
/// <summary>
/// Checks the cache looking for a file with same hash of given <paramref name="file"/>.
/// If it finds a file with the same hash, then it deletes specified
/// <paramref name="file"/> and it returns the file found in cache. Otherwise, the file is
/// cached and it is returned to the caller.
/// Internal path, used for low level operations.
/// </summary>
/// <param name="cache">Cache.</param>
/// <param name="databaseConfiguration">Database configuration.</param>
/// <param name="file">
/// File which might be deleted if a file with the same hash exists in the cache.
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>
/// The original file, if it does not exist in the cache. Otherwise, it returns the cached file.
/// </returns>
/// <remarks>This method is used to avoid flooding the server with the same files.</remarks>
public static async Task<TempFileDTO> CheckCacheAsync(
ICache cache, IOptions<DatabaseConfiguration> databaseConfiguration,
TempFileDTO file, CancellationToken cancellationToken)
{
if (file._path == null || !file._path.IsFile)
{
return file;
}
string hash;
using (var fileStream = File.OpenRead(file.Path))
{
hash = ComputeHash(fileStream);
}
var cachedFile = await cache.GetOrAddSlidingAsync(
partition: nameof(TempFileDTO),
key: new CacheKey(hash),
valueGetter: () => Task.FromResult(file),
interval: databaseConfiguration.Value.CacheLifetime,
cancellationToken: cancellationToken,
continueOnCapturedContext: false).ConfigureAwait(false);
if (cachedFile.Path != file.Path)
{
File.Delete(file.Path);
}
return cachedFile;
}
internal Uri? InternalPath => _path;
/// <inheritdoc/>
public CacheKey GetCacheKey() => Path;
......@@ -221,26 +172,6 @@ public void SetPath(Uri path)
_path = path;
}
private static string ComputeHash(Stream stream)
{
stream.Position = 0L;
using var hashStream = SHA384.Create();
var hashBytes = hashStream.ComputeHash(stream);
// Create a new Stringbuilder to collect the bytes and create a string.
var hashString = new StringBuilder();
// Loop through each byte of the hashed data and format each one as a hexadecimal string.
for (var i = 0; i < hashBytes.Length; i++)
{
hashString.Append(hashBytes[i].ToString("x2", CultureInfo.InvariantCulture));
}
// Return the hexadecimal string.
return hashString.ToString();
}
private static void TryImproveFileInformation(ref string contentType, ref string? extension)
{
if (!string.IsNullOrWhiteSpace(extension))
......
......@@ -115,7 +115,7 @@ private async Task DeleteOldTempFilesAsync(CancellationToken stoppingToken)
}
_logger.LogDebug("File '{FileName}' has a last access time older than {Threshold}, it will be deleted", fileName, threshold);
File.Delete(file.Path);
await _tempFileStore.DeleteTempFileAsync(file, stoppingToken).ConfigureAwait(false);
deletedFileCount++;
}
......
......@@ -61,12 +61,7 @@ public async Task<TempFileDTO> DownloadFileAsync(Uri fileUri, CancellationToken
{
return await _cache.GetOrAddSlidingAsync(
partition: nameof(IDownloadManager),
key: new CacheKey(nameof(DownloadFileAsync), fileUri),
valueGetter: async () =>
{
var file = await _downloadManager.DownloadFileAsync(fileUri, cancellationToken).ConfigureAwait(false);
return await TempFileDTO.CheckCacheAsync(_cache, _databaseConfiguration, file, cancellationToken).ConfigureAwait(false);
},
valueExpression: () => _downloadManager.DownloadFileAsync(fileUri, cancellationToken),
interval: _databaseConfiguration.Value.CacheLifetime,
cancellationToken: cancellationToken,
continueOnCapturedContext: false).ConfigureAwait(false);
......
......@@ -113,7 +113,7 @@ public async Task<TempFileDTO> DownloadFileAsync(Uri fileUri, CancellationToken
await responseStream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
return file;
return await _tempFileStore.HandleFileDownloadAsync(file, cancellationToken).ConfigureAwait(false);
}
}
}
......@@ -22,6 +22,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
......@@ -60,6 +64,12 @@ public sealed class CachingTempFileStore : ITempFileStore
/// <inheritdoc/>
public string RootDirectory => _tempFileStore.RootDirectory;
/// <inheritdoc/>
public Task DeleteTempFileAsync(TempFileDTO file, CancellationToken cancellationToken)
{
return _tempFileStore.DeleteTempFileAsync(file, cancellationToken);
}
/// <inheritdoc/>
public Task<TempFileDTO> GetTempFileAsync(string contentType, string? extension, CancellationToken cancellationToken)
{
......@@ -72,18 +82,87 @@ public Task<IEnumerable<TempFileDTO>> GetTempFilesAsync(CancellationToken cancel
return _tempFileStore.GetTempFilesAsync(cancellationToken);
}
/// <inheritdoc/>
public async Task<TempFileDTO> HandleFileDownloadAsync(TempFileDTO downloadedFile, CancellationToken cancellationToken)
{
return await CheckCacheAsync(downloadedFile, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<TempFileDTO> HandleFileUploadAsync(IFormFile formFile, CancellationToken cancellationToken)
{
var file = await _tempFileStore.HandleFileUploadAsync(formFile, cancellationToken).ConfigureAwait(false);
return await TempFileDTO.CheckCacheAsync(_cache, _databaseConfiguration, file, cancellationToken).ConfigureAwait(false);
return await CheckCacheAsync(file, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<TempFileDTO> HandleFileUploadAsync(UploadedFileDTO externalFile, CancellationToken cancellationToken)
{
var file = await _tempFileStore.HandleFileUploadAsync(externalFile, cancellationToken).ConfigureAwait(false);
return await TempFileDTO.CheckCacheAsync(_cache, _databaseConfiguration, file, cancellationToken).ConfigureAwait(false);
return await CheckCacheAsync(file, cancellationToken).ConfigureAwait(false);
}
private static string ComputeHash(Stream stream)
{
stream.Position = 0L;
using var hashStream = SHA384.Create();
var hashBytes = hashStream.ComputeHash(stream);
// Create a new Stringbuilder to collect the bytes and create a string.
var hashString = new StringBuilder();
// Loop through each byte of the hashed data and format each one as a hexadecimal string.
for (var i = 0; i < hashBytes.Length; i++)
{
hashString.Append(hashBytes[i].ToString("x2", CultureInfo.InvariantCulture));
}
// Return the hexadecimal string.
return hashString.ToString();
}
/// <summary>
/// Checks the cache looking for a file with same hash of given <paramref name="file"/>.
/// If it finds a file with the same hash, then it deletes specified
/// <paramref name="file"/> and it returns the file found in cache. Otherwise, the file is
/// cached and it is returned to the caller.
/// </summary>
/// <param name="file">
/// File which might be deleted if a file with the same hash exists in the cache.
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>
/// The original file, if it does not exist in the cache. Otherwise, it returns the cached file.
/// </returns>
/// <remarks>This method is used to avoid flooding the server with the same files.</remarks>
private async Task<TempFileDTO> CheckCacheAsync(TempFileDTO file, CancellationToken cancellationToken)
{
if (file.InternalPath == null || !file.InternalPath.IsFile)
{
return file;
}
string hash;
using (var fileStream = File.OpenRead(file.Path))
{
hash = ComputeHash(fileStream);
}
var cachedFile = await _cache.GetOrAddSlidingAsync(
partition: nameof(TempFileDTO),
key: new CacheKey(hash),
valueGetter: () => Task.FromResult(file),
interval: _databaseConfiguration.Value.CacheLifetime,
cancellationToken: cancellationToken,
continueOnCapturedContext: false).ConfigureAwait(false);
if (cachedFile.Path != file.Path)
{
await DeleteTempFileAsync(file, cancellationToken).ConfigureAwait(false);
}
return cachedFile;
}
}
}
......@@ -39,6 +39,13 @@ public interface ITempFileStore
/// </summary>
string RootDirectory { get; }
/// <summary>
/// Deletes specified temporary file.
/// </summary>
/// <param name="file">Temporary file.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task DeleteTempFileAsync(TempFileDTO file, CancellationToken cancellationToken);
/// <summary>
/// Creates a temporary file, using given <paramref name="contentType"/> to determine its extension.
/// </summary>
......@@ -69,6 +76,16 @@ public interface ITempFileStore
/// <returns>An enumerable with all temporary files.</returns>
Task<IEnumerable<TempFileDTO>> GetTempFilesAsync(CancellationToken cancellationToken);
/// <summary>
/// Checks whether downloaded file is already available or not among temporary files.
/// </summary>
/// <param name="downloadedFile">Downloaded file.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>
/// The same file, if a local copy was not found. Otherwise, it returns the existing local copy.
/// </returns>
Task<TempFileDTO> HandleFileDownloadAsync(TempFileDTO downloadedFile, CancellationToken cancellationToken);
/// <summary>
/// Makes a local copy of given form file.
/// </summary>
......
......@@ -54,6 +54,13 @@ public SystemTempFileStore(ILogger<SystemTempFileStore> logger)
/// <inheritdoc/>
public string RootDirectory { get; } = Path.Combine(Path.GetTempPath(), "thumbnailer");
/// <inheritdoc/>
public Task DeleteTempFileAsync(TempFileDTO file, CancellationToken cancellationToken)
{
File.Delete(file.Path);
return Task.CompletedTask;
}
/// <inheritdoc/>
public Task<TempFileDTO> GetTempFileAsync(string contentType, string? extension, CancellationToken cancellationToken)
{
......@@ -84,6 +91,12 @@ public Task<IEnumerable<TempFileDTO>> GetTempFilesAsync(CancellationToken cancel
.ToList());
}
/// <inheritdoc/>
public Task<TempFileDTO> HandleFileDownloadAsync(TempFileDTO downloadedFile, CancellationToken cancellationToken)
{
return Task.FromResult(downloadedFile);
}
/// <inheritdoc/>
public async Task<TempFileDTO> HandleFileUploadAsync(IFormFile formFile, CancellationToken cancellationToken)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment