using MediaBrowser.Common.Extensions;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading.Tasks;
namespace MediaBrowser.Common.IO
{
///
/// This is a wrapper for storing large numbers of files within a directory on a file system.
/// Simply pass a filename into GetResourcePath and it will return a full path location of where the file should be stored.
///
public class FileSystemRepository : IDisposable
{
///
/// Contains the list of subfolders under the main directory
/// The directory entry is created when the item is first added to the dictionary
///
private readonly ConcurrentDictionary _subFolderPaths = new ConcurrentDictionary();
///
/// The _file locks
///
private readonly NamedLock _fileLocks = new NamedLock();
///
/// Gets or sets the path.
///
/// The path.
protected string Path { get; set; }
///
/// Initializes a new instance of the class.
///
/// The path.
///
public FileSystemRepository(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException();
}
Path = path;
Initialize();
}
///
/// Initializes this instance.
///
protected void Initialize()
{
if (!Directory.Exists(Path))
{
Directory.CreateDirectory(Path);
}
}
///
/// Gets the full path of where a resource should be stored within the repository
///
/// Name of the unique.
/// The file extension.
/// System.String.
///
public string GetResourcePath(string uniqueName, string fileExtension)
{
if (string.IsNullOrEmpty(uniqueName))
{
throw new ArgumentNullException();
}
if (string.IsNullOrEmpty(fileExtension))
{
throw new ArgumentNullException();
}
var filename = uniqueName.GetMD5() + fileExtension;
return GetResourcePath(filename);
}
///
/// Gets the full path of where a file should be stored within the repository
///
/// The filename.
/// System.String.
///
public string GetResourcePath(string filename)
{
if (string.IsNullOrEmpty(filename))
{
throw new ArgumentNullException();
}
return GetInternalResourcePath(filename);
}
///
/// Takes a filename and returns the full path of where it should be stored
///
/// The filename.
/// System.String.
private string GetInternalResourcePath(string filename)
{
var prefix = filename.Substring(0, 1);
var folder = _subFolderPaths.GetOrAdd(prefix, GetCachePath);
return System.IO.Path.Combine(folder, filename);
}
///
/// Creates a subfolder under the image cache directory and returns the full path
///
/// The prefix.
/// System.String.
private string GetCachePath(string prefix)
{
var path = System.IO.Path.Combine(Path, prefix);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
return path;
}
///
/// Determines if a resource is present in the repository
///
/// Name of the unique.
/// The file extension.
/// true if the specified unique name contains resource; otherwise, false.
public bool ContainsResource(string uniqueName, string fileExtension)
{
return ContainsFilePath(GetResourcePath(uniqueName, fileExtension));
}
///
/// Determines if a file with a given name is present in the repository
///
/// The filename.
/// true if the specified filename contains filename; otherwise, false.
///
public bool ContainsFilename(string filename)
{
if (string.IsNullOrEmpty(filename))
{
throw new ArgumentNullException();
}
return ContainsFilePath(GetInternalResourcePath(filename));
}
///
/// Determines if a file is present in the repository
///
/// The path.
/// true if [contains file path] [the specified path]; otherwise, false.
///
public bool ContainsFilePath(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException();
}
return File.Exists(path);
}
///
/// Waits for lock.
///
/// The resource path.
public Task WaitForLockAsync(string resourcePath)
{
return _fileLocks.WaitAsync(resourcePath);
}
///
/// Releases the lock.
///
/// The resource path.
public void ReleaseLock(string resourcePath)
{
_fileLocks.Release(resourcePath);
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
Dispose(true);
}
///
/// Releases unmanaged and - optionally - managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected virtual void Dispose(bool dispose)
{
if (dispose)
{
_fileLocks.Dispose();
}
}
}
}