Files
UnrealEngineUWP/Engine/Source/Programs/Shared/EpicGames.Core/ZipArchiveExtensions.cs
justin marcus e489d8065b Unzip optimization for archives with lots of small files
- Remove redunant exists check when creating directories.  Directory.CreateDirectory already does that.
- Use streams directly instead of calling ZipFileExtensions.ExtractToFile.


[REVIEW] [at]Mitchell.Fisher [at]Graeme.Thornton
#preflight https://horde.devtools.epicgames.com/job/62bf34b2b024a2608c89b606

#ROBOMERGE-OWNER: justin.marcus
#ROBOMERGE-AUTHOR: justin.marcus
#ROBOMERGE-SOURCE: CL 20919800 via CL 20919810 via CL 20919999 via CL 20920500
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v971-20777995)

[CL 20923415 by justin marcus in ue5-main branch]
2022-07-01 19:42:38 -04:00

88 lines
3.1 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Runtime.InteropServices;
namespace EpicGames.Core
{
/// <summary>
/// Additional functionality around <see cref="System.IO.Compression.ZipArchive"/> to support non-Windows filesystems
/// </summary>
public static class ZipArchiveExtensions
{
/// <summary>
/// Create a zip archive entry, preserving platform mode bits
/// </summary>
/// <param name="destination"></param>
/// <param name="sourceFileName"></param>
/// <param name="entryName"></param>
/// <param name="compressionLevel"></param>
/// <returns></returns>
public static ZipArchiveEntry CreateEntryFromFile_CrossPlatform(this ZipArchive destination, string sourceFileName, string entryName, CompressionLevel compressionLevel)
{
ZipArchiveEntry entry = ZipFileExtensions.CreateEntryFromFile(destination, sourceFileName, entryName, compressionLevel);
int result = -1;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
result = FileUtils.GetFileMode_Linux(sourceFileName);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
result = FileUtils.GetFileMode_Mac(sourceFileName);
}
if(result >= 0)
{
entry.ExternalAttributes = (int)result << 16;
}
return entry;
}
/// <summary>
/// Internal field storing information about the platform that created a ZipArchiveEntry. Cannot interpret how to treat the attribute bits without reading this.
/// </summary>
static readonly FieldInfo s_versionMadeByPlatformField = typeof(ZipArchiveEntry).GetField("_versionMadeByPlatform", BindingFlags.NonPublic | BindingFlags.Instance)!;
/// <summary>
/// Extract a zip archive entry, preserving platform mode bits
/// </summary>
/// <param name="entry"></param>
/// <param name="targetFileName"></param>
/// <param name="overwrite"></param>
public static void ExtractToFile_CrossPlatform(this ZipArchiveEntry entry, string targetFileName, bool overwrite)
{
// This seems to be consistently at least 35% faster than ZipFileExtensions.ExtractToFile(Entry, TargetFileName, Overwrite) when the
// archive contains many small files.
using (FileStream OutputStream = new FileStream(targetFileName, overwrite ? FileMode.Create : FileMode.CreateNew, FileAccess.Write))
{
using (Stream InStream = entry.Open())
{
InStream.CopyTo(OutputStream);
}
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
int madeByPlatform = Convert.ToInt32(s_versionMadeByPlatformField.GetValue(entry)!);
if (madeByPlatform == 3 || madeByPlatform == 19) // Unix or OSX
{
FileUtils.SetFileMode_Linux(targetFileName, (ushort)(entry.ExternalAttributes >> 16));
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
int madeByPlatform = Convert.ToInt32(s_versionMadeByPlatformField.GetValue(entry)!);
if (madeByPlatform == 3 || madeByPlatform == 19) // Unix or OSX
{
FileUtils.SetFileMode_Mac(targetFileName, (ushort)(entry.ExternalAttributes >> 16));
}
}
}
}
}