ContentReader for Compressed (+read content for provider)

This commit is contained in:
2023-09-05 22:24:25 +02:00
parent e6fd8d4ab5
commit def5ece688
15 changed files with 195 additions and 57 deletions

View File

@@ -1,22 +1,42 @@
using System.Text;
using FileTime.Core.ContentAccess;
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
using SharpCompress.Archives;
namespace FileTime.Tools.Compression.ContentProvider;
public class CompressedContentProvider : SubContentProviderBase, ICompressedContentProvider
public sealed class CompressedContentProvider : SubContentProviderBase, ICompressedContentProvider
{
public CompressedContentProvider(
IContentProvider parentContentProvider,
ITimelessContentProvider timelessContentProvider
ITimelessContentProvider timelessContentProvider,
IContentAccessorFactory contentAccessorFactory,
IContentProvider parentContentProvider
)
: base(parentContentProvider, "compression", timelessContentProvider)
: base(
timelessContentProvider,
contentAccessorFactory,
parentContentProvider,
"compression")
{
}
public override Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default)
=> Task.FromResult((byte[]?)"Not implemented..."u8.ToArray());
public override async Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default)
{
var parentElementContext = await GetParentElementReaderAsync(element);
var reader = parentElementContext.ContentReader;
var subPath = parentElementContext.SubNativePath.Path;
await using var readerStream = reader.AsStream();
using var archive = ArchiveFactory.Open(readerStream);
var entry = archive.Entries.First(e => e.Key == subPath);
await using var contentReader= entry.OpenEntryStream();
var data = new byte[1024 * 1024];
var readAsync = await contentReader.ReadAsync(data, cancellationToken);
return data[..readAsync].ToArray();
}
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => throw new NotImplementedException();
}

View File

@@ -3,17 +3,19 @@ using FileTime.Core.Timeline;
namespace FileTime.Tools.Compression.ContentProvider;
public class CompressedContentProviderFactory : ICompressedContentProviderFactory
public sealed class CompressedContentProviderFactory : ICompressedContentProviderFactory
{
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IContentAccessorFactory _contentAccessorFactory;
public CompressedContentProviderFactory(ITimelessContentProvider timelessContentProvider)
public CompressedContentProviderFactory(
ITimelessContentProvider timelessContentProvider,
IContentAccessorFactory contentAccessorFactory)
{
_timelessContentProvider = timelessContentProvider;
_contentAccessorFactory = contentAccessorFactory;
}
public ICompressedContentProvider Create(IContentProvider parentContentProvider)
{
return new CompressedContentProvider(parentContentProvider, _timelessContentProvider);
}
public ICompressedContentProvider Create(IContentProvider parentContentProvider)
=> new CompressedContentProvider(_timelessContentProvider, _contentAccessorFactory, parentContentProvider);
}

View File

@@ -0,0 +1,40 @@
using FileTime.Core.ContentAccess;
using SharpCompress.Archives;
namespace FileTime.Tools.Compression.ContentProvider;
public sealed class CompressedContentReader : IContentReader
{
private readonly IDisposable[] _disposables;
private readonly Stream _stream;
public int PreferredBufferSize => 1024 * 1024;
public long? Position => _stream.Position;
public CompressedContentReader(IArchiveEntry entry, IDisposable[] disposables)
{
_disposables = disposables;
_stream = entry.OpenEntryStream();
}
public void Dispose()
{
_stream.Dispose();
foreach (var disposable in _disposables)
{
disposable.Dispose();
}
}
public async Task<byte[]> ReadBytesAsync(int bufferSize, int? offset = null)
{
var data = new byte[bufferSize];
var read = await _stream.ReadAsync(data, offset ?? 0, bufferSize);
return data[..read].ToArray();
}
public void SetPosition(long position) => _stream.Seek(position, SeekOrigin.Begin);
public Stream AsStream() => _stream;
}

View File

@@ -0,0 +1,34 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
using SharpCompress.Archives;
namespace FileTime.Tools.Compression.ContentProvider;
public sealed class CompressedContentReaderFactory : SubContentReaderBase<CompressedContentProvider>
{
public CompressedContentReaderFactory(IContentAccessorFactory contentAccessorFactory)
: base(contentAccessorFactory)
{
}
public override async Task<IContentReader> CreateContentReaderAsync(IElement element)
{
if (element.Provider is not CompressedContentProvider provider)
throw new ArgumentException(
$"Provider must be {nameof(CompressedContentProvider)}, but it is " + element.Provider.GetType(),
nameof(element));
var parentElementReaderContext = await GetParentElementReaderAsync(element, provider);
var reader = parentElementReaderContext.ContentReader;
var subPath = parentElementReaderContext.SubNativePath;
var readerStream = reader.AsStream();
var archive = ArchiveFactory.Open(readerStream);
var entry = archive.Entries.First(e => e.Key == subPath.Path);
var disposables = new IDisposable[] {archive, readerStream};
return new CompressedContentReader(entry, disposables);
}
}

View File

@@ -28,6 +28,7 @@ public static class Startup
services.AddSingleton<IUserCommandHandler, CompressionUserCommandHandler>();
services.TryAddSingleton<ICompressedContentProviderFactory, CompressedContentProviderFactory>();
services.AddSingleton<ISubContentProvider, CompressedSubContentProvider>();
services.TryAddSingleton<IContentReaderFactory<CompressedContentProvider>, CompressedContentReaderFactory>();
return services;
}
}

View File

