132 lines
6.1 KiB
C#
132 lines
6.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace UnityEditor.Build.Pipeline.Utilities
|
|
{
|
|
/// <summary>
|
|
/// Since C# only has GZipStream until .NET 4.0, we are forced to implement our own packing system
|
|
/// for artifact files. We compact all artifacts into a single GZip Stream with the header before
|
|
/// the file name and contents. The header contains the file name length and file length (in bytes).
|
|
/// </summary>
|
|
public class FileCompressor
|
|
{
|
|
/// <summary>
|
|
/// Compresses all artifacts located at a specified directory.
|
|
/// </summary>
|
|
/// <param name="directoryPath">The directory containing the artifacts.</param>
|
|
/// <param name="archiveFilePath">The file path at which the archive will be created.</param>
|
|
/// <returns>Returns true if the directory was found and compressed. Returns false otherwise.</returns>
|
|
public static bool Compress(string directoryPath, string archiveFilePath)
|
|
{
|
|
if (!directoryPath.EndsWith("/") && !directoryPath.EndsWith("\\"))
|
|
directoryPath += Path.DirectorySeparatorChar.ToString();
|
|
|
|
var directory = new DirectoryInfo(directoryPath);
|
|
if (!directory.Exists)
|
|
return false;
|
|
|
|
var files = directory.GetFiles("*", SearchOption.AllDirectories);
|
|
files = files.Where(x => (File.GetAttributes(x.FullName) & FileAttributes.Hidden) != FileAttributes.Hidden && x.Extension != ".sbpGz").ToArray();
|
|
if (files.Length == 0)
|
|
return false;
|
|
|
|
using (var archiveStream = new FileStream(archiveFilePath, FileMode.OpenOrCreate, FileAccess.Write))
|
|
{
|
|
using (var gZipStream = new GZipStream(archiveStream, CompressionMode.Compress))
|
|
{
|
|
foreach (var file in files)
|
|
{
|
|
var relativePath = file.FullName.Substring(directory.FullName.Length);
|
|
relativePath = relativePath.Replace("\\", "/");
|
|
|
|
using (var fileStream = file.OpenRead())
|
|
{
|
|
byte[] filePathBytes = Encoding.ASCII.GetBytes(relativePath);
|
|
|
|
// Write chunk header
|
|
gZipStream.Write(BitConverter.GetBytes(filePathBytes.Length), 0, sizeof(int));
|
|
gZipStream.Write(BitConverter.GetBytes(fileStream.Length), 0, sizeof(long));
|
|
|
|
// Write chunk body
|
|
// Write file path
|
|
gZipStream.Write(filePathBytes, 0, filePathBytes.Length);
|
|
|
|
// Write file contents
|
|
byte[] readBuffer = new byte[4096];
|
|
int readSize = readBuffer.Length;
|
|
while (readSize == readBuffer.Length)
|
|
{
|
|
readSize = fileStream.Read(readBuffer, 0, readBuffer.Length);
|
|
gZipStream.Write(readBuffer, 0, readSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracts all artifacts compressed in an archive.
|
|
/// </summary>
|
|
/// <param name="archiveFilePath">The archive to decompress.</param>
|
|
/// <param name="directoryPath">The path where the extracted artifacts will be stored.</param>
|
|
/// <returns>Returns true if the archive was found and decompressed. Returns false otherwise.</returns>
|
|
public static bool Decompress(string archiveFilePath, string directoryPath)
|
|
{
|
|
var archiveFile = new FileInfo(archiveFilePath);
|
|
if (!archiveFile.Exists)
|
|
return false;
|
|
|
|
var directory = new DirectoryInfo(directoryPath);
|
|
if (!directory.Exists)
|
|
directory.Create();
|
|
|
|
using (var archiveStream = archiveFile.OpenRead())
|
|
{
|
|
using (var gZipStream = new GZipStream(archiveStream, CompressionMode.Decompress))
|
|
{
|
|
while (true)
|
|
{
|
|
// Read chunk header
|
|
byte[] header = new byte[sizeof(int) + sizeof(long)];
|
|
int readSize = gZipStream.Read(header, 0, header.Length);
|
|
if (readSize != header.Length)
|
|
break;
|
|
|
|
int filePathLength = BitConverter.ToInt32(header, 0);
|
|
long fileLenth = BitConverter.ToInt64(header, sizeof(int));
|
|
|
|
// Read chunk body
|
|
// Read file path
|
|
byte[] filePathBytes = new byte[filePathLength];
|
|
gZipStream.Read(filePathBytes, 0, filePathLength);
|
|
string filePath = Encoding.ASCII.GetString(filePathBytes);
|
|
|
|
var pathSeperator = filePath.LastIndexOf("/");
|
|
if (pathSeperator > -1)
|
|
Directory.CreateDirectory(string.Format("{0}/{1}", directoryPath, filePath.Substring(0, pathSeperator)));
|
|
|
|
// Read file contents
|
|
using (var fileStream = new FileStream(directoryPath + "/" + filePath, FileMode.OpenOrCreate, FileAccess.Write))
|
|
{
|
|
byte[] readBuffer = new byte[4096];
|
|
long readRemaining = fileLenth;
|
|
while (readRemaining > 0)
|
|
{
|
|
readSize = readBuffer.Length < readRemaining ? readBuffer.Length : (int)readRemaining;
|
|
gZipStream.Read(readBuffer, 0, readSize);
|
|
fileStream.Write(readBuffer, 0, readSize);
|
|
readRemaining -= readSize;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|