Enigmativity
Enigmativity

Reputation: 117164

Creating a logical folder structure when creating a 7-Zip archive via the command-line

I have been archiving files using the "DotNetZip" library using code like this:

var files = new []
{
    new { file = new FileInfo(@"C:\_test\Data\Testing\data.txt"), source = "Data" },
    new { file = new FileInfo(@"C:\_test\Logs\Testing\data.txt"), source = "Logs" },
};  

using (Ionic.Zip.ZipFile zipFile = new Ionic.Zip.ZipFile())
{
    zipFile.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
    foreach (var f in files)
    {
        zipFile.AddFile(f.file.FullName, f.source);
    }
    zipFile.Save(@"C:\_test\output.zip");
}

The key thing to note here is that the file names are the same, they are directly contained within a folder with the same name, and when I create the Zip file I am creating logic folders for the files to sit in so there is no file name clash.

The problem is that the Zip compression is terrible. In fact I have examples of where I'm putting in a bunch of small files and getting a larger Zip!

I've then tested using 7-Zip and the results are excellent. The file sizes are much smaller than with Zip.

So, I've attempted to rewrite my code using 7-Zip, but there doesn't seem to be a good 7-Zip library for .NET and the only option is to use 7-Zip via the command line.

I've attempted this in Powershell:

."C:\Program Files\7-Zip\7z.exe" a -mx9 "C:\_test\20210112 Testing.7z" "C:\_test\Data\Testing\data.txt" "C:\_test\Logs\Testing\data.txt"

But I get this output:

7-Zip 19.00 (x64) : Copyright (c) 1999-2018 Igor Pavlov : 2019-02-21

Scanning the drive:
2 files, 40 bytes (1 KiB)

Creating archive: C:\_test\__test.7z

ERROR:
Duplicate filename on disk:
data.txt
data.txt

If I add the -spf command-line then it works, but my archive is now created with every single folder starting from C.

Is there a way in 7-Zip to allow it to create a logical folder structure when adding items from the command line?

To be clear, I'm selecting a subset of the files in the folders to be archived and I want to place them in logical folders.

Upvotes: 6

Views: 4714

Answers (4)

Poiter
Poiter

Reputation: 481

We use the SevenZipSharp NuGet package for this from code. It has a CompressFileDictionary feature where it adds all the files present in the dictionary, so you can cherry pick which files to add.

Example:

private void CreateArchive(string archivePath, Dictionary<string, string> materialsToArchive, int part)
{
    SevenZip.SevenZipCompressor.SetLibraryPath(Properties.Settings.Default.Location7zDll);
    SevenZip.SevenZipCompressor compressor = new SevenZip.SevenZipCompressor()
    {
        CompressionMode = part == 0 ? SevenZip.CompressionMode.Create : SevenZip.CompressionMode.Append,
        ArchiveFormat = SevenZip.OutArchiveFormat.Tar,
        CompressionMethod = SevenZip.CompressionMethod.Copy,
        CompressionLevel = SevenZip.CompressionLevel.None,
        IncludeEmptyDirectories = true,
        TempFolderPath = Path.GetDirectoryName(archivePath)
    };

    compressor.Compressing += new EventHandler<SevenZip.ProgressEventArgs>(CompressorCompressing);
    lastPercent = 0;
    firstTransfer = true;
    compressor.CompressFileDictionary(materialsToArchive, archivePath);
}

The Key of a dictionary entry is the relative path in the zip, and Value the real path to the file...

Upvotes: 1

stackprotector
stackprotector

Reputation: 13588

Is there a way in 7-Zip to allow it to create a logical folder structure when adding items from the command line?

To be clear, I'm selecting a subset of the files in the folders to be archived and I want to place them in logical folders.

Unfortunately, no, there is no such way in 7-Zip. The author confirmed that on his sourceforge site in 2015. Since then, there has not been any update on this.

The built-in PowerShell cmdlet Compress-Archive also does not support such a feature.


You can only achieve this by an obvious workaround: Create your logical folder structure temporarily, copy your files to it, archive this temporary folder structure and delete it afterwards. If you want to preserve the timestamps of your files - which will be altered by a regular copy operation - you can use Robocopy.exe (already built-in in Windows) in connection with the /COPY:DAT /DCOPY:DAT parameters to perform the copy operation.

Upvotes: 2

tukan
tukan

Reputation: 17345

Interesting question. I'm using 7z regularly, but never did I need such a workflow.

As you know you are getting the error due to the fact that in the archive you have duplicate file in flat structure. The 7z archiver does not know which one to used, thus the error.

I think you were quite close, but the issue is that you are using fully classified path to access the files for packing.

Solution

Your solution is to run the 7z from C:\_test\:

"C:\Program Files\7-Zip\7z.exe" a -mx9 -spf2 "C:\_test\20210112_Testing.7z" "Data\Testing\data.txt" "Logs\Testing\data.txt"

Now your archive will have the different path for the same file name. I also recommend not using space in the 7z file name - you will save yourself troubles in the future. The -spf2 switch will adjust if you have relative or absolute path based on the input. I tend to have it there if I should need to mix it.

Note

I like to use the -spf2 (does not include the drive letter in the full path) switch as that makes life easier when switching among OS.

Upvotes: 2

msitt
msitt

Reputation: 1237

If you simply want to zip up everything in the folder, then you could do this it will maintain folder structure.

."C:\Program Files\7-Zip\7z.exe" a -mx9 "C:\_test\20210112 Testing.7z" "C:\_test\*"

EDIT: To selectively archive certain files, use relative paths. In your case, you could set C:\_test as your current directory and then run

."C:\Program Files\7-Zip\7z.exe" a -mx9 "C:\_test\20210112 Testing.7z" "Data\Testing\data.txt" "Logs\Testing\data.txt"

Upvotes: 5

Related Questions