Return direct stream from archives

This commit is contained in:
Matt Nadareski
2024-07-16 14:58:04 -04:00
parent 203d6ce3df
commit 0fc4e2192d
8 changed files with 115 additions and 173 deletions

View File

@@ -749,7 +749,6 @@ namespace SabreTools.DatTools
try try
{ {
// TODO: Write entry to a temporary file to avoid over-large in-memory streams // TODO: Write entry to a temporary file to avoid over-large in-memory streams
// TODO: Once entry is written, replace GetEntryStream implementations
// TODO: Only write to file if there are multiple dupes // TODO: Only write to file if there are multiple dupes
ItemType itemType = datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue<ItemType>(); ItemType itemType = datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue<ItemType>();
(stream, _) = archive.GetEntryStream(datItem.GetName() ?? itemType.AsStringValue() ?? string.Empty); (stream, _) = archive.GetEntryStream(datItem.GetName() ?? itemType.AsStringValue() ?? string.Empty);

View File

@@ -172,42 +172,26 @@ namespace SabreTools.FileTypes.Archives
/// <inheritdoc/> /// <inheritdoc/>
public override (Stream?, string?) GetEntryStream(string entryName) public override (Stream?, string?) GetEntryStream(string entryName)
{ {
var ms = new MemoryStream();
string? realEntry;
// If we have an invalid file // If we have an invalid file
if (this.Filename == null) if (this.Filename == null)
return (null, null); return (null, null);
try try
{ {
// Decompress the _filename stream // Open the entry stream
realEntry = Path.GetFileNameWithoutExtension(this.Filename); string realEntry = Path.GetFileNameWithoutExtension(this.Filename);
var gz = new gZip(); var gz = new gZip();
ZipReturn ret = gz.ZipFileOpen(this.Filename); ZipReturn ret = gz.ZipFileOpen(this.Filename);
ret = gz.ZipFileOpenReadStream(0, out Stream? gzstream, out ulong streamSize); ret = gz.ZipFileOpenReadStream(0, out Stream? stream, out ulong streamSize);
// Write the file out // Return the stream
byte[] gbuffer = new byte[_bufferSize]; return (stream, realEntry);
int glen;
while ((glen = gzstream!.Read(gbuffer, 0, _bufferSize)) > 0)
{
ms.Write(gbuffer, 0, glen);
ms.Flush();
}
// Dispose of the streams
gzstream.Dispose();
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); logger.Error(ex);
ms = null; return (null, null);
realEntry = null;
} }
return (ms, realEntry);
} }
#endregion #endregion

View File

@@ -137,35 +137,44 @@ namespace SabreTools.FileTypes.Archives
public override (Stream?, string?) GetEntryStream(string entryName) public override (Stream?, string?) GetEntryStream(string entryName)
{ {
#if NET462_OR_GREATER || NETCOREAPP #if NET462_OR_GREATER || NETCOREAPP
var ms = new MemoryStream();
string? realEntry = null;
// If we have an invalid file // If we have an invalid file
if (this.Filename == null) if (this.Filename == null)
return (null, null); return (null, null);
try try
{ {
SharpCompress.Archives.Rar.RarArchive ra = SharpCompress.Archives.Rar.RarArchive.Open(this.Filename, new ReaderOptions { LeaveStreamOpen = false, }); Stream? stream = null;
string? realEntry = null;
var ra = SharpCompress.Archives.Rar.RarArchive.Open(this.Filename, new ReaderOptions { LeaveStreamOpen = false, });
foreach (RarArchiveEntry entry in ra.Entries) foreach (RarArchiveEntry entry in ra.Entries)
{ {
if (entry?.Key != null && !entry.IsDirectory && entry.Key.Contains(entryName)) // Skip invalid entries
{ if (entry?.Key == null || !entry.IsComplete)
// Write the file out continue;
realEntry = entry.Key;
entry.WriteTo(ms); // Skip directory entries
} if (entry.IsDirectory)
continue;
// Skip non-matching keys
if (!entry.Key.Contains(entryName))
continue;
// Open the entry stream
realEntry = entry.Key;
stream = entry.OpenEntryStream();
break;
} }
ra.Dispose(); ra.Dispose();
return (stream, realEntry);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); logger.Error(ex);
ms = null; return (null, null);
realEntry = null;
} }
return (ms, realEntry);
#else #else
// TODO: Support RAR archives in old .NET // TODO: Support RAR archives in old .NET
return (null, null); return (null, null);

View File

@@ -223,70 +223,51 @@ namespace SabreTools.FileTypes.Archives
/// <inheritdoc/> /// <inheritdoc/>
public override (Stream?, string?) GetEntryStream(string entryName) public override (Stream?, string?) GetEntryStream(string entryName)
{ {
var ms = new MemoryStream();
string? realEntry = null;
// If we have an invalid file // If we have an invalid file
if (this.Filename == null) if (this.Filename == null)
return (null, null); return (null, null);
try try
{ {
SevenZ zf = new(); Stream? stream = null;
string? realEntry = null;
var zf = new SevenZ();
ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true); ZipReturn zr = zf.ZipFileOpen(this.Filename, -1, true);
if (zr != ZipReturn.ZipGood) if (zr != ZipReturn.ZipGood)
{
throw new Exception(CompressUtils.ZipErrorMessageText(zr)); throw new Exception(CompressUtils.ZipErrorMessageText(zr));
}
for (int i = 0; i < zf.LocalFilesCount() && zr == ZipReturn.ZipGood; i++) for (int i = 0; i < zf.LocalFilesCount() && zr == ZipReturn.ZipGood; i++)
{ {
if (zf.GetLocalFile(i).Filename!.Contains(entryName)) // Get the entry
{ var entry = zf.GetLocalFile(i);
// Open the read stream
realEntry = zf.GetLocalFile(i).Filename;
zr = zf.ZipFileOpenReadStream(i, out Stream? readStream, out ulong streamsize);
// If the stream is smaller than the buffer, just run one loop through to avoid issues // Skip invalid entries
if (streamsize < _bufferSize) if (entry.Filename == null)
{ continue;
byte[] ibuffer = new byte[streamsize];
int ilen = readStream!.Read(ibuffer, 0, (int)streamsize);
ms.Write(ibuffer, 0, ilen);
ms.Flush();
}
// Otherwise, we do the normal loop
else
{
byte[] ibuffer = new byte[_bufferSize];
int ilen;
while (streamsize > _bufferSize)
{
ilen = readStream!.Read(ibuffer, 0, _bufferSize);
ms.Write(ibuffer, 0, ilen);
ms.Flush();
streamsize -= _bufferSize;
}
ilen = readStream!.Read(ibuffer, 0, (int)streamsize); // Skip directory entries
ms.Write(ibuffer, 0, ilen); if (entry.IsDirectory)
ms.Flush(); continue;
}
zr = zf.ZipFileCloseReadStream(); // Skip non-matching keys
} if (!entry.Filename.Contains(entryName))
continue;
// Open the entry stream
realEntry = entry.Filename;
zr = zf.ZipFileOpenReadStream(i, out stream, out ulong streamsize);
break;
} }
zf.ZipFileClose(); zf.ZipFileClose();
return (stream, realEntry);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); logger.Error(ex);
ms = null; return (null, null);
realEntry = null;
} }
return (ms, realEntry);
} }
#endregion #endregion

