I am using the following code to extract all files in a folder
using (ZipArchive archive = new ZipArchive(zipStream))
{
archive.ExtractToDirectory(location);
}
But if one file exist then it throws an exception. Is there is any way to tell the Compression API to replace the existing files.
I found one way is to get all the file names first then check whether file exist and delete it. But this is somehow very costly for me.
I have created an extension. any comment to it improve will be appreciated,
public static class ZipArchiveExtensions
{
public static void ExtractToDirectory(this ZipArchive archive, string destinationDirectoryName, bool overwrite)
{
if (!overwrite)
{
archive.ExtractToDirectory(destinationDirectoryName);
return;
}
DirectoryInfo di = Directory.CreateDirectory(destinationDirectoryName);
string destinationDirectoryFullPath = di.FullName;
foreach (ZipArchiveEntry file in archive.Entries)
{
string completeFileName = Path.GetFullPath(Path.Combine(destinationDirectoryFullPath, file.FullName));
if (!completeFileName.StartsWith(destinationDirectoryFullPath, StringComparison.OrdinalIgnoreCase))
{
throw new IOException("Trying to extract file outside of destination directory. See this link for more info: https://snyk.io/research/zip-slip-vulnerability");
}
if (file.Name == "")
{// Assuming Empty for Directory
Directory.CreateDirectory(Path.GetDirectoryName(completeFileName));
continue;
}
file.ExtractToFile(completeFileName, true);
}
}
}
This code will not throw exception when the folder is not exist, instead of that it will create the folder.
public static class ZipArchiveExtensions
{
public static void ExtractToDirectory(this ZipArchive archive, string destinationDirectoryName, bool overwrite)
{
if (!overwrite)
{
archive.ExtractToDirectory(destinationDirectoryName);
return;
}
foreach (ZipArchiveEntry file in archive.Entries)
{
string completeFileName = Path.Combine(destinationDirectoryName, file.FullName);
string directory = Path.GetDirectoryName(completeFileName);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
if (file.Name != "")
file.ExtractToFile(completeFileName, true);
}
}
}
Take a look at this: Creating zip files easily in .NET 4.5. Your problem seems to be adressed. Alternatively, you can also check DotNetZip.
As I'm a total Linq fan, the Linq-ish way just for reference:
using (var strm = File.OpenRead(zipPath))
using (ZipArchive a = new ZipArchive(strm))
{
a.Entries.Where(o => o.Name == string.Empty && !Directory.Exists(Path.Combine(basePath, o.FullName))).ToList().ForEach(o => Directory.CreateDirectory(Path.Combine(basePath, o.FullName)));
a.Entries.Where(o => o.Name != string.Empty).ToList().ForEach(e => e.ExtractToFile(Path.Combine(basePath, e.FullName), true));
}
You can extract files to some temp directory and than copy files with the
"File.Copy" with the ovveride option true to your destination directory
I know that it's not a perfect solution , but this way you do not need to
check if file exist
Here is a method that takes a path to the zip file.
Based on the accepted answer.
public void ExtractZipFileToDirectory(string sourceZipFilePath, string destinationDirectoryName, bool overwrite)
{
using (var archive = ZipFile.Open(sourceZipFilePath, ZipArchiveMode.Read))
{
if (!overwrite)
{
archive.ExtractToDirectory(destinationDirectoryName);
return;
}
DirectoryInfo di = Directory.CreateDirectory(destinationDirectoryName);
string destinationDirectoryFullPath = di.FullName;
foreach (ZipArchiveEntry file in archive.Entries)
{
string completeFileName = Path.GetFullPath(Path.Combine(destinationDirectoryFullPath, file.FullName));
if (!completeFileName.StartsWith(destinationDirectoryFullPath, StringComparison.OrdinalIgnoreCase))
{
throw new IOException("Trying to extract file outside of destination directory. See this link for more info: https://snyk.io/research/zip-slip-vulnerability");
}
if (file.Name == "")
{// Assuming Empty for Directory
Directory.CreateDirectory(Path.GetDirectoryName(completeFileName));
continue;
}
file.ExtractToFile(completeFileName, true);
}
}
}
Since .NET Standard 2.1, it's as easy as setting overwriteFiles to true in:
ZipFile.ExtractToDirectory(string sourceFile, string destDir, Encoding entryNameEncoding, bool overwriteFiles)
Example:
ZipFile.ExtractToDirectory("c:\\file.zip","c:\\destination_folder", Encoding.UTF8, true);
Hi I'm using DotNetZip download from nugget.
I just simply use this code.
This will auto replace the files in the directory if exists.
"OverwriteSilently" !
using (ZipFile archive = new ZipFile(#"" + System.Environment.CurrentDirectory + "\\thezipfile.zip"))
{
archive.ExtractAll(#"" + System.Environment.CurrentDirectory, ExtractExistingFileAction.OverwriteSilently);
}
This is useful when you have zip file path
public static class ZipArchiveHelper
{
public static void ExtractToDirectory(string archiveFileName, string destinationDirectoryName, bool overwrite)
{
if (!overwrite)
{
ZipFile.ExtractToDirectory(archiveFileName, destinationDirectoryName);
}
else
{
using (var archive = ZipFile.OpenRead(archiveFileName))
{
foreach (var file in archive.Entries)
{
var completeFileName = Path.Combine(destinationDirectoryName, file.FullName);
var directory = Path.GetDirectoryName(completeFileName);
if (!Directory.Exists(directory) && !string.IsNullOrEmpty(directory))
Directory.CreateDirectory(directory);
if (file.Name != "")
file.ExtractToFile(completeFileName, true);
}
}
}
}
}
A little remade method from the answer to create all the folders
public static void ExtractToDirectory(this ZipArchive archive, string destinationDirectoryName, bool overwrite)
{
if (!overwrite)
{
archive.ExtractToDirectory(destinationDirectoryName);
return;
}
foreach (ZipArchiveEntry file in archive.Entries)
{
string completeFileName = Path.Combine(destinationDirectoryName, file.FullName);
if (file.Name == "")
{// Assuming Empty for Directory
Directory.CreateDirectory(Path.GetDirectoryName(completeFileName));
continue;
}
// create dirs
var dirToCreate = destinationDirectoryName;
for (var i = 0; i < file.FullName.Split('/').Length - 1; i++)
{
var s = file.FullName.Split('/')[i];
dirToCreate = Path.Combine(dirToCreate, s);
if (!Directory.Exists(dirToCreate))
Directory.CreateDirectory(dirToCreate);
}
file.ExtractToFile(completeFileName, true);
}
}
Looks like the only way to dodge including that blob of code is to simply delete the files before extracting the archive with del:
del (location + "\*")
using (ZipArchive archive = new ZipArchive(zipStream))
{
archive.ExtractToDirectory(location);
}
It's not exactly what the OP wanted, but it is compact.
Related
static string mydir = #"C:\Boba\bin\Release\ZipTest";
static string zipfile = string.Concat(mydir, ".zip");
using (ZipStorer zip = ZipStorer.Create(zipfile))
{
zip.AddDirectory(ZipStorer.Compression.Deflate, mydir, zipfile);
}
But after I unpack the archive, folders appear
Dir: Boba -> bin > Release > ZipTest > Files...
How do I add only the ZipTest folder?
I tried to do it like this:
DirectoryInfo d = new DirectoryInfo(mydir);
zip.AddDirectory(ZipStorer.Compression.Deflate, Path.GetFileName(d.FullName), Path.GetFileName(d.FullName), comment);
A Zip archive is created, and inside a folder called ZipTestZipTest, inside there are files and an empty archive called .zip.
How to make it just ZipTest inside the archive?
And so that there is no empty archive in the ZipTest folder?
Try set 3rd argument (_pathnameInZip) for .AddDirectory as empty string:
string dir = #"C:\Boba\bin\Release\ZipTest";
string zipFile = string.Concat(dir, ".zip");
string comment = "My ZipTest";
using (ZipStorer zip = ZipStorer.Create(zipFile))
{
zip.AddDirectory(ZipStorer.Compression.Deflate, dir, string.Empty, comment);
}
If you need without an internal directory, then you can do this:
public static void PackToZipWithoutInternalDir(string dir, string zipout, string comment = "")
{
if (Directory.Exists(dir) && !string.IsNullOrWhiteSpace(dir) && !string.IsNullOrWhiteSpace(zipout))
{
try
{
using var zip = ZipStorer.Create(zipout, comment); // true for stream
zip.EncodeUTF8 = true; // Text encoding
zip.ForceDeflating = true; // Force file compression
foreach (string listDir in Directory.EnumerateDirectories(dir, "*", SearchOption.TopDirectoryOnly))
{
// Add folders with files to the archive
zip.AddDirectory(ZipStorer.Compression.Deflate, listDir, string.Empty);
}
foreach (string listFiles in Directory.EnumerateFiles(dir, "*.*", SearchOption.TopDirectoryOnly))
{
// Add residual files in the current directory to the archive.
zip.AddFile(ZipStorer.Compression.Deflate, listFiles, Path.GetFileName(listFiles));
}
}
catch (Exception ex) { Console.WriteLine(ex); }
}
}
Use:
namespace ZipStorerEx
{
using System;
using System.IO;
public static class Program
{
private static readonly string CurrDir = Environment.CurrentDirectory;
private static readonly string BeginDir = Path.Combine(CurrDir, "YouDir");
private static readonly string ZipOut = $"{BeginDir}.zip";
[STAThread]
public static void Main()
{
Console.Title = "ZipStorerEx";
PackToZipWithoutInternalDir(BeginDir, ZipOut, "It's Good");
Console.ReadKey();
}
}
}
I did the following, but i don't see the zip file in the directroy. C#
public static void AddToZip(string fileToAdd, string directory)
{
string entryName = fileToAdd.Replace(directory, string.Empty);
string archiveName = entryName.Replace(Path.GetExtension(entryName), ".zip");
using (ZipArchive za = ZipFile.Open(archiveName, ZipArchiveMode.Create))
{
za.CreateEntryFromFile(fileToAdd, entryName, CompressionLevel.Optimal);
}
}
and this is the link i followed.
http://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive(v=vs.110).aspx
Finally got it working after some trial and error.
public static void AddToZip(string fileToAdd, string directory)
{
string entryName = fileToAdd.Replace(directory, string.Empty);//name of the file inside zip archive
string tempDir = Path.Combine(directory, Path.GetFileNameWithoutExtension(entryName));
if (Directory.Exists(tempDir)) DeleteDirector(tempDir);
else Directory.CreateDirectory(tempDir);
System.IO.File.Move(fileToAdd, Path.Combine(tempDir, entryName));//as the CreateFromDirectoy add all the file from the directory provided, we are moving our file to temp dir.
string archiveName = entryName.Replace(Path.GetExtension(entryName), ".zip"); //name of the zip file.
ZipFile.CreateFromDirectory(tempDir, Path.Combine(directory, archiveName));
DeleteDirector(tempDir);
}
private static void DeleteDirector(string deletedir)
{
foreach (string file in Directory.GetFiles(deletedir))
{
System.IO.File.Delete(file);
}
Directory.Delete(deletedir);
}
I know this is not the best solution. so, you are welcome to modify/improve it.
Alright so I'm having a bit of an issue here. I'm trying to delete a specific folder inside another folder on my webserver using ASP.NET (C#) The folder being deleted is based on a textbox.
The directory is like this
/images/folderx
folderx = txtDelFolder.Text;
The problem is that everything I try deletes every single thing inside the images folder. I'm guessing that it is not recognizing my folder in the filepath
string path = #"\httpdocs\images\ +
txtDelFolder.Text;
I have also tried
string path = #"\httpdocs\images\ +
txtDelFolder.Text + "\";
Tried all this with both single '\' and double '\'
Would appreciate any help on this
Also where it says <directfilepath> I actually have the filepath typed out, just didn't want to share that here.
****edit****
string path = Server.MapPath("~/imagestest/" + txtEditTitle.Text);
if(Directory.Exists(path))
{
DeleteDirectory(path);
}
}
}
private void DeleteDirectory(string path)
{
foreach(string filename in Directory.GetFiles(path))
{
File.Delete(filename);
}
foreach(string subfolders in Directory.GetDirectories(path))
{
Directory.Delete(subfolders, true);
}
}
Try this:
private void DeleteFiles(string folder)
{
string path=Server.MapPath("~/httpdocs/images/" + folder);
string[] files=Directory.GetFiles(path, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
File.Delete(file);
}
//then delete folder
Directory.Delete(path);
}
try this one :
public void DeleteFolder(string folderPath)
{
if (!Directory.Exists(folderPath))
return;
// get the directory with the specific name
DirectoryInfo dir = new DirectoryInfo(folderPath);
try
{
foreach (FileInfo fi in dir.GetFiles())
fi.Delete();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Don't see why this wouldn't work:
public static bool DeleteDirectory(string input)
{
if (Directory.Exists(input))
{
Directory.Delete(input, true);
return !Directory.Exists(input);
}
else
return true;
}
string thePath = Server.MapPath(#"~/images/");
thePath = Path.Combine(Path.GetFullPath(thePath), txtInput.Text);
if(DeleteDirectory(thePath))
Console.WriteLine("YAY");
else
Console.WriteLine("BOO");
In C#.NET, How to copy a file to another location, overwriting the existing file if the source file is newer than the existing file (have a later "Modified date"), and doing noting if the source file is older?
You can use the FileInfo class and it's properties and methods:
FileInfo file = new FileInfo(path);
string destDir = #"C:\SomeDirectory";
FileInfo destFile = new FileInfo(Path.Combine(destDir, file.Name));
if (destFile.Exists)
{
if (file.LastWriteTime > destFile.LastWriteTime)
{
// now you can safely overwrite it
file.CopyTo(destFile.FullName, true);
}
}
You can use the FileInfo class:
FileInfo infoOld = new FileInfo("C:\\old.txt");
FileInfo infoNew = new FileInfo("C:\\new.txt");
if (infoNew.LastWriteTime > infoOld.LastWriteTime)
{
File.Copy(source path,destination path, true) ;
}
Here is my take on the answer: Copies not moves folder contents. If the target does not exist the code is clearer to read. Technically, creating a fileinfo for a non existent file will have a LastWriteTime of DateTime.Min so it would copy but falls a little short on readability. I hope this tested code helps someone.
**EDIT: I have updated my source to be much more flexable. Because it was based on this thread I have posted the update here. When using masks subdirs are not created if the subfolder does not contain matched files. Certainly a more robust error handler is in your future. :)
public void CopyFolderContents(string sourceFolder, string destinationFolder)
{
CopyFolderContents(sourceFolder, destinationFolder, "*.*", false, false);
}
public void CopyFolderContents(string sourceFolder, string destinationFolder, string mask)
{
CopyFolderContents(sourceFolder, destinationFolder, mask, false, false);
}
public void CopyFolderContents(string sourceFolder, string destinationFolder, string mask, Boolean createFolders, Boolean recurseFolders)
{
try
{
if (!sourceFolder.EndsWith(#"\")){ sourceFolder += #"\"; }
if (!destinationFolder.EndsWith(#"\")){ destinationFolder += #"\"; }
var exDir = sourceFolder;
var dir = new DirectoryInfo(exDir);
SearchOption so = (recurseFolders ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
foreach (string sourceFile in Directory.GetFiles(dir.ToString(), mask, so))
{
FileInfo srcFile = new FileInfo(sourceFile);
string srcFileName = srcFile.Name;
// Create a destination that matches the source structure
FileInfo destFile = new FileInfo(destinationFolder + srcFile.FullName.Replace(sourceFolder, ""));
if (!Directory.Exists(destFile.DirectoryName ) && createFolders)
{
Directory.CreateDirectory(destFile.DirectoryName);
}
if (srcFile.LastWriteTime > destFile.LastWriteTime || !destFile.Exists)
{
File.Copy(srcFile.FullName, destFile.FullName, true);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message + Environment.NewLine + Environment.NewLine + ex.StackTrace);
}
}
In a batch file, this will work:
XCopy "c:\my directory\source.ext" "c:\my other directory\dest.ext" /d
I've got main folder:
c:\test
And there I have 2 folders: Movies and Photos.
Photos has three folders with files with the same structure: People, Animals and Buildings. I'm trying this code:
Directory.Move(#"c:\test\Movies", #"c:\test\Test");
I get exception:
File already exists
This method will move content of a folder recursively and overwrite existing files.
You should add some exception handling.
Edit:
This method is implemented with a while loop and a stack instead of recursion.
public static void MoveDirectory(string source, string target)
{
var stack = new Stack<Folders>();
stack.Push(new Folders(source, target));
while (stack.Count > 0)
{
var folders = stack.Pop();
Directory.CreateDirectory(folders.Target);
foreach (var file in Directory.GetFiles(folders.Source, "*.*"))
{
string targetFile = Path.Combine(folders.Target, Path.GetFileName(file));
if (File.Exists(targetFile)) File.Delete(targetFile);
File.Move(file, targetFile);
}
foreach (var folder in Directory.GetDirectories(folders.Source))
{
stack.Push(new Folders(folder, Path.Combine(folders.Target, Path.GetFileName(folder))));
}
}
Directory.Delete(source, true);
}
public class Folders
{
public string Source { get; private set; }
public string Target { get; private set; }
public Folders(string source, string target)
{
Source = source;
Target = target;
}
}
Update:
This is a simpler version with the use of Directory.EnumerateFiles recursively instead of using a stack.
This will only work with .net 4 or later, to us it with an earlier version of .net change Directory.EnumerateFiles to Directory.GetFiles.
public static void MoveDirectory(string source, string target)
{
var sourcePath = source.TrimEnd('\\', ' ');
var targetPath = target.TrimEnd('\\', ' ');
var files = Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories)
.GroupBy(s=> Path.GetDirectoryName(s));
foreach (var folder in files)
{
var targetFolder = folder.Key.Replace(sourcePath, targetPath);
Directory.CreateDirectory(targetFolder);
foreach (var file in folder)
{
var targetFile = Path.Combine(targetFolder, Path.GetFileName(file));
if (File.Exists(targetFile)) File.Delete(targetFile);
File.Move(file, targetFile);
}
}
Directory.Delete(source, true);
}
The destination directory should not already exist - the Directory.Move method creates the destination directory for you.
ProcessStartInfo p = new ProcessStartInfo("cmd", "/c move \"c:\\test\\Movies\" \"c:\\test\Test\\"");
p.WindowStyle = ProcessWindowStyle.Hidden; //hide mode
Process.Start(p);
Is it safe for you to delete the destination folder before copying new contents to it?
Directory.Delete(#"c:\test\test");
Directory.Move(#"c:\test\movies",#"c:\test\test");
The most common 2 reasons why Directory.Move could fail are, if:
It's a different volume (you need to Copy/Delete)
It already exists (doesn't support overwrite by default)
Here is my simple solution for the second problem (overwrite):
public bool MoveDirectory(string sourceDirName, string destDirName, bool overwrite)
{
if (overwrite && Directory.Exists(destDirName))
{
var needRestore = false;
var tmpDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
try
{
Directory.Move(destDirName, tmpDir);
needRestore = true; // only if fails
Directory.Move(sourceDirName, destDirName);
return true;
}
catch (Exception)
{
if (needRestore)
{
Directory.Move(tmpDir, destDirName);
}
}
finally
{
Directory.Delete(tmpDir, true);
}
}
else
{
Directory.Move(sourceDirName, destDirName); // Can throw an Exception
return true;
}
return false;
}
You can use move method directly.
Directory.Move(#"c:\test\Movies\", #"c:\test\Test\");
The folder will be deleted and copied it into Test Folder.