Overwrite entry in zip file - c#

I'm having trouble, I got this code:
DirectoryInfo di = new DirectoryInfo(dir);
FileInfo[] rgFiles = di.GetFiles();
DirectoryInfo[] d = di.GetDirectories();
if(rgFiles != null && d != null) {
foreach (FileInfo fi in rgFiles)
{
foreach (DirectoryInfo dii in d)
{
using (ZipFile zip = ZipFile.Read(locateZipFile()))
{
zip.AddFile(fi.FullName, "");
zip.AddDirectory(dii.FullName,dii.Name);
toolStripStatusLabel1.Text = "Inserting " + fi.Name;
toolStripStatusLabel1.Text = "Inserting " + dii.Name + " and all of it's contents";
MessageBox.Show("Inserted the file " + fi.Name);
MessageBox.Show("Inserted the folder " + dii.Name + " and all contents in it.");
zip.Save();
}
}
}
Everything works great, but when I'm trying to add a file that is named the same in the zip, it does not overwrite it, which i want it to.. Any ideas on how i can do that? thanks.

You can use the UpdateFile method.
zip.UpdateFile(fi.FullName, "");
This method adds a file to a zip archive, or, if the file already exists in the zip archive, this method Updates the content of that given filename in the zip archive. The UpdateFile method might more accurately be called "AddOrUpdateFile".
Upon success, there is no way for the application to learn whether the file was added versus updated.

Before the line
zip.AddFile(fi.FullName, "");
you must test if the name already exists in entries. If yes, remove it and then insert it again.

Related

Suggestions on speeding up hard drive backup code

I have the following hard drive backup code that compares the .LastWriteTime() time of each file before copying and it is running slower than I expected. My assumption is that it should run pretty fast (on the order of a few minutes) if there are no files to update. I'm finding that it is still taking over an hour for 210 GB via USB3.0. I'm wondering if there are any unnecessary, time-consuming parts of my code that I can improve. I was also thinking about putting each directorycopy() call on a different thread (at least for the first level of directories, but was unsure if that was bad practice).
The code is mostly borrowed from:
https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories
I made changes to ignore the $Recycle Bin folder, log the files that have changed or had issues such as long filenames and being deliberate in how the Exceptions were handled. But most importantly, I added a check to see which file is newer before copying.
private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
if (sourceDirName.Contains("$")) // avoids $Recycle Bin
return;
if (!dir.Exists)
{
textb_Status.AppendText("Issue with " + dir.FullName + " This folder will not be compied.");
return;
//throw new DirectoryNotFoundException(
// "Source directory does not exist or could not be found: "
// + sourceDirName);
}
DirectoryInfo[] dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
try
{
file.CopyTo(temppath);
}
catch (PathTooLongException)
{
textb_Status.AppendText("Filename Too long \n " + file.FullName + "\n");
}
catch (IOException ex)
{
FileInfo sourcefile = new FileInfo(file.FullName);
FileInfo destFile = new FileInfo(temppath);
int CompareValue = sourcefile.LastWriteTime.CompareTo(destFile.LastWriteTime); //<0==> Earlier (old) =0 ==> same >0 Later (newer)
//textb_Status.AppendText("CompareValue: " + CompareValue + "\n");
if (CompareValue > 0) // Represents newer file
{
file.CopyTo(temppath, true);
textb_Status.AppendText("Updated: " + file.FullName + "\n");
}
}
catch (Exception ex2)
{
textb_Status.AppendText("Issue with " + file.FullName + "\n");
textb_Status.AppendText("Error Message \n");
textb_Status.AppendText(ex2.Message + "\n");
}
}
// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
I'm expecting the backup process to be on the order of a few minutes if there are only a few files to update.
I don't think, it's the amount of data, which slows down the process, but the number of files. The initial file access (check if it exists, get the stats) is pretty expensive, regardless of file size. Furthermore, many people consider using exceptions for control-flow bad style and throwing and catching exceptions may be quite expensive. And from your use case (ie most of the files are unchanged) there are MANY exceptions thrown.
Also depending on your disks (SSD or HDD), multithreaded reads and writes may be a very bad idea and slow down the whole process.
And depending on the implementation of File.Copy() you may be better off, checking the target first, and only do the Copy if it's really necessary. But this is something you can only know after a benchmark.
Thank you #derpirscher. For each file, I checked if it existed before trying to write. This way, no exception is thrown. The drive was checked in about 5 seconds! I modified a few files deep in the source directory to make sure they were being detected and copied. They were.
I had the sense Exceptions were expensive, I just didn't know it was this bad....great lesson!
My code is below. Note: I was receiving errors when trying to get files from my System Volume Information folder, so I started with a check to make sure sourceDirName did not equal that directory.
private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
{
if (sourceDirName.Contains("System Volume Information"))
return;
//textb_Status.AppendText("Current Directory: " + sourceDirName +"\n");
DirectoryInfo[] dirs = null;
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
if (sourceDirName.Contains("$")) // avoids $Recycle Bin
return;
if (!dir.Exists)
{
textb_Status.AppendText("Issue with " + dir.FullName + " This folder will not be compied.");
return;
//throw new DirectoryNotFoundException(
// "Source directory does not exist or could not be found: "
// + sourceDirName);
}
{
dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
try
{
if (File.Exists(temppath)) // Check for newer
{
FileInfo sourcefile = new FileInfo(file.FullName);
FileInfo destFile = new FileInfo(temppath);
int CompareValue = sourcefile.LastWriteTime.CompareTo(destFile.LastWriteTime); //<0==> Earlier (old) =0 ==> same >0 Later (newer)
//textb_Status.AppendText("CompareValue: " + CompareValue + "\n");
if (CompareValue > 0) // Represents newer file
{
file.CopyTo(temppath, true);
textb_Status.AppendText("********** Updated: " + file.FullName + "********* \n");
}
}
else
{
file.CopyTo(temppath);
}
}
catch (PathTooLongException)
{
textb_Status.AppendText("Filename Too long \r\n\n " + file.FullName + "\r\n\n");
}
catch (IOException ex)
{
FileInfo sourcefile = new FileInfo(file.FullName);
FileInfo destFile = new FileInfo(temppath);
int CompareValue = sourcefile.LastWriteTime.CompareTo(destFile.LastWriteTime); //<0==> Earlier (old) =0 ==> same >0 Later (newer)
//textb_Status.AppendText("CompareValue: " + CompareValue + "\n");
if (CompareValue > 0) // Represents newer file
{
file.CopyTo(temppath, true);
textb_Status.AppendText("Updated: " + file.FullName + "\n");
}
}
catch (Exception ex2)
{
textb_Status.AppendText("Issue with " + file.FullName + "\n");
textb_Status.AppendText("Error Message \n");
textb_Status.AppendText(ex2.Message + "\n\n");
}
}
// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
}