View File

@@ -138,31 +138,40 @@ namespace SabreTools.FileTypes.Archives
public override (Stream?, string?) GetEntryStream(string entryName) public override (Stream?, string?) GetEntryStream(string entryName)
{ {
#if NET462_OR_GREATER || NETCOREAPP #if NET462_OR_GREATER || NETCOREAPP
var ms = new MemoryStream();
string? realEntry = null;
try try
{ {
TarArchive ta = TarArchive.Open(this.Filename!, new ReaderOptions { LeaveStreamOpen = false, }); Stream? stream = null;
string? realEntry = null;
var ta = TarArchive.Open(this.Filename!, new ReaderOptions { LeaveStreamOpen = false, });
foreach (TarArchiveEntry entry in ta.Entries) foreach (TarArchiveEntry entry in ta.Entries)
{ {
if (entry?.Key != null && !entry.IsDirectory && entry.Key.Contains(entryName)) // Skip invalid entries
{ if (entry?.Key == null || !entry.IsComplete)
// Write the file out continue;
realEntry = entry.Key;
entry.WriteTo(ms); // Skip directory entries
} if (entry.IsDirectory)
continue;
// Skip non-matching keys
if (!entry.Key.Contains(entryName))
continue;
// Open the entry stream
realEntry = entry.Key;
stream = entry.OpenEntryStream();
break;
} }
ta.Dispose(); ta.Dispose();
return (stream, realEntry);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); logger.Error(ex);
ms = null; return (null, null);
realEntry = null;
} }
return (ms, realEntry);
#else #else
// TODO: Support tape archives in old .NET // TODO: Support tape archives in old .NET
return (null, null); return (null, null);

View File

@@ -155,36 +155,24 @@ namespace SabreTools.FileTypes.Archives
public override (Stream?, string?) GetEntryStream(string entryName) public override (Stream?, string?) GetEntryStream(string entryName)
{ {
#if NET462_OR_GREATER || NETCOREAPP #if NET462_OR_GREATER || NETCOREAPP
var ms = new MemoryStream(); // If we have an invalid file
string? realEntry; if (this.Filename == null)
return (null, null);
try try
{ {
// Decompress the _filename stream // Open the entry stream
realEntry = Path.GetFileNameWithoutExtension(this.Filename); string realEntry = Path.GetFileNameWithoutExtension(this.Filename);
var xz = new XZStream(File.OpenRead(this.Filename!)); var stream = new XZStream(File.OpenRead(this.Filename));
// Write the file out // Return the stream
byte[] xbuffer = new byte[_bufferSize]; return (stream, realEntry);
int xlen;
while ((xlen = xz.Read(xbuffer, 0, _bufferSize)) > 0)
{
ms.Write(xbuffer, 0, xlen);
ms.Flush();
}
// Dispose of the streams
xz.Dispose();
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); logger.Error(ex);
ms = null; return (null, null);
realEntry = null;
} }
return (ms, realEntry);
#else #else
// TODO: Support XZ archives in old .NET // TODO: Support XZ archives in old .NET
return (null, null); return (null, null);

View File

@@ -198,66 +198,51 @@ namespace SabreTools.FileTypes.Archives
/// <inheritdoc/> /// <inheritdoc/>
public override (Stream?, string?) GetEntryStream(string entryName) public override (Stream?, string?) GetEntryStream(string entryName)
{ {
var ms = new MemoryStream(); // If we have an invalid file
string? realEntry = null; if (this.Filename == null)
return (null, null);
try try
{ {
Zip zf = new(); Stream? stream = null;
string? realEntry = null;
var zf = new Zip();
ZipReturn zr = zf.ZipFileOpen(this.Filename!, -1, true); ZipReturn zr = zf.ZipFileOpen(this.Filename!, -1, true);
if (zr != ZipReturn.ZipGood) if (zr != ZipReturn.ZipGood)
{
throw new Exception(CompressUtils.ZipErrorMessageText(zr)); throw new Exception(CompressUtils.ZipErrorMessageText(zr));
}
for (int i = 0; i < zf.LocalFilesCount() && zr == ZipReturn.ZipGood; i++) for (int i = 0; i < zf.LocalFilesCount() && zr == ZipReturn.ZipGood; i++)
{ {
if (zf.GetLocalFile(i).Filename!.Contains(entryName)) // Get the entry
{ var entry = zf.GetLocalFile(i);
// Open the read stream
realEntry = zf.GetLocalFile(i).Filename;
zr = zf.ZipFileOpenReadStream(i, false, out Stream? readStream, out ulong streamsize, out ushort cm);
// If the stream is smaller than the buffer, just run one loop through to avoid issues // Skip invalid entries
if (streamsize < _bufferSize) if (entry.Filename == null)
{ continue;
byte[] ibuffer = new byte[streamsize];
int ilen = readStream!.Read(ibuffer, 0, (int)streamsize);
ms.Write(ibuffer, 0, ilen);
ms.Flush();
}
// Otherwise, we do the normal loop
else
{
byte[] ibuffer = new byte[_bufferSize];
int ilen;
while (streamsize > _bufferSize)
{
ilen = readStream!.Read(ibuffer, 0, _bufferSize);
ms.Write(ibuffer, 0, ilen);
ms.Flush();
streamsize -= _bufferSize;
}
ilen = readStream!.Read(ibuffer, 0, (int)streamsize); // Skip directory entries
ms.Write(ibuffer, 0, ilen); if (entry.IsDirectory)
ms.Flush(); continue;
}
zr = zf.ZipFileCloseReadStream(); // Skip non-matching keys
} if (!entry.Filename.Contains(entryName))
continue;
// Open the entry stream
realEntry = entry.Filename;
zr = zf.ZipFileOpenReadStream(i, out stream, out ulong streamsize);
break;
} }
zf.ZipFileClose(); zf.ZipFileClose();
return (stream, realEntry);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); logger.Error(ex);
ms = null; return (null, null);
realEntry = null;
} }
return (ms, realEntry);
} }
#endregion #endregion
@@ -779,7 +764,7 @@ namespace SabreTools.FileTypes.Archives
// Instantiate the streams // Instantiate the streams
oldZipFile.ZipFileOpenReadStream(index, false, out Stream? zreadStream, out ulong istreamSize, out ushort icompressionMethod); oldZipFile.ZipFileOpenReadStream(index, false, out Stream? zreadStream, out ulong istreamSize, out ushort icompressionMethod);
long msDosDateTime = oldZipFile.GetLocalFile(index).LastModified; long msDosDateTime = oldZipFile.GetLocalFile(index).LastModified;
TimeStamps ts = new() { ModTime = msDosDateTime }; TimeStamps ts = new() { ModTime = msDosDateTime };
zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.GetLocalFile(index).Filename!, istreamSize, (ushort)CompressionMethod.Deflated, out writeStream, ts); zipFile.ZipFileOpenWriteStream(false, true, oldZipFile.GetLocalFile(index).Filename!, istreamSize, (ushort)CompressionMethod.Deflated, out writeStream, ts);
// Copy the input stream to the output // Copy the input stream to the output

