kamihama-server/KamihamaWeb/Services/DiskCacheService.cs

124 lines
4.2 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using KamihamaWeb.Interfaces;
using KamihamaWeb.Util;
using Microsoft.Extensions.Configuration;
using RestSharp;
using Serilog;
namespace KamihamaWeb.Services
{
public class DiskCacheService: IDiskCacheSingleton
{
public DiskCacheService(IConfiguration config, IRestSharpTransient rest) : this(Guid.NewGuid(), config, rest)
{
}
public DiskCacheService(Guid guid, IConfiguration config, IRestSharpTransient rest)
{
Guid = guid;
Rest = rest;
if (string.IsNullOrEmpty(config["MagiRecoServer:CacheDirectory"]))
{
throw new Exception("CacheDirectory is not set in the configuration! Exiting.");
}
CacheDirectory = config["MagiRecoServer:CacheDirectory"];
Directory.CreateDirectory(CacheDirectory);
}
public Guid Guid { get; }
private IRestSharpTransient Rest { get; set; }
private string CacheDirectory { get; set; } = "";
public async Task<Tuple<int, Stream>> Get(string cacheItem, string versionMd5)
{
var filename = CryptUtil.CalculateSha256(cacheItem + "?" + versionMd5);
var filePath = Path.Combine(CacheDirectory, filename);
if (File.Exists(filePath))
{
Log.Debug($"Loading {cacheItem} from disk ({filePath}).");
var maxLoops = 5;
while (maxLoops-- > 0)
{
try
{
var stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return new Tuple<int, Stream>(0, stream);
}
catch (IOException) // File in use, wait
{
Log.Debug("Failed, file is already being downloaded, retrying in 500ms.");
await Task.Delay(500);
}
}
Log.Warning($"Max loops exceeded in DiskCacheService.Get() for {cacheItem}.");
return new Tuple<int, Stream>(500, null);
}
Log.Debug($"Fetching {cacheItem}.");
return await FastFetch(cacheItem, filePath, versionMd5);
}
private async Task<Tuple<int, Stream>> FastFetch(string item, string path, string md5)
{
var maxLoops = 5;
var deleteFlag = false;
while (maxLoops-- > 0)
{
try
{
FileStream file = File.Open(path, FileMode.Create, FileAccess.ReadWrite,
FileShare.None); // Open file
try
{
if (file.Length > 0)
{
return new Tuple<int, Stream>(0, file);
}
var stream = await Rest.FetchAsset(item + $"?{md5}");
if (stream.Item2 == null)
{
deleteFlag = true;
return stream;
}
((MemoryStream) stream.Item2).WriteTo(file);
stream.Item2.Seek(0, SeekOrigin.Begin);
return stream;
}
catch (Exception)
{
deleteFlag = true;
}
finally
{
file.Close();
await file.DisposeAsync();
if (deleteFlag)
{
File.Delete(path);
}
}
}
catch (IOException)
{
await Task.Delay(500);
}
}
Log.Warning($"Max loops exceeded in DiskCacheService.FastFetch() for {item}.");
File.Delete(path);
return new Tuple<int, Stream>(500, null);
}
}
}