You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Lidarr/src/NzbDrone.Common/ArchiveService.cs

127 lines
4.3 KiB

using System;
using System.Collections.Generic;
using System.IO;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip;
using NLog;
namespace NzbDrone.Common
{
public interface IArchiveService
{
void Extract(string compressedFile, string destination);
void CreateZip(string path, IEnumerable<string> files);
}
public class ArchiveService : IArchiveService
{
private readonly Logger _logger;
public ArchiveService(Logger logger)
{
_logger = logger;
}
public void Extract(string compressedFile, string destination)
{
_logger.Debug("Extracting archive [{0}] to [{1}]", compressedFile, destination);
if (compressedFile.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
{
ExtractZip(compressedFile, destination);
}
else
{
ExtractTgz(compressedFile, destination);
}
_logger.Debug("Extraction complete.");
}
public void CreateZip(string path, IEnumerable<string> files)
{
using (var zipFile = ZipFile.Create(path))
{
zipFile.BeginUpdate();
foreach (var file in files)
{
zipFile.Add(file, Path.GetFileName(file));
}
zipFile.CommitUpdate();
}
}
private void ExtractZip(string compressedFile, string destination)
{
using (var fileStream = File.OpenRead(compressedFile))
{
var zipFile = new ZipFile(fileStream);
_logger.Debug("Validating Archive {0}", compressedFile);
if (!zipFile.TestArchive(true, TestStrategy.FindFirstError, OnZipError))
{
throw new IOException(string.Format("File {0} failed archive validation.", compressedFile));
}
foreach (ZipEntry zipEntry in zipFile)
{
if (!zipEntry.IsFile)
{
continue; // Ignore directories
}
var entryFileName = zipEntry.Name;
// to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
// Optionally match entrynames against a selection list here to skip as desired.
// The unpacked length is available in the zipEntry.Size property.
var buffer = new byte[4096]; // 4K is optimum
var zipStream = zipFile.GetInputStream(zipEntry);
// Manipulate the output filename here as desired.
var fullZipToPath = Path.Combine(destination, entryFileName);
var directoryName = Path.GetDirectoryName(fullZipToPath);
if (directoryName.Length > 0)
{
Directory.CreateDirectory(directoryName);
}
// Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
// of the file, but does not waste memory.
// The "using" will close the stream even if an exception occurs.
using (var streamWriter = File.Create(fullZipToPath))
{
StreamUtils.Copy(zipStream, streamWriter, buffer);
}
}
}
}
private void ExtractTgz(string compressedFile, string destination)
{
Stream inStream = File.OpenRead(compressedFile);
Stream gzipStream = new GZipInputStream(inStream);
var tarArchive = TarArchive.CreateInputTarArchive(gzipStream, null);
tarArchive.ExtractContents(destination);
tarArchive.Close();
gzipStream.Close();
inStream.Close();
}
private void OnZipError(TestStatus status, string message)
{
if (!string.IsNullOrWhiteSpace(message))
{
_logger.Error("File {0} failed zip validation. {1}", status.File.Name, message);
}
}
}
}