Welcome to today’s post.
In today’s post I will show you how to use the file compression utilities that are in the .NET library. Many of the useful compression utilities are available by including the System.IO.Compression namespace.
In the previous post, I showed how to use libraries with the System.IO and System.IO.Compression namespaces and the ZipFile class to implement basic file uploading and downloading.
In this post, I will show how to use the compression utilities to performs some useful yet basic file archiving operations.
Before we can make use of any file archiving and compression utilities, we will need to have ready a folder that contain some files that can be archived.
In the previous post, I discussed some of the most common uses of file compression and archiving utilities in applications, which included Packaging uploads for end user uploads and subscribers and disk space management.
In the first section, I will show how to use the ZipFile and ZipArchive classes to programmatically create ZIP file compressed archives.
Compression and Archiving of Uploaded Files
In this section, I will show how to archive and compress files within an upload folder.
For details of the plumbing of the utility class and the dependencies that are injected into it, including the configuration settings and logging, refer to my previous post on how to upload and download files into a folder within a file server.
In the method that I will implement, I assume that I have an upload folder that has sub-folders where files are uploaded into. Each folder name is of the format YYYYMMDD.
In the archive method I will implement two tasks:
- Archive an existing upload sub-folder.
- Retrieve the attributes of the archive file and return them from the method.
I will first show the archive method and afterwards explain how it works.
The archive method is shown below:
public async Task<FileArchiveResponse?> ArchiveFolderFilesV2()
{
var archiveFile = GetArchiveFileName;
var currentDirectory = UploadFolder;
string subFolder = UploadSubFolder;
var folderExists = System.IO.Directory.Exists(currentDirectory + "\\" + subFolder);
if (folderExists == false)
throw new DirectoryNotFoundException($"The sub-folder {subFolder} cannot be found.");
if (System.IO.File.Exists($"{currentDirectory}\\{subFolder}_{archiveFile}"))
{
System.IO.File.Delete($"{currentDirectory}\\{subFolder}_{archiveFile}");
_logger.LogInformation($"Deleting existing Archive file {archiveFile}");
}
FileArchiveResponse far;
try
{
ZipFile.CreateFromDirectory(
$"{currentDirectory}\\{subFolder}",
$"{currentDirectory}\\{subFolder}_{archiveFile}");
}
catch (Exception ex)
{
_logger.LogInformation($"Error Creating Archive for {archiveFile}. Error: {ex.Message.ToString()}");
}
// Determine archive file attributes..
long archiveSize = 0;
long archiveCompressedSize = 0;
using (var streamDestination =
new System.IO.FileStream($"{currentDirectory}\\{subFolder}_{archiveFile}", FileMode.Open, FileAccess.ReadWrite))
{
_logger.LogInformation($"Archive {archiveFile}. Seek Status: {streamDestination.CanSeek}");
streamDestination.Seek(0, SeekOrigin.Begin);
ZipArchive zipArchive = new ZipArchive(streamDestination, ZipArchiveMode.Update);
zipArchive.Comment = $"Archive file {{subFolder}_{archiveFile}} of upload subfolder {subFolder}.";
zipArchive.Entries.ToList().ForEach(x =>
{
archiveSize += x.Length;
archiveCompressedSize += x.CompressedLength;
});
far = new FileArchiveResponse()
{
ArchiveComment = zipArchive.Comment,
ArchiveFolder = subFolder,
ArchiveFileSize = archiveSize,
ArchiveCompressedFileSize = archiveCompressedSize,
NumberOfArchiveEntries = zipArchive.Entries.Count()
};
zipArchive.Dispose();
}
return far;
}
In the archive method, I carried out the following operations:
- We check if the sub-folder exists.
- If the sub-folder does not exist, we throw and error and exit.
- We check if there is an existing ZIP archive in the parent upload folder of the format YYYYMMDD_archive.zip. If the archive file exists, we delete it.
- We then create a new archive by calling the command:
ZipFile.CreateFromDirectory(folder, archive_file_name)
where:
folder is the sub-folder we want to archive.
archive_file_name is the full path and name of the archive file that we want to generate.
- Open the archive file as a file stream in file mode Open and file access ReadWrite.
- Open the zip archive file with the ZipArchive class in Update mode.
- Set a comment for the zip archive.
- Iterate through the file entries within the Entries collection of the ZipArchive object.
- Tally up the uncompressed and compressed file sizes using the Length and CompressedLength properties.
- Store the file zip archive properties in an object.
- Dispose of the ZipArchive object.
The archive function is executed through a controller method as shown:
[HttpPost("api/[controller]/ArchiveFolderFilesV2")]
public async Task<IActionResult> ArchiveFolderFilesV2()
{
var result = await _fileUtilityService.ArchiveFolderFilesV2();
if (result == null)
{
return NotFound();
}
return Ok(result);
}
The resulting Swagger UI method is shown:

