Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 262
Enigmativity Avatar asked Jan 12 '21 02:01

Enigmativity


People also ask

What is logical folder structure?

Logical Folders. Logical folders are the blue folders displayed in the Projects window. These folders are strictly for organizing files within the IDE and do not represent the actual location of files on your hard drive. The Files window shows the actual files and directory structure of your project.

How do I create a 7zip archive?

Step 1: Right click on the file / folder to be encrypted. Step 2: Select “7-Zip” then “Add to archive…” Step 3: In the Add to Archive window change the name of the archive you wish to create. Step 4: Change the Archive format to “Zip”.

How do I create a zip file without a folder?

You can use -j . -j --junk-paths Store just the name of a saved file (junk the path), and do not store directory names. By default, zip will store the full path (relative to the current directory).


4 Answers

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"
like image 51
msitt Avatar answered Oct 21 '22 12:10

msitt


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.

like image 27
tukan Avatar answered Oct 21 '22 12:10

tukan


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.

like image 2
stackprotector Avatar answered Oct 21 '22 12:10

stackprotector


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...

like image 1
Poiter Avatar answered Oct 21 '22 14:10

Poiter