@@ -2,7 +2,7 @@
namespace FileTime.Tools.VirtualDiskSources;
public class DiscUtilsInitializer : IPreStartupHandler
public sealed class DiscUtilsInitializer : IPreStartupHandler
{
public Task InitAsync()
{

View File

@@ -5,43 +5,40 @@ using FileTime.Core.Timeline;
namespace FileTime.Tools.VirtualDiskSources;
public class VirtualDiskContentProvider : SubContentProviderBase, IVirtualDiskContentProvider
public sealed class VirtualDiskContentProvider : SubContentProviderBase, IVirtualDiskContentProvider
{
private readonly IContentAccessorFactory _contentAccessorFactory;
public VirtualDiskContentProvider(
IContentProvider parentContentProvider,
ITimelessContentProvider timelessContentProvider,
IContentAccessorFactory contentAccessorFactory)
: base(parentContentProvider, "virtual-disk", timelessContentProvider)
IContentAccessorFactory contentAccessorFactory,
IContentProvider parentContentProvider)
: base(
timelessContentProvider,
contentAccessorFactory,
parentContentProvider,
"virtual-disk"
)
{
_contentAccessorFactory = contentAccessorFactory;
}
public override async Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default)
{
var elementNativePath = element.NativePath!;
var supportedPath = await ParentContentProvider.GetSupportedPathPart(elementNativePath);
if (supportedPath is null) return null;
var parentItem = await ParentContentProvider.GetItemByNativePathAsync(supportedPath, element.PointInTime);
if (parentItem is not IElement parentElement) return null;
var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider);
var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement);
var parentElementContext = await GetParentElementReaderAsync(element);
var reader = parentElementContext.ContentReader;
var subPath = parentElementContext.SubNativePath.Path;
await using var readerStream = reader.AsStream();
using var discReader = new UdfReader(readerStream);
var subPath = elementNativePath.Path.Substring(supportedPath.Path.Length + 2 + Constants.SubContentProviderRootContainer.Length);
var fileInfo = discReader.GetFileInfo(subPath);
await using var contentReader = fileInfo.Open(FileMode.Open, FileAccess.Read);
var data = new byte[1024 * 1024];
var readAsync = await contentReader.ReadAsync(data, cancellationToken);
return data[0..readAsync].ToArray();
return data[..readAsync].ToArray();
}
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path)

View File

@@ -3,7 +3,7 @@ using FileTime.Core.Timeline;
namespace FileTime.Tools.VirtualDiskSources;
public class VirtualDiskContentProviderFactory : IVirtualDiskContentProviderFactory
public sealed class VirtualDiskContentProviderFactory : IVirtualDiskContentProviderFactory
{
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IContentAccessorFactory _contentAccessorFactory;
@@ -17,5 +17,5 @@ public class VirtualDiskContentProviderFactory : IVirtualDiskContentProviderFact
}
public IVirtualDiskContentProvider Create(IContentProvider parentContentProvider)
=> new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider, _contentAccessorFactory);
=> new VirtualDiskContentProvider(_timelessContentProvider, _contentAccessorFactory, parentContentProvider);
}

View File

@@ -2,7 +2,7 @@
namespace FileTime.Tools.VirtualDiskSources;
public class VirtualDiskContentReader : IContentReader
public sealed class VirtualDiskContentReader : IContentReader
{
private readonly Stream _stream;
private readonly ICollection<IDisposable> _disposables;

View File

@@ -4,37 +4,28 @@ using FileTime.Core.Models;
namespace FileTime.Tools.VirtualDiskSources;
public class VirtualDiskContentReaderFactory : IContentReaderFactory<VirtualDiskContentProvider>
public sealed class VirtualDiskContentReaderFactory : SubContentReaderBase<VirtualDiskContentProvider>
{
private readonly IContentAccessorFactory _contentAccessorFactory;
public VirtualDiskContentReaderFactory(IContentAccessorFactory contentAccessorFactory)
public VirtualDiskContentReaderFactory(IContentAccessorFactory contentAccessorFactory)
: base(contentAccessorFactory)
{
_contentAccessorFactory = contentAccessorFactory;
}
public async Task<IContentReader> CreateContentReaderAsync(IElement element)
public override async Task<IContentReader> CreateContentReaderAsync(IElement element)
{
if (element.Provider is not VirtualDiskContentProvider provider)
throw new ArgumentException(
"Provider must be VirtualDiskContentProvider, but it is " + element.Provider.GetType(),
$"Provider must be {nameof(VirtualDiskContentProvider)}, but it is " + element.Provider.GetType(),
nameof(element));
var elementNativePath = element.NativePath!;
var supportedPath = (await provider.ParentContentProvider.GetSupportedPathPart(elementNativePath))!;
var parentElement = (IElement) await provider.ParentContentProvider.GetItemByNativePathAsync(supportedPath, element.PointInTime);
var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider);
var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement);
var parentElementReaderContext = await GetParentElementReaderAsync(element, provider);
var reader = parentElementReaderContext.ContentReader;
var subPath = parentElementReaderContext.SubNativePath;
var readerStream = reader.AsStream();
var discReader = new UdfReader(readerStream);
var subPath = elementNativePath.Path.Substring(supportedPath.Path.Length + 2 + Constants.SubContentProviderRootContainer.Length);
var fileInfo = discReader.GetFileInfo(subPath);
var fileInfo = discReader.GetFileInfo(subPath.Path);
var contentReader = fileInfo.Open(FileMode.Open, FileAccess.Read);

View File

@@ -8,7 +8,7 @@ using FileTime.Core.Timeline;
namespace FileTime.Tools.VirtualDiskSources;
public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
public sealed class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider
{
private readonly IContentAccessorFactory _contentAccessorFactory;
private readonly ITimelessContentProvider _timelessContentProvider;