Is there a library or tool for zipping numerous files - c#

I've downloaded ICSharpCode.SharpZipLib and DotNetZip. I'm zipping over 100 files at a time varying a meg to 4 megs. When I use ICSharpCode I get a 'ContextSwitchDeadlock' error. DotNetZip fails on the finalizing of the file every time.
Also, I'm working on sharepoint folders (mapped to my local drive)
private bool zipall()
//ICSharpCode
{
int i = 0;
progressBarzipping.Minimum = 0;
progressBarzipping.Maximum = listBoxfiles.Items.Count;
ZipOutputStream zipOut = new ZipOutputStream(File.Create(textBoxDropPath.Text + "\\" + textBoxZipFileName.Text + ".zip"));
foreach (string fName in listBoxfiles.Items)
{
try
{
FileInfo fi = new FileInfo(fName);
ZipEntry entry = new ZipEntry(fi.Name);
FileStream sReader = File.OpenRead(fName);
byte[] buff = new byte[Convert.ToInt32(sReader.Length)];
sReader.Read(buff, 0, (int)sReader.Length);
entry.DateTime = fi.LastWriteTime;
entry.Size = sReader.Length;
sReader.Close();
zipOut.PutNextEntry(entry);
zipOut.Write(buff, 0, buff.Length);
}
catch
{
MessageBox.Show("Zip Failed");
zipOut.Finish();
zipOut.Close();
progressBarzipping.Value = 0;
return false;
}
i++;
progressBarzipping.Value = i;
}
zipOut.Finish();
zipOut.Close();
MessageBox.Show("Zip Complete");
progressBarzipping.Value = 0;
return true;
}
//Not sure but I think this was my DotNetZip approach
//using (ZipFile zip = new ZipFile())
// {
// foreach(string file in listboxFiles.Items)
// {
// zip.AddFile(file);
// }
// zip.Save(PathToNewZip);
// }

You didn't provide the exception. When using DotNetZip, I guess the problem might be with the sharepoint mapped drive. DotNetZip normally will save the zip as a temp file, then rename it. Maybe this isn't working because of sharepoint. If that's the case, try opening a filestream and saving it to that stream. This avoids the rename operation.
progressBarzipping.Minimum = 0;
progressBarzipping.Maximum = listBoxfiles.Items.Count;
using (Stream fs = new FileStream(PathToNewZip, FileMode.Create, FileAccess.Write))
{
using (ZipFile zip = new ZipFile())
{
zip.AddFiles(listboxFiles.Items);
// do the progress bar:
zip.SaveProgress += (sender, e) => {
if (e.EventType == ZipProgressEventType.Saving_BeforeWriteEntry) {
progressBarzipping.PerformStep();
}
};
zip.Save(fs);
}
}

Related

Copy files from one Zip file to another