Unable to move files from a listbox to a newly created folder

I'm having issues moving files from a listbox to a newly created folder. I get: Cannot create a file when that file already exists.
public void CreateFolders()
{
//create folders
string folder1 = pattern.Substring(0, 2);
string folder2 = pattern.Substring(3, 2);
string folder3 = pattern.Substring(6, 2);
Directory.CreateDirectory("c:\\destinationfolder" + "\\" + folder1);
Directory.CreateDirectory("c:\\destinationfolder" + "\\" + folder1 + "\\" + folder2);
Directory.CreateDirectory("c:\\destinationfolder" + "\\" + folder1 + "\\" + folder2 + "\\" + folder3);
var destinationDirectoryFinal = Directory.CreateDirectory("c:\\destinationfolder" + "\\" + folder1 + "\\" + folder2 + "\\" + folder3);
destinationDirectory = destinationDirectoryFinal.FullName.ToString();
}
public void MoveFiles()
{
try
{
//Move files from listbox to newly created folders
foreach (string files in listBox1.Items)
{
File.Move(files, destinationDirectory);
}
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex);
}
}
The problem you're running into here is that you're trying to move a file to a directory that already contains a file with that name.
There are a couple of options you can choose from:
Option One
Check if the file exists before attempting the Move
foreach (var file in listBox1.Items)
{
// Only move the file if it doesn't already exist
if (!File.Exists(Path.Combine(destinationDirectory, Path.GetFileName(file))))
{
File.Move(file, destinationDirectory);
}
}
Option Two
Always overwrite the file if it exists. We can do this in a two step process - first by calling File.Copy with the "overwrite" parameter set to true, and then by calling File.Delete to delete the file in the original location:
foreach (var file in listBox1.Items)
{
// If the destination file already exists, overwrite it. Then delete the original
File.Copy(file, Path.Combine(destinationDirectory, Path.GetFileName(file)), true);
File.Delete(file);
}
Note:
Another thing you can do to prevent errors (in either case) is to ensure that the source file exists, and that the source directory is not the same as the destination directory before you try anything:
foreach (var file in listBox1.Items)
{
// Ensure that the file exists and that the source
// and destination directories are not the same
if (!(File.Exists(file)) ||
Path.GetDirectoryName(file).Equals(
destinationDirectory, StringComparison.OrdinalIgnoreCase))
{
continue; // Continue to the next loop iteration without doing anything
}
// Rest of loop code here...
}