Following execution of the method, we get the following response:

As we can see, the response shows two files are in the ZIP archive and the uncompressed and compressed file totals are displayed.
In the upload folder, we can see the file archive has been created as a ZIP file with the expected format:

When we select the ZIP archive file, we will see the explorer pane display the files that are in the archive with useful file archive entry properties in the columns:

We can also open the file directly using any installed compression tools you may have. I have opened the archive file using 7-Zip:

When viewing the file properties, we can see that the comment that we added within our code has been successfully updated in the zip file:

The properties show the overall uncompressed file size and the packed (compressed) size of the resulting archive.
In the next section, I will cover two common issues that arise when using folder compression and the opening of file archives.
Problems to Avoid During Folder Compression and Opening File Archives
Folder Compression and the Archive Destination
One common problem that I have seen with other developers is attempting to store a generated ZIP archive file within the same folder that we are archiving.
When you try the following command using the ZipFile.CreateFromDirectory() command to create a ZIP archive file from a sub-folder:
ZipFile.CreateFromDirectory(
$"{currentDirectory}\\{subFolder}",
$"{currentDirectory}\\{subFolder}\\{archiveFile}"
);
You would think the above will work, however it will only create the ZIP file in the sub-folder and leave it empty with the following exception being thrown:
Error Creating Archive for archive.zip. Error: The process cannot access the file '....archive.zip' because it is being used by another process.
When you think about it, the actual zip file is created first, then the operation of populating the file entries from the same sub-folder into the zip archive requires access to the sub-folder that has created the archive! However, the process thread that has created the empty archive has not released the process thread that has a lock on the file. This has resulted in a process deadlock!
To resolve the above issue, we create the ZIP archive outside of the sub-folder so that the thread that is creating the ZIP file from the sub-folder it does not interfere with the thread that is populating the ZIP entries within the same sub-folder. We do this as follows:
ZipFile.CreateFromDirectory(
$"{currentDirectory}\\{subFolder}",
$"{currentDirectory}\\{subFolder}_{archiveFile}"
);
Opening and Manipulating File ZIP Archives
Opening a stream and Zip Archive in Read mode will not allow you to update the comments or insert, remove entries from the archive. Updating the comment in this case will not work and will not give an error:
using (var stream =
System.IO.File.OpenRead($"{currentDirectory}\\{subFolder}_{archiveFile}"))
{
ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Read);
..
If you try the following to open the archive using the following method:
using (var streamDestination =
System.IO.File.OpenWrite($"{currentDirectory}\\{subFolder}_{archiveFile}"))
{
streamDestination.Seek(0, SeekOrigin.Begin);
ZipArchive zipArchive = new ZipArchive(streamDestination, ZipArchiveMode.Update);
..
You will get the following error:
System.ArgumentException: Update mode requires a stream with read, write, and seek capabilities.
at System.IO.Compression.ZipArchive..ctor(Stream stream, ZipArchiveMode mode, Boolean leaveOpen, Encoding entryNameEncoding)
at System.IO.Compression.ZipArchive..ctor(Stream stream, ZipArchiveMode mode)
To open the file in a writable mode which is also has seek capability, do the following:
using (var streamDestination =
new System.IO.FileStream($"{currentDirectory}\\{subFolder}_{archiveFile}",
FileMode.Open,
FileAccess.ReadWrite))
{
_logger.LogInformation($"Archive {archiveFile}. Seek Status: {streamDestination.CanSeek}");
streamDestination.Seek(0, SeekOrigin.Begin);
ZipArchive zipArchive = new ZipArchive(streamDestination, ZipArchiveMode.Update);
..
The above will permit you to seek through the archive file as well as perform updates.
In the above sections, we have seen how to perform basic ZIP file archiving and compression tasks.
I explained how to make use of the new static ZipFile class method:
ZipFile.CreateFromDirectory()
from within .NET 8.0 to create ZIP archives and make use of the ZipArchive class to update properties within the compressed archive.
In the next post, I will show how to extract the files from a ZIP archive into a separate folder.
That is all for today’s post.
I hope that you have found this post useful and informative.

Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.