View File

@@ -212,9 +212,6 @@ namespace SabreTools.FileTypes
/// <returns>Stream representing the entry, null on error</returns> /// <returns>Stream representing the entry, null on error</returns>
public virtual (Stream?, string?) GetEntryStream(string entryName) public virtual (Stream?, string?) GetEntryStream(string entryName)
{ {
var ms = new MemoryStream();
string? realentry = null;
// If we have an invalid filename // If we have an invalid filename
if (this.Filename == null) if (this.Filename == null)
return (null, null); return (null, null);
@@ -231,30 +228,20 @@ namespace SabreTools.FileTypes
// Now sort through to find the first file that matches // Now sort through to find the first file that matches
string? match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault(); string? match = files.Where(s => s.EndsWith(entryName)).FirstOrDefault();
// If we had a file, copy that over to the new name // If we had a file, open and return the stream
if (!string.IsNullOrEmpty(match)) if (!string.IsNullOrEmpty(match))
{ {
#if NET20 || NET35 var stream = File.OpenRead(match);
var tempStream = File.OpenRead(match); return (stream, match);
byte[] buffer = new byte[32768];
int read;
while ((read = tempStream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
#else
File.OpenRead(match).CopyTo(ms);
#endif
realentry = match;
} }
return (null, null);
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex); logger.Error(ex);
return (ms, realentry); return (null, null);
} }
return (ms, realentry);
} }
#endregion #endregion