Compress a single file using C# - c#

I am using .NET 4.5, and the ZipFile class works great if I am trying to zip up an entire directory with "CreateFromDirectory". However, I only want to zip up one file in the directory. I tried pointing to a specific file (folder\data.txt), but that doesn't work. I considered the ZipArchive class since it has a "CreateEntryFromFile" method, but it seems this only allows you to create an entry into an existing file.
Is there no way to simply zip up one file without creating an empty zipfile (which has its issues) and then using the ZipArchiveExtension's "CreateEntryFromFile" method?
**This is also assuming I am working on a company program which cannot use third-party add-ons at the moment.
example from:http://msdn.microsoft.com/en-us/library/ms404280%28v=vs.110%29.aspx
string startPath = #"c:\example\start";
string zipPath = #"c:\example\result.zip";
string extractPath = #"c:\example\extract";
ZipFile.CreateFromDirectory(startPath, zipPath);
ZipFile.ExtractToDirectory(zipPath, extractPath);
But if startPath were to be #"c:\example\start\myFile.txt;", it would throw an error that the directory is invalid.

Use the CreateEntryFromFile off a an archive and use a file or memory stream:
Using a filestream if you are fine creating the zip file and then adding to it:
using (FileStream fs = new FileStream(#"C:\Temp\output.zip",FileMode.Create))
using (ZipArchive arch = new ZipArchive(fs, ZipArchiveMode.Create))
{
arch.CreateEntryFromFile(#"C:\Temp\data.xml", "data.xml");
}
Or if you need to do everything in memory and write the file once it is done, use a memory stream:
using (MemoryStream ms = new MemoryStream())
using (ZipArchive arch = new ZipArchive(ms, ZipArchiveMode.Create))
{
arch.CreateEntryFromFile(#"C:\Temp\data.xml", "data.xml");
}
Then you can write the MemoryStream to a file.
using (FileStream file = new FileStream("file.bin", FileMode.Create, System.IO.FileAccess.Write)) {
byte[] bytes = new byte[ms.Length];
ms.Read(bytes, 0, (int)ms.Length);
file.Write(bytes, 0, bytes.Length);
ms.Close();
}

Using file (or any) stream:
using (var zip = ZipFile.Open("file.zip", ZipArchiveMode.Create))
{
var entry = zip.CreateEntry("file.txt");
entry.LastWriteTime = DateTimeOffset.Now;
using (var stream= File.OpenRead(#"c:\path\to\file.txt"))
using (var entryStream = entry.Open())
stream.CopyTo(entryStream);
}
or briefer:
// reference System.IO.Compression
using (var zip = ZipFile.Open("file.zip", ZipArchiveMode.Create))
zip.CreateEntryFromFile("file.txt", "file.txt");
make sure you add references to System.IO.Compression
Update
Also, check out the new dotnet API documentation for ZipFile and ZipArchive too. There are a few examples there. There is also a warning about referencing System.IO.Compression.FileSystem to use ZipFile.
To use the ZipFile class, you must reference the
System.IO.Compression.FileSystem assembly in your project.

The simplest way to get this working is to use a temporary folder.
FOR ZIPPING:
Create a temp folder
Move file to folder
Zip folder
Delete folder
FOR UNZIPPING:
Unzip archive
Move file from temp folder to your location
Delete temp folder

In .NET, there are quite a few ways to tackle the problem, for a single file. If you don't want to learn everything there, you can get an abstracted library, like SharpZipLib (long standing open source library), sevenzipsharp (requires 7zip libs underneath) or DotNetZip.

just use following code for compressing a file.
public void Compressfile()
{
string fileName = "Text.txt";
string sourcePath = #"C:\SMSDBBACKUP";
DirectoryInfo di = new DirectoryInfo(sourcePath);
foreach (FileInfo fi in di.GetFiles())
{
//for specific file
if (fi.ToString() == fileName)
{
Compress(fi);
}
}
}
public static void Compress(FileInfo fi)
{
// Get the stream of the source file.
using (FileStream inFile = fi.OpenRead())
{
// Prevent compressing hidden and
// already compressed files.
if ((File.GetAttributes(fi.FullName)
& FileAttributes.Hidden)
!= FileAttributes.Hidden & fi.Extension != ".gz")
{
// Create the compressed file.
using (FileStream outFile =
File.Create(fi.FullName + ".gz"))
{
using (GZipStream Compress =
new GZipStream(outFile,
CompressionMode.Compress))
{
// Copy the source file into
// the compression stream.
inFile.CopyTo(Compress);
Console.WriteLine("Compressed {0} from {1} to {2} bytes.",
fi.Name, fi.Length.ToString(), outFile.Length.ToString());
}
}
}
}
}
}

Related

Is possible to create zip password protected file without first creating file, then zip it

I am writing data into text file and using below code,
await using var file = new StreamWriter(filePath);
foreach (var packet in resultPackets)
{
file.WriteLine(JsonConvert.SerializeObject(packet));
}
And I am using below code to zip the file with password protected using `DotNetZip,
using (ZipFile zip = new ZipFile())
{
zip.Password = "password";
zip.AddFile(filePath);
zip.Save(#"C:\tmp\data4.zip");
}
Is there a way to combined both, I want to create a file on the fly as password protected.
I don't
want to create first file with data, t
then create zip file from it
and delete the base file
Is this possible? Thanks!
Okay, so since this is still unanswered, here's a small program that does the job for me:
using (var stream = new MemoryStream())
using (var streamWriter = new StreamWriter(stream))
{
// Insert your code in here, i.e.
//foreach (var packet in resultPackets)
//{
// streamWriter.WriteLine(JsonConvert.SerializeObject(packet));
//}
// ... instead I write a simple string.
streamWriter.Write("Hello World!");
// Make sure the contents from the StreamWriter are actually flushed into the stream, then seek the beginning of the stream.
streamWriter.Flush();
stream.Seek(0, SeekOrigin.Begin);
using (ZipFile zip = new ZipFile())
{
zip.Password = "password";
// Write the contents of the stream into a file that is called "test.txt"
zip.AddEntry("test.txt", stream);
// Save the archive.
zip.Save("test.zip");
}
}
Note how AddEntry does not create any form of temporary file. Instead, when the archive is saved, the contents of the stream are read and put into a compressed file within the archive. However, be aware that the whole content of the file are completely kept in memory before it the archive is written to the disk.

Zip files without inclusion of folders

I'm using System.IO.Compression in order to compress a file into a .zip, below the source code:
using (FileStream zipToOpen = new FileStream(zipName, FileMode.CreateNew)){
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update)){
ZipArchiveEntry readmeEntry = archive.CreateEntry(#"C:\Users\soc\myFold\someFile.xml");
}
}
This piece of code works well, but unfortunately in the .zip there's the entire sequence of folders (C: -> Users -> ... -> someFile.xml); can I obtain a final .zip with ONLY the file I need? I know that with other libraries this fact is possible (DotNetZip add files without creating folders), but I would like to know if it were possible do the same with the standard library.
You seem to be under the impression that the file will be added to the archive, which is not the case. CreateEntry merely creates an entry with the specified path and entry name, you still need to write the actual file.
In fact, the code in your question is quite similar to the code in the documentation, so I assume you got it from there?
Anyway, to get only the file name you can use Path.GetFileName and then you can write the actual file content to the zip entry.
var filePath = #"C:\temp\foo.txt";
var zipName = #"C:\temp\foo.zip";
using (FileStream zipToOpen = new FileStream(zipName, FileMode.CreateNew))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry(Path.GetFileName(filePath));
using (StreamReader reader = new StreamReader(filePath))
using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
{
writer.Write(reader.ReadToEnd());
}
}
}
The code above will create an archive with foo.txt in the root and with the content of the source file, without any additional directories.

Compressing a folder to gzip/zip file from PCL

I am developing UWP and Windows phone 8.1 in the same solution.
On both projects I need a functionality of compressing a whole folder to one gzip file (in order to send it to server).
Libraries I've tried and encountered issues with:
SharpZipLib - uses System.IClonable which I cannot referance in my PCL project
DotNetZip - Not Suporting PCL/UWP
System.IO.Compression - Work only with Stream, cannot compress whole folder
I can split the implementation for each platform (although it is not perfect) but I still didn't found something that can be used in UWP.
Any help will be appriciated
Ok, so I found this project called SharpZipLib.Portable which is also an open source
Github : https://github.com/ygrenier/SharpZipLib.Portable
Really nice :)
Working on a UWP library you will have to use the Stream subsystem of the System.IO.Compression. There are many such limitations when you need a PCL version of .NET Framework. Live with that.
In your context that is not much of a trouble.
The required usings are:
using System;
using System.IO;
using System.IO.Compression;
Then the methods...
private void CreateArchive(string iArchiveRoot)
{
using (MemoryStream outputStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(outputStream, ZipArchiveMode.Create, true))
{
//Pick all the files you need in the archive.
string[] files = Directory.GetFiles(iArchiveRoot, "*", SearchOption.AllDirectories);
foreach (string filePath in files)
{
FileAppend(iArchiveRoot, filePath, archive);
}
}
}
}
private void FileAppend(
string iArchiveRootPath,
string iFileAbsolutePath,
ZipArchive iArchive)
{
//Has to return something like "dir1/dir2/part1.txt".
string fileRelativePath = MakeRelativePath(iFileAbsolutePath, iArchiveRootPath);
ZipArchiveEntry clsEntry = iArchive.CreateEntry(fileRelativePath, CompressionLevel.Optimal);
Stream entryData = clsEntry.Open();
//Write the file data to the ZipArchiveEntry.
entryData.Write(...);
}
//http://stackoverflow.com/questions/275689/how-to-get-relative-path-from-absolute-path
private string MakeRelativePath(
string fromPath,
string toPath)
{
if (String.IsNullOrEmpty(fromPath)) throw new ArgumentNullException("fromPath");
if (String.IsNullOrEmpty(toPath)) throw new ArgumentNullException("toPath");
Uri fromUri = new Uri(fromPath);
Uri toUri = new Uri(toPath);
if (fromUri.Scheme != toUri.Scheme) { return toPath; } // path can't be made relative.
Uri relativeUri = fromUri.MakeRelativeUri(toUri);
String relativePath = Uri.UnescapeDataString(relativeUri.ToString());
if (toUri.Scheme.Equals("file", StringComparison.OrdinalIgnoreCase))
{
relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}
return relativePath;
}

Create SPListItem from ZipArchiveEntry without FileStream

I'm a new .Net developper and i'm facing an issue while developping an Uploaded Zip File in a document Library.
i need to extract the content of the Zip File uploaded to do some actions on the files contained in it.
So i choosed to use ZipArchive Stream to handle my problem, i can retrieve my SPFile from my DocLib easily and create the stream from it.
But i'm not able to create embedded files from ZipArchiveEntry, i tried the following piece of code ( not a copy/past, i'm not on my dev machine )
foreach(SPFile myFile in mySPFolder.Files)
{
ZipArchive myZip = new ZipArchive(myFile.OpenBinaryStream());
foreach(ZipArchiveEntry subZip in ZipArchive.Entries)
{
SPFile newFile = list.RootFolder.Add("myxml.xml",subZip.Open())
}
}
I'm facing an issue while creating my newFile as it's throwing me a System I/O error, as per my understanding it's maybe due to the fact that the stream returned by the method ZipArchiveEntry.Open() is a deflatestream.
I saw that the file creation can be done with a MemoryStream, but i'm not able to understand how to convert a deflatestream to a memorystream.
I got the solution from another place but..
In order to get a memorystream from a deflatestream you need to use the CopyTo() method from Stream.
public void ExtractLibraryZipFolder(SPWeb web, SPList myList, string FolderPath, SPFile myFile, bool overWrite)
{
ZipArchive myZip = new ZipArchive(myFile.OpenBinaryStream());
foreach (ZipArchiveEntry subZip in myZip.Entries)
{
MemoryStream myMemoryStream = new MemoryStream();
subZip.Open().CopyTo(myMemoryStream);
if (FolderPath != string.Empty)
{
SPFolder theFolder = web.GetFolder("/ImportToolLibrary/");
theFolder.SubFolders[FolderPath].Files.Add(subZip.Name, myMemoryStream);
}
else
{
SPFile myUpload = myList.RootFolder.Files.Add(subZip.Name, myMemoryStream);
}
}
}

Is it possible to change file content directly in zip file?

I have form with text box and customer wants to store all changes from this textbox to zip archive.
I am using http://dotnetzip.codeplex.com
and i have example of code:
using (ZipFile zip = new ZipFile())
{
zip.AddFile("text.txt");
zip.Save("Backup.zip");
}
And i dont want to create each time temp text.txt and zip it back.
Can I access text.txt as Stream inside zip file and save text there?
There is an example in DotNetZip that use a Stream with the method AddEntry.
String zipToCreate = "Content.zip";
String fileNameInArchive = "Content-From-Stream.bin";
using (System.IO.Stream streamToRead = MyStreamOpener())
{
using (ZipFile zip = new ZipFile())
{
ZipEntry entry= zip.AddEntry(fileNameInArchive, streamToRead);
zip.Save(zipToCreate); // the stream is read implicitly here
}
}
A little test using LinqPad shows that it is possible to use a MemoryStream to build the zip file
void Main()
{
UnicodeEncoding uniEncoding = new UnicodeEncoding();
byte[] firstString = uniEncoding.GetBytes("This is the current contents of your TextBox");
using(MemoryStream memStream = new MemoryStream(100))
{
memStream.Write(firstString, 0 , firstString.Length);
// Reposition the stream at the beginning (otherwise an empty file will be created in the zip archive
memStream.Seek(0, SeekOrigin.Begin);
using (ZipFile zip = new ZipFile())
{
ZipEntry entry= zip.AddEntry("TextBoxData.txt", memStream);
zip.Save(#"D:\temp\memzip.zip");
}
}
}
There is another DotNetZip method accepting a file path as an argument:
zip.RemoveEntry(entry);
zip.AddEntry(entry.FileName, text, ASCIIEncoding.Unicode);

Categories