I am copying files from one zip file to another in certain circumstances. I am wondering if there is a better way to do it than what I came up with:
using FileStream sourceFileStream = new FileStream(source.FileName, FileMode.Open);
using FileStream targetFileStream = new FileStream(target.FileName, FileMode.Open, FileAccess.ReadWrite);
using ZipArchive sourceZip = new ZipArchive(sourceFileStream, ZipArchiveMode.Read);
using ZipArchive targetZip = new ZipArchive(targetFileStream, ZipArchiveMode.Update);
ZipArchiveEntry sourceEntry = sourceZip.GetEntry(filePathInArchive);
if (sourceEntry == null)
return;
ZipArchiveEntry targetEntry = targetZip.GetEntry(filePathInArchive);
if (targetEntry != null)
targetEntry.Delete();
targetZip.CreateEntry(filePathInArchive);
targetEntry = targetZip.GetEntry(filePathInArchive);
if (targetEntry != null)
{
Stream writer = targetEntry.Open();
Stream reader = sourceEntry.Open();
int b;
do
{
b = reader.ReadByte();
writer.WriteByte((byte)b);
} while (b != -1);
writer.Close();
reader.Close();
}
Tips and suggestions would be appreciated.
You can iterate each entry from source archive with opening its streams and using Stream.CopyTo write source entry content to target entry.
From C# 8.0 it looks compact and works fine:
static void CopyZipEntries(string sourceZipFile, string targetZipFile)
{
using FileStream sourceFS = new FileStream(sourceZipFile, FileMode.Open);
using FileStream targetFS = new FileStream(targetZipFile, FileMode.Open);
using ZipArchive sourceZIP = new ZipArchive(sourceFS, ZipArchiveMode.Read, false, Encoding.GetEncoding(1251));
using ZipArchive targetZIP = new ZipArchive(targetFS, ZipArchiveMode.Update, false, Encoding.GetEncoding(1251));
foreach (ZipArchiveEntry sourceEntry in sourceZIP.Entries)
{
// 'is' is replacement for 'null' check
if (targetZIP.GetEntry(sourceEntry.FullName) is ZipArchiveEntry existingTargetEntry)
existingTargetEntry.Delete();
using (Stream targetEntryStream = targetZIP.CreateEntry(sourceEntry.FullName).Open())
{
sourceEntry.Open().CopyTo(targetEntryStream);
}
}
}
With earlier than C# 8.0 versions it works fine too, but more braces needed:
static void CopyZipEntries(string sourceZipFile, string targetZipFile)
{
using (FileStream sourceFS = new FileStream(sourceZipFile, FileMode.Open))
{
using (FileStream targetFS = new FileStream(targetZipFile, FileMode.Open))
{
using (ZipArchive sourceZIP = new ZipArchive(sourceFS, ZipArchiveMode.Read, false, Encoding.GetEncoding(1251)))
{
using (ZipArchive targetZIP = new ZipArchive(targetFS, ZipArchiveMode.Update, false, Encoding.GetEncoding(1251)))
{
foreach (ZipArchiveEntry sourceEntry in sourceZIP.Entries)
{
if (targetZIP.GetEntry(sourceEntry.FullName) is ZipArchiveEntry existingTargetEntry)
{
existingTargetEntry.Delete();
}
using (Stream target = targetZIP.CreateEntry(sourceEntry.FullName).Open())
{
sourceEntry.Open().CopyTo(target);
}
}
}
}
}
}
}
For single specified file copy just replace bottom part from foreach loop to if condition:
static void CopyZipEntry(string fileName, string sourceZipFile, string targetZipFile)
{
// ...
// It means specified file exists in source ZIP-archive
// and we can copy it to target ZIP-archive
if (sourceZIP.GetEntry(fileName) is ZipArchiveEntry sourceEntry)
{
if (targetZIP.GetEntry(sourceEntry.FullName) is ZipArchiveEntry existingTargetEntry)
existingTargetEntry.Delete();
using (Stream targetEntryStream = targetZIP.CreateEntry(sourceEntry.FullName).Open())
{
sourceEntry.Open().CopyTo(targetEntryStream);
}
}
else
MessageBox.Show("Source ZIP-archive doesn't contains file " + fileName);
}
Thanks to the input so far, I cleaned up and improved the code. I think this looks cleaner and more reliable.
//Making sure files exist etc before this part...
string filePathInArchive = source.GetFilePath(fileId);
using FileStream sourceFileStream = new FileStream(source.FileName, FileMode.Open);
using FileStream targetFileStream = new FileStream(target.FileName, FileMode.Open, FileAccess.ReadWrite);
using ZipArchive sourceZip = new ZipArchive(sourceFileStream, ZipArchiveMode.Read, false );
using ZipArchive targetZip = new ZipArchive(targetFileStream, ZipArchiveMode.Update, false);
ZipArchiveEntry sourceEntry = sourceZip.GetEntry(filePathInArchive);
if (sourceEntry != null)
{
if (targetZip.GetEntry(filePathInArchive) is { } existingTargetEntry)
{
existingTargetEntry.Delete();
}
using var targetEntryStream = targetZip.CreateEntry(sourceEntry.FullName).Open();
sourceEntry.Open().CopyTo(targetEntryStream);
}

C# Sharepoint upload large file into subfolder

I have an issue when trying too upload a large file to a sub sharepoint folder.
The issue is related to the variable libraryName. I am not sure have i can change this so i can use an url instead.
Example:
var site = ""https://sharepoint.com/sites/Test_Site1/"
var relative = "Documents/Folder1/folder2/
https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/upload-large-files-sample-app-for-sharepoint
public Microsoft.SharePoint.Client.File UploadFileSlicePerSlice(ClientContext ctx, string libraryName, string fileName, int fileChunkSizeInMB = 3)
{
// Each sliced upload requires a unique ID.
Guid uploadId = Guid.NewGuid();
// Get the name of the file.
string uniqueFileName = Path.GetFileName(fileName);
// Ensure that target library exists, and create it if it is missing.
if (!LibraryExists(ctx, ctx.Web, libraryName))
{
CreateLibrary(ctx, ctx.Web, libraryName);
}
// Get the folder to upload into.
List docs = ctx.Web.Lists.GetByTitle(libraryName);
ctx.Load(docs, l => l.RootFolder);
// Get the information about the folder that will hold the file.
ctx.Load(docs.RootFolder, f => f.ServerRelativeUrl);
ctx.ExecuteQuery();
// File object.
Microsoft.SharePoint.Client.File uploadFile = null;
// Calculate block size in bytes.
int blockSize = fileChunkSizeInMB * 1024 * 1024;
// Get the information about the folder that will hold the file.
ctx.Load(docs.RootFolder, f => f.ServerRelativeUrl);
ctx.ExecuteQuery();
// Get the size of the file.
long fileSize = new FileInfo(fileName).Length;
if (fileSize <= blockSize)
{
// Use regular approach.
using (FileStream fs = new FileStream(fileName, FileMode.Open))
{
FileCreationInformation fileInfo = new FileCreationInformation();
fileInfo.ContentStream = fs;
fileInfo.Url = uniqueFileName;
fileInfo.Overwrite = true;
uploadFile = docs.RootFolder.Files.Add(fileInfo);
ctx.Load(uploadFile);
ctx.ExecuteQuery();
// Return the file object for the uploaded file.
return uploadFile;
}
}
else
{
// Use large file upload approach.
ClientResult<long> bytesUploaded = null;
FileStream fs = null;
try
{
fs = System.IO.File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using (BinaryReader br = new BinaryReader(fs))
{
byte[] buffer = new byte[blockSize];
Byte[] lastBuffer = null;
long fileoffset = 0;
long totalBytesRead = 0;
int bytesRead;
bool first = true;
bool last = false;
// Read data from file system in blocks.
while ((bytesRead = br.Read(buffer, 0, buffer.Length)) > 0)
{
totalBytesRead = totalBytesRead + bytesRead;
// You've reached the end of the file.
if (totalBytesRead == fileSize)
{
last = true;
// Copy to a new buffer that has the correct size.
lastBuffer = new byte[bytesRead];
Array.Copy(buffer, 0, lastBuffer, 0, bytesRead);
}
if (first)
{
using (MemoryStream contentStream = new MemoryStream())
{
// Add an empty file.
FileCreationInformation fileInfo = new FileCreationInformation();
fileInfo.ContentStream = contentStream;
fileInfo.Url = uniqueFileName;
fileInfo.Overwrite = true;
uploadFile = docs.RootFolder.Files.Add(fileInfo);
// Start upload by uploading the first slice.
using (MemoryStream s = new MemoryStream(buffer))
{
// Call the start upload method on the first slice.
bytesUploaded = uploadFile.StartUpload(uploadId, s);
ctx.ExecuteQuery();
// fileoffset is the pointer where the next slice will be added.
fileoffset = bytesUploaded.Value;
}
// You can only start the upload once.
first = false;
}
}
else
{
if (last)
{
// Is this the last slice of data?
using (MemoryStream s = new MemoryStream(lastBuffer))
{
// End sliced upload by calling FinishUpload.
uploadFile = uploadFile.FinishUpload(uploadId, fileoffset, s);
ctx.ExecuteQuery();
// Return the file object for the uploaded file.
return uploadFile;
}
}
else
{
using (MemoryStream s = new MemoryStream(buffer))
{
// Continue sliced upload.
bytesUploaded = uploadFile.ContinueUpload(uploadId, fileoffset, s);
ctx.ExecuteQuery();
// Update fileoffset for the next slice.
fileoffset = bytesUploaded.Value;
}
}
}
} // while ((bytesRead = br.Read(buffer, 0, buffer.Length)) > 0)
}
}
finally
{
if (fs != null)
{
fs.Dispose();
}
}
}
return null;
}
This is the first page where i run the method
using Microsoft.SharePoint.Client;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace Contoso.Core.LargeFileUpload
{
class Program
{
static void Main(string[] args)
{
// Request Office365 site from the user
string siteUrl = #"https://bundegruppen.sharepoint.com/sites/F24-2905/";
/* Prompt for Credentials */
//Console.WriteLine("Filer blir overført til site: {0}", siteUrl);
string userName = "xx.xx#bxxbygg.no";
SecureString pwd = new SecureString();
string password = "xxx";
foreach (char c in password.ToCharArray()) pwd.AppendChar(c);
/* End Program if no Credentials */
if (string.IsNullOrEmpty(userName) || (pwd == null))
return;
ClientContext ctx = new ClientContext(siteUrl);
ctx.AuthenticationMode = ClientAuthenticationMode.Default;
ctx.Credentials = new SharePointOnlineCredentials(userName, pwd);
// These should both work as expected.
try
{
// Alternative 3 for uploading large files: slice per slice which allows you to stop and resume a download
new FileUploadService().UploadFileSlicePerSliceToFolder(ctx, "Dokumenter/General", #"C:\Temp\F24_Sammenstillingsmodell.smc");
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Exception while uploading files to the target site: {0}.", ex.ToString()));
Console.WriteLine("Press enter to continue.");
Console.Read();
}
// Just to see what we have in console
Console.ForegroundColor = ConsoleColor.White;
}
}
}
The code you have is written just to upload the specified file to the RootFolder of the named Library. If you pass in a full path to a folder instead of just a Library Name, it will fail.
The following is a modded version of the function that should allow you to pass a full serverRelativeUrl to the desired folder:
public Microsoft.SharePoint.Client.File UploadFileSlicePerSliceToFolder(ClientContext ctx, string serverRelativeFolderUrl, string fileName, int fileChunkSizeInMB = 3)
{
// Each sliced upload requires a unique ID.
Guid uploadId = Guid.NewGuid();
// Get the name of the file.
string uniqueFileName = Path.GetFileName(fileName);
// Get the folder to upload into.
Folder uploadFolder = ctx.web.GetFolderByServerRelativeUrl(serverRelativeFolderUrl);
// Get the information about the folder that will hold the file.
ctx.Load(uploadFolder);
ctx.ExecuteQuery();
// File object.
Microsoft.SharePoint.Client.File uploadFile = null;
// Calculate block size in bytes.
int blockSize = fileChunkSizeInMB * 1024 * 1024;
// Get the information about the folder that will hold the file.
ctx.Load(uploadFolder, f => f.ServerRelativeUrl);
ctx.ExecuteQuery();
// Get the size of the file.
long fileSize = new FileInfo(fileName).Length;
if (fileSize <= blockSize)
{
// Use regular approach.
using (FileStream fs = new FileStream(fileName, FileMode.Open))
{
FileCreationInformation fileInfo = new FileCreationInformation();
fileInfo.ContentStream = fs;
fileInfo.Url = uniqueFileName;
fileInfo.Overwrite = true;
uploadFile = uploadFolder.Files.Add(fileInfo);
ctx.Load(uploadFile);
ctx.ExecuteQuery();
// Return the file object for the uploaded file.
return uploadFile;
}
}
else
{
// Use large file upload approach.
ClientResult<long> bytesUploaded = null;
FileStream fs = null;
try
{
fs = System.IO.File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using (BinaryReader br = new BinaryReader(fs))
{
byte[] buffer = new byte[blockSize];
Byte[] lastBuffer = null;
long fileoffset = 0;
long totalBytesRead = 0;
int bytesRead;
bool first = true;
bool last = false;
// Read data from file system in blocks.
while ((bytesRead = br.Read(buffer, 0, buffer.Length)) > 0)
{
totalBytesRead = totalBytesRead + bytesRead;
// You've reached the end of the file.
if (totalBytesRead == fileSize)
{
last = true;
// Copy to a new buffer that has the correct size.
lastBuffer = new byte[bytesRead];
Array.Copy(buffer, 0, lastBuffer, 0, bytesRead);
}
if (first)
{
using (MemoryStream contentStream = new MemoryStream())
{
// Add an empty file.
FileCreationInformation fileInfo = new FileCreationInformation();
fileInfo.ContentStream = contentStream;
fileInfo.Url = uniqueFileName;
fileInfo.Overwrite = true;
uploadFile = uploadFolder.Files.Add(fileInfo);
// Start upload by uploading the first slice.
using (MemoryStream s = new MemoryStream(buffer))
{
// Call the start upload method on the first slice.
bytesUploaded = uploadFile.StartUpload(uploadId, s);
ctx.ExecuteQuery();
// fileoffset is the pointer where the next slice will be added.
fileoffset = bytesUploaded.Value;
}
// You can only start the upload once.
first = false;
}
}
else
{
if (last)
{
// Is this the last slice of data?
using (MemoryStream s = new MemoryStream(lastBuffer))
{
// End sliced upload by calling FinishUpload.
uploadFile = uploadFile.FinishUpload(uploadId, fileoffset, s);
ctx.ExecuteQuery();
// Return the file object for the uploaded file.
return uploadFile;
}
}
else
{
using (MemoryStream s = new MemoryStream(buffer))
{
// Continue sliced upload.
bytesUploaded = uploadFile.ContinueUpload(uploadId, fileoffset, s);
ctx.ExecuteQuery();
// Update fileoffset for the next slice.
fileoffset = bytesUploaded.Value;
}
}
}
} // while ((bytesRead = br.Read(buffer, 0, buffer.Length)) > 0)
}
}
finally
{
if (fs != null)
{
fs.Dispose();
}
}
}
return null;
}

Invalid zip file after creating it with System.IO.Compression

I'm trying to create a zip file that contains one or more files.
I'm using the .NET framework 4.5 and more specifically System.IO.Compression namespace.
The objective is to allow a user to download a zip file through a ASP.NET MVC application.
The zip file is being generated and sent to the client but when I try to open it by doing double click on it I get the following error:
Windows cannot open the folder.
The compressed (zipped) folder ... is invalid.
Here's my code:
[HttpGet]
public FileResult Download()
{
var fileOne = CreateFile(VegieType.POTATO);
var fileTwo = CreateFile(VegieType.ONION);
var fileThree = CreateFile(VegieType.CARROT);
IEnumerable<FileContentResult> files = new List<FileContentResult>() { fileOne, fileTwo, fileThree };
var zip = CreateZip(files);
return zip;
}
private FileContentResult CreateFile(VegieType vType)
{
string fileName = string.Empty;
string fileContent = string.Empty;
switch (vType)
{
case VegieType.BATATA:
fileName = "batata.csv";
fileContent = "THIS,IS,A,POTATO";
break;
case VegieType.CEBOLA:
fileName = "cebola.csv";
fileContent = "THIS,IS,AN,ONION";
break;
case VegieType.CENOURA:
fileName = "cenoura.csv";
fileContent = "THIS,IS,A,CARROT";
break;
default:
break;
}
var fileBytes = Encoding.GetEncoding(1252).GetBytes(fileContent);
return File(fileBytes, MediaTypeNames.Application.Octet, fileName);
}
private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
byte[] retVal = null;
if (files.Any())
{
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
{
foreach (var f in files)
{
var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
using (var entryStream = entry.Open())
{
entryStream.Write(f.FileContents, 0, f.FileContents.Length);
entryStream.Close();
}
}
zipStream.Position = 0;
retVal = zipStream.ToArray();
}
}
}
return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
}
Can anyone please shed some light on why is windows saying that my zip file is invalid when I double click on it.
A final consideration, I can open it using 7-Zip.
You need to get the MemoryStream buffer via ToArray after the ZipArchive object gets disposed. Otherwise you end up with corrupted archive.
And please note that I have changed the parameters of ZipArchive constructor to keep it open when adding entries.
There is some checksumming going on when the ZipArchive is beeing disposed so if you read the MemoryStream before, it is still incomplete.
private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
byte[] retVal = null;
if (files.Any())
{
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
foreach (var f in files)
{
var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
using (BinaryWriter writer = new BinaryWriter(entry.Open()))
{
writer.Write(f.FileContents, 0, f.FileContents.Length);
writer.Close();
}
}
zipStream.Position = 0;
}
retVal = zipStream.ToArray();
}
}
return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
}
Just return the stream...
private ActionResult CreateZip(IEnumerable files)
{
if (files.Any())
{
MemoryStream zipStream = new MemoryStream();
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
{
foreach (var f in files)
{
var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
using (var entryStream = entry.Open())
{
entryStream.Write(f.FileContents, 0, f.FileContents.Length);
entryStream.Close();
}
}
}
zipStream.Position = 0;
return File(zipStream, MediaTypeNames.Application.Zip, "horta.zip");
}
return new EmptyResult();
}
Try changing
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
to
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
In this usage, the archive is forced to write to the stream when it is closed. However, if the leaveOpen argument of the constructor is set to false, it will close the underlying stream too.
When I added a wrong name for the entry as in the example
var fileToZip = "/abc.txt";
ZipArchiveEntry zipFileEntry = zipArchive.CreateEntry(fileToZip);
I got the same error. After correcting the file name, it is ok now.
I got the "The compressed (zipped) folder ... is invalid." error because my entries were named with a leading "/" in front of them. Some zip extractors had no problem with this but the Windows one does. I resolved it by removing the slash from the entry name (from "/file.txt" to "file.txt").

How to Zip a folder with encrypt password using ICSharplib

How to Zip a folder using ICSharplib.
Is there any way I can add a encrypt password while zipping it ?
There is no option that I can use any other dll. Have to use only ICSharplib.
Currently I am using this code block
private static void CompressFiles(string folderPath) {
string zipOutput = #"C:\temp\myoutput.zip";
try {
using (ZipOutputStream zs = new ZipOutputStream(File.Create(zipOutput))) {
zs.SetLevel(9); // 0-9 (9 being best compression)
foreach (string file in Directory.GetFiles(folderPath)) {
ZipEntry entry = new ZipEntry(Path.GetFileName(file));
entry.DateTime = DateTime.Now;
using (FileStream fs = File.OpenRead(file)) {
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
entry.Size = buffer.Length; // This is very important
zs.PutNextEntry(entry);
zs.Write(buffer, 0, buffer.Length);
}
}
zs.Finish();
zs.Close();
}
}
catch { throw; }
}
It can zip all the files in the folder.
But What I want is to zip the whole folder.
Like the folders in side that folder also be included in the zip file .
Thanks in advance
Use the FastZip object.
ICSharpCode.SharpZipLib.Zip.FastZip z = new ICSharpCode.SharpZipLib.Zip.FastZip();
z.CreateEmptyDirectories = true;
z.CreateZip("F:\\ZipTest.zip", "F:\\ZipTest\\", true, "");
if (File.Exists("F:\\ZipTest.zip"))
Console.WriteLine("Done");
else
Console.WriteLine("Failed");
I use following code:
public static bool ZipIt(string sourcePath, string destinationPath)
{
List<string> ListOfFiles = GetListOfFiles(sourcePath);
try
{
string OutPath = destinationPath + ".zip";
int TrimLength = (Directory.GetParent(sourcePath)).ToString().Length;
TrimLength += 1;
//remove '\'
FileStream ostream;
byte[] obuffer;
ZipOutputStream oZipStream = new ZipOutputStream(System.IO.File.Create(OutPath));
oZipStream.Password = EncodePassword("Password");
oZipStream.SetLevel(9);
// 9 = maximum compression level
ZipEntry oZipEntry;
foreach (string Fil in ListOfFiles.ToArray()) // for each file, generate a zipentry
{
oZipEntry = new ZipEntry(Fil.Remove(0, TrimLength));
oZipStream.PutNextEntry(oZipEntry);
if (!Fil.EndsWith(#"/")) // if a file ends with '/' its a directory
{
ostream = File.OpenRead(Fil);
obuffer = new byte[ostream.Length];
ostream.Read(obuffer, 0, obuffer.Length);
oZipStream.Write(obuffer, 0, obuffer.Length);
ostream.Close();
}
}
oZipStream.Finish();
oZipStream.Close();
return true;
}
catch (Exception ex)
{
return false;
}
}
public static string EncodePassword(string originalPassword)
{
Byte[] encodedBytes;
encodedBytes = ASCIIEncoding.Default.GetBytes(originalPassword);
return BitConverter.ToString(encodedBytes);
}

Extract a zipped file only (without the folder)

With the SharpZip lib I can easily extract a file from a zip archive:
FastZip fz = new FastZip();
string path = "C:/bla.zip";
fz.ExtractZip(bla,"C:/Unzips/",".*");
However this puts the uncompressed folder in the output directory.
Suppose there is a foo.txt file within bla.zip which I want. Is there an easy way to just extract that and place it in the output directory (without the folder)?
The FastZip does not seem to provide a way to change folders, but the "manual" way of doing supports this.
If you take a look at their example:
public void ExtractZipFile(string archiveFilenameIn, string outFolder) {
ZipFile zf = null;
try {
FileStream fs = File.OpenRead(archiveFilenameIn);
zf = new ZipFile(fs);
foreach (ZipEntry zipEntry in zf) {
if (!zipEntry.IsFile) continue; // Ignore directories
String entryFileName = zipEntry.Name;
// to remove the folder from the entry:
// entryFileName = Path.GetFileName(entryFileName);
byte[] buffer = new byte[4096]; // 4K is optimum
Stream zipStream = zf.GetInputStream(zipEntry);
// Manipulate the output filename here as desired.
String fullZipToPath = Path.Combine(outFolder, entryFileName);
string directoryName = Path.GetDirectoryName(fullZipToPath);
if (directoryName.Length > 0)
Directory.CreateDirectory(directoryName);
using (FileStream streamWriter = File.Create(fullZipToPath)) {
StreamUtils.Copy(zipStream, streamWriter, buffer);
}
}
} finally {
if (zf != null) {
zf.IsStreamOwner = true;stream
zf.Close();
}
}
}
As they note, instead of writing:
String entryFileName = zipEntry.Name;
you can write:
String entryFileName = Path.GetFileName(entryFileName)
to remove the folders.
Assuming you know this is the only file (not folder) in the zip:
using(ZipFile zip = new ZipFile(zipStm))
{
foreach(ZipEntry ze in zip)
if(ze.IsFile)//must be our foo.txt
{
using(var fs = new FileStream(#"C:/Unzips/foo.txt", FileMode.OpenOrCreate, FileAccess.Write))
zip.GetInputStream(ze).CopyTo(fs);
break;
}
}
If you need to handle other possibilities, or e.g. getting the name of the zip-entry, the complexity rises accordingly.

Categories