C# File.Delete doesn't not work

I am trying to delete some files in a directory from a separate thread, but sometimes the delete doesn't work.
DirectoryInfo dirInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
FileInfo[] fileNames = dirInfo.GetFiles("*.*");
foreach (FileInfo fileName in fileNames)
{
string destinationFilename = cncDestinationDirectory + #"\" + dirInfo.Name + #"\" + fileName.Name;
if (File.Exists(destinationFilename))
File.Delete(destinationFilename);
File.Move(fileName.FullName, destinationFilename);
}
My goal is to move some files in a directory but, as I know the File.Move doesn't work if the destination file already exists. So, I check if the file exists and if it is true, I delete this file, then move to the original.
The File.Delete also cause a prematurely exit from the function.
The current directory is not the same folder as the executable is running because I set previously it into another folder.
How can I avoid this error? And still move the files in the destination directory?
The problem there is that the access to the file is denied because of the read only attribute of the file.
So, I set all my files attributes as normal as the follow:
DirectoryInfo dirInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
FileInfo[] fileNames = dirInfo.GetFiles("*.*");
foreach (FileInfo fileName in fileNames)
{
if (fileName.Extension == ".iso")
return;
string destinationFilename = cncDestinationDirectory + #"\" + dirInfo.Name + #"\" + fileName.Name;
fileName.Attributes = FileAttributes.Normal;
if (File.Exists(destinationFilename))
{
File.SetAttributes(destinationFilename, FileAttributes.Normal);
File.Delete(destinationFilename);
}
File.Move(fileName.FullName, destinationFilename);
}
You need to decide how to handle error cases like you suggest in your question. It's entirely possible that between checking the file exists and then deleting it, that the file has been opened by another process. You can catch an exception around the File.Delete and then not move the origin file if it throws, but you will end up with files that haven't moved. There's nothing you can do about it.
DirectoryInfo dirInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
FileInfo[] fileNames = dirInfo.GetFiles("*.*");
foreach (FileInfo fileName in fileNames)
{
string destinationFilename = cncDestinationDirectory + #"\" + dirInfo.Name + #"\" + fileName.Name;
try
{
if (File.Exists(destinationFilename))
File.Delete(destinationFilename);
File.Move(fileName.FullName, destinationFilename);
}
catch(IOException exception)
{
Console.WriteLine($"Can't move file { filename.FullName}");
}
}

c# UnauthorizedAccessException when using ZipArchive but not ZipFile

