Content Reader/Writer, StreamCopyCommandHandler
This commit is contained in:
@@ -2,14 +2,13 @@ using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Runtime.InteropServices;
|
||||
using DynamicData;
|
||||
using FileTime.App.Core.Models;
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Enums;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Models.Extensions;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
namespace FileTime.Providers.Local;
|
||||
|
||||
public sealed partial class LocalContentProvider : ContentProviderBase, ILocalContentProvider
|
||||
{
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
@@ -21,6 +20,8 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
_timelessContentProvider = timelessContentProvider;
|
||||
_isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
SupportsContentStreams = true;
|
||||
|
||||
RefreshRootDirectories();
|
||||
|
||||
Items.OnNext(_rootDirectories.Connect());
|
||||
@@ -50,11 +51,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
{
|
||||
var rootDrive = _rootDirectories
|
||||
.Items
|
||||
.FirstOrDefault(r =>
|
||||
.FirstOrDefault(r =>
|
||||
path.Path.StartsWith(
|
||||
GetNativePath(r.Path).Path,
|
||||
_isCaseInsensitive
|
||||
? StringComparison.InvariantCultureIgnoreCase
|
||||
GetNativePath(r.Path).Path,
|
||||
_isCaseInsensitive
|
||||
? StringComparison.InvariantCultureIgnoreCase
|
||||
: StringComparison.InvariantCulture
|
||||
)
|
||||
);
|
||||
@@ -74,11 +75,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
{
|
||||
if ((path?.Length ?? 0) == 0)
|
||||
{
|
||||
return Task.FromResult((IItem) this);
|
||||
return Task.FromResult((IItem)this);
|
||||
}
|
||||
else if (Directory.Exists(path))
|
||||
{
|
||||
return Task.FromResult((IItem) DirectoryToContainer(
|
||||
return Task.FromResult((IItem)DirectoryToContainer(
|
||||
new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
|
||||
pointInTime,
|
||||
!itemInitializationSettings.SkipChildInitialization)
|
||||
@@ -86,7 +87,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
}
|
||||
else if (File.Exists(path))
|
||||
{
|
||||
return Task.FromResult((IItem) FileToElement(new FileInfo(path), pointInTime));
|
||||
return Task.FromResult((IItem)FileToElement(new FileInfo(path), pointInTime));
|
||||
}
|
||||
|
||||
var type = forceResolvePathType switch
|
||||
@@ -118,10 +119,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
return forceResolvePathType switch
|
||||
{
|
||||
AbsolutePathType.Container => Task.FromResult(
|
||||
(IItem) CreateEmptyContainer(
|
||||
(IItem)CreateEmptyContainer(
|
||||
nativePath,
|
||||
pointInTime,
|
||||
Observable.Return(new List<Exception>() {innerException})
|
||||
Observable.Return(new List<Exception>() { innerException })
|
||||
)
|
||||
),
|
||||
AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)),
|
||||
@@ -236,7 +237,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
SourceCache<AbsolutePath, string>? result = null;
|
||||
try
|
||||
{
|
||||
var items = initializeChildren ? (List<AbsolutePath>?) GetItemsByContainer(directoryInfo, pointInTime) : null;
|
||||
var items = initializeChildren ? (List<AbsolutePath>?)GetItemsByContainer(directoryInfo, pointInTime) : null;
|
||||
if (items != null)
|
||||
{
|
||||
result = new SourceCache<AbsolutePath, string>(i => i.Path.Path);
|
||||
@@ -245,7 +246,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
exceptions.OnNext(new List<Exception>() {e});
|
||||
exceptions.OnNext(new List<Exception>() { e });
|
||||
}
|
||||
|
||||
return Task.FromResult(result?.Connect());
|
||||
@@ -319,10 +320,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
|
||||
var size = maxLength ?? realFileSize switch
|
||||
{
|
||||
> int.MaxValue => int.MaxValue,
|
||||
_ => (int) realFileSize
|
||||
_ => (int)realFileSize
|
||||
};
|
||||
var buffer = new byte[size];
|
||||
await reader.ReadAsync(buffer, 0, size);
|
||||
await reader.ReadAsync(buffer.AsMemory(0, size), cancellationToken);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
71
src/Providers/FileTime.Providers.Local/LocalContentReader.cs
Normal file
71
src/Providers/FileTime.Providers.Local/LocalContentReader.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using FileTime.Core.ContentAccess;
|
||||
|
||||
namespace FileTime.Providers.Local;
|
||||
|
||||
public class LocalContentReader : IContentReader
|
||||
{
|
||||
private readonly FileStream _readerStream;
|
||||
private readonly BinaryReader _binaryReader;
|
||||
private bool _disposed;
|
||||
|
||||
public int PreferredBufferSize => 1024 * 1024;
|
||||
public long? Position { get; private set; }
|
||||
|
||||
public LocalContentReader(FileStream readerStream)
|
||||
{
|
||||
_readerStream = readerStream;
|
||||
_binaryReader = new BinaryReader(_readerStream);
|
||||
}
|
||||
|
||||
public Task<byte[]> ReadBytesAsync(int bufferSize, int? offset = null)
|
||||
{
|
||||
var max = bufferSize > 0 && bufferSize < PreferredBufferSize ? bufferSize : PreferredBufferSize;
|
||||
|
||||
if (offset != null)
|
||||
{
|
||||
if (Position == null) Position = 0;
|
||||
var buffer = new byte[max];
|
||||
var bytesRead = _binaryReader.Read(buffer, offset.Value, max);
|
||||
Position += bytesRead;
|
||||
|
||||
if (buffer.Length != bytesRead)
|
||||
{
|
||||
Array.Resize(ref buffer, bytesRead);
|
||||
}
|
||||
return Task.FromResult(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(_binaryReader.ReadBytes(max));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPosition(long position)
|
||||
{
|
||||
Position = position;
|
||||
}
|
||||
|
||||
~LocalContentReader()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_readerStream.Dispose();
|
||||
_binaryReader.Dispose();
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Providers.Local;
|
||||
|
||||
public class LocalContentReaderFactory : IContentReaderFactory<ILocalContentProvider>
|
||||
{
|
||||
public Task<IContentReader> CreateContentReaderAsync(IElement element)
|
||||
=> Task.FromResult((IContentReader)new LocalContentReader(File.OpenRead(element.NativePath!.Path)));
|
||||
}
|
||||
60
src/Providers/FileTime.Providers.Local/LocalContentWriter.cs
Normal file
60
src/Providers/FileTime.Providers.Local/LocalContentWriter.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using FileTime.Core.ContentAccess;
|
||||
|
||||
namespace FileTime.Providers.Local;
|
||||
|
||||
public class LocalContentWriter : IContentWriter
|
||||
{
|
||||
private readonly FileStream _writerStream;
|
||||
private readonly BinaryWriter _binaryWriter;
|
||||
private bool disposed;
|
||||
public int PreferredBufferSize => 1024 * 1024;
|
||||
|
||||
public LocalContentWriter(FileStream writerStream)
|
||||
{
|
||||
_writerStream = writerStream;
|
||||
_binaryWriter = new BinaryWriter(_writerStream);
|
||||
}
|
||||
|
||||
public Task WriteBytesAsync(byte[] data, int? index = null)
|
||||
{
|
||||
if (index != null)
|
||||
{
|
||||
_binaryWriter.Write(data, index.Value, data.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
_binaryWriter.Write(data);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task FlushAsync()
|
||||
{
|
||||
_binaryWriter.Flush();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
~LocalContentWriter()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_writerStream.Dispose();
|
||||
_binaryWriter.Dispose();
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using FileTime.Core.ContentAccess;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Providers.Local;
|
||||
|
||||
public class LocalContentWriterFactory : IContentWriterFactory<ILocalContentProvider>
|
||||
{
|
||||
public Task<IContentWriter> CreateContentWriterAsync(IElement element)
|
||||
=> Task.FromResult((IContentWriter)new LocalContentWriter(File.OpenRead(element.NativePath!.Path)));
|
||||
}
|
||||
@@ -12,6 +12,10 @@ public static class Startup
|
||||
serviceCollection.TryAddSingleton<IContentProvider>(sp => sp.GetRequiredService<ILocalContentProvider>());
|
||||
serviceCollection.TryAddSingleton<IItemCreator<ILocalContentProvider>, LocalItemCreator>();
|
||||
serviceCollection.TryAddSingleton<IItemCreator<LocalContentProvider>>(sp => sp.GetRequiredService<IItemCreator<ILocalContentProvider>>());
|
||||
serviceCollection.TryAddSingleton<IContentReaderFactory<ILocalContentProvider>, LocalContentReaderFactory>();
|
||||
serviceCollection.TryAddSingleton<IContentReaderFactory<LocalContentProvider>>(sp => sp.GetRequiredService<IContentReaderFactory<ILocalContentProvider>>());
|
||||
serviceCollection.TryAddSingleton<IContentWriterFactory<ILocalContentProvider>, LocalContentWriterFactory>();
|
||||
serviceCollection.TryAddSingleton<IContentWriterFactory<LocalContentProvider>>(sp => sp.GetRequiredService<IContentWriterFactory<ILocalContentProvider>>());
|
||||
return serviceCollection;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user