I am able to zip files from a specific folder using ZipFile.CreateFromDirectory in the following test code (I only used this code to test how zipping works):
// Where the files are located
string strStartPath = txtTargetFolder.Text;
// Where the zip file will be placed
string strZipPath = #"C:\Users\smelmo\Desktop\testFinish\" + strFileNameRoot + "_" + txtDateRange1.Text.Replace(#"/", "_") + "_" + txtDateRange2.Text.Replace(#"/", "_") + ".zip";
ZipFile.CreateFromDirectory(strStartPath, strZipPath);
However, this zips together ALL of the contents in the folder. I am trying to zip together specific items in the folder using ZipArchive in the following code:
// Where the files are located
string strStartPath = txtTargetFolder.Text;
// Where the zip file will be placed
string strZipPath = #"C:\Users\smelmo\Desktop\testFinish\" + strFileNameRoot + "_" + txtDateRange1.Text.Replace(#"/", "_") + "_" + txtDateRange2.Text.Replace(#"/", "_") + ".zip";
using (ZipArchive archive = ZipFile.OpenRead(strStartPath))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (!(entry.FullName.EndsWith(".TIF", StringComparison.OrdinalIgnoreCase)))
{
entry.ExtractToFile(Path.Combine(strZipPath, entry.FullName));
}
}
}
It is giving the error at ZipFile.OpenRead(strStartPath). Why am I able to access the exact folder in the first block of code but not the second? Or is there an easier way to search through a folder and only zip specific items?
You are utilizing the Zip libraries wrong
Effectively you are trying to open a directory as if it were a zip file, then loop over the contents of that directory (which again is actually a zip file) and then attempting to extract each member into a different zip file
Here is a working example of what you have described you are trying to do:
string strStartPath = #"PATH TO FILES TO PUT IN ZIP FILE";
string strZipPath = #"PATH TO ZIP FILE";
if (File.Exists(strZipPath))
File.Delete(strZipPath);
using (ZipArchive archive = ZipFile.Open(strZipPath, ZipArchiveMode.Create))
{
foreach (FileInfo file in new DirectoryInfo(strStartPath).GetFiles())
{
if (!(file.FullName.EndsWith(".TIF", StringComparison.OrdinalIgnoreCase)))
{
archive.CreateEntryFromFile(Path.Combine(file.Directory.ToString(), file.Name), file.Name);
}
}
}
This will take all the root level contents of a folder and put it in the zip file. You will need to implement your own way of getting subfolders and their contents recursively, but that is beyond the scope of this question.
EDIT: Here is a working example with proper folder recursion to select all files even in subdirectories
public void ZipFolder()
{
string strStartPath = #"PATH TO FILES TO PUT IN ZIP FILE";
string strZipPath = #"PATH TO ZIP FILE";
if (File.Exists(strZipPath))
File.Delete(strZipPath);
using (ZipArchive archive = ZipFile.Open(strZipPath, ZipArchiveMode.Create))
{
foreach (FileInfo file in RecurseDirectory(strStartPath))
{
if (!(file.FullName.EndsWith(".TIF", StringComparison.OrdinalIgnoreCase)))
{
var destination = Path.Combine(file.DirectoryName, file.Name).Substring(strStartPath.Length + 1);
archive.CreateEntryFromFile(Path.Combine(file.Directory.ToString(), file.Name), destination);
}
}
}
}
public IEnumerable<FileInfo> RecurseDirectory(string path, List<FileInfo> currentData = null)
{
if (currentData == null)
currentData = new List<FileInfo>();
var directory = new DirectoryInfo(path);
foreach (var file in directory.GetFiles())
currentData.Add(file);
foreach (var d in directory.GetDirectories())
RecurseDirectory(d.FullName, currentData);
return currentData;
}

DirectoryInfo.GetFiles() is not returning all files on desktop(it excludes shortcuts)

I have a listBox1 that should display all the files on my desktop, i have used the following method to do so
string filepath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
DirectoryInfo path = new DirectoryInfo(filepath);
foreach (var file in path.GetFiles())
{
listBox1.Items.Add("File : " + file.Name);
}
It works but for some reason it doesn't display some shortcuts, it displays a few shortcuts but most of them are not displayed. I have no idea why this is happening
You are probably missing the shortcuts in the "All Users" desktop:
string filepath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
DirectoryInfo path = new DirectoryInfo(filepath);
foreach (var file in path.GetFiles())
{
listBox1.Items.Add("File : " + file.Name);
}
// Get files in the "common" desktop
filepath = Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
path = new DirectoryInfo(filepath);
foreach (var file in path.GetFiles())
{
listBox1.Items.Add("File : " + file.Name);
}
You can refactor to combine the common code if that works.

Categories