Why is it that File.Move(sourceFileName, destFileName) works fine when the source file and destination files are in different partitions, but Directory.Move(sourceDirName, destDirName) don't? It throws
System.IO.IOException: "Source and destination path must have
identical roots. Move will not work across volumes."
I even tried to create a DirectoryInfo instance and use the MoveTo(destDirName) method but without success.
Am I missing something? Do I really have to implement a "move" functionality myself? (the directory I want to move is very large btw).
You should Use Copy Function followed by a remove. As Move only works in the same drive.
Directory.Move has a condition that states that :
IO Exception will be thrown if an attempt was made to move a directory to a different volume.
Another option is, to add a reference to the Microsoft.VisualBasic namespace and use the MoveDirectory method, which can move across volumes.
Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(sourceDirName, destDirName);
Although this is not a Vb.Net question but I found no one mentioned this method so I think might help... Only you need to convert it to C# if needed.
Code:
My.Computer.FileSystem.MoveDirectory(SrcDir,DestDir)
This works on different volume seamlessly/ per my experience.
Based on the posts "Copy a directory to a different drive" and "Non-recursive way to get all files in a directory and its subdirectories in Java", I wrote this non-recursive method and it works fine:
public static void Move(string source, string target)
{
if (!Directory.Exists(source))
{
throw new System.IO.DirectoryNotFoundException("Source directory couldn't be found.");
}
if (Directory.Exists(target))
{
throw new System.IO.IOException("Target directory already exists.");
}
DirectoryInfo sourceInfo = Directory.CreateDirectory(source);
DirectoryInfo targetInfo = Directory.CreateDirectory(target);
if (sourceInfo.FullName == targetInfo.FullName)
{
throw new System.IO.IOException("Source and target directories are the same.");
}
Stack<DirectoryInfo> sourceDirectories = new Stack<DirectoryInfo>();
sourceDirectories.Push(sourceInfo);
Stack<DirectoryInfo> targetDirectories = new Stack<DirectoryInfo>();
targetDirectories.Push(targetInfo);
while (sourceDirectories.Count > 0)
{
DirectoryInfo sourceDirectory = sourceDirectories.Pop();
DirectoryInfo targetDirectory = targetDirectories.Pop();
foreach (FileInfo file in sourceDirectory.GetFiles())
{
file.CopyTo(Path.Combine(targetDirectory.FullName, file.Name), overwrite: true);
}
foreach(DirectoryInfo subDirectory in sourceDirectory.GetDirectories())
{
sourceDirectories.Push(subDirectory);
targetDirectories.Push(targetDirectory.CreateSubdirectory(subDirectory.Name));
}
}
sourceInfo.Delete(true);
}
You can also p/invoke SHFileOperation which is the same function Windows Explorer uses to move directories around. It will either perform a true move or recursive-copy-then-delete, as appropriate.
It can also show the same progress UI as explorer, just by setting a flag.
I know this post is a little old... but there is a way around this! Don't try and move the directory, but zip it up and move it as a File.Move(src,dest); and you can then extract it and there you have it!
I had same problem in VB.NET and instead of "Directory.Move" I used MoveFolder with "FileSystemObject".
You can preserve creation dates with this method.
Scripting.FileSystemObject oFSO = new Scripting.FileSystemObject();
oFSO.MoveFolder(sourceDirName, destDirName)
i have this problem to and i like to solve it in this way
string startPath = #".\start";
string zipPath = #".\result.zip";
string extractPath = #".\extract";
ZipFile.CreateFromDirectory(startPath, zipPath);
ZipFile.ExtractToDirectory(zipPath, extractPath);
Related
This question already has answers here:
How do I create a file AND any folders, if the folders don't exist?
(9 answers)
Closed 4 years ago.
I've been writing a simple console application as a part of exercise in project. Tasks are rather straightforward:
2nd method has to create nested directory tree where every folder name is Guid.
3rd method has to put empty file in chosen directory tree at specific level.
My main problem lies within 3rd method. Because while it works fine and creates file 'till third level of any directory, beyond that point it always throw "System.IO.DirectoryNotFoundException" - as it "can't find part of the path".
I use string as a container for path, but since it's few Guid set together it gets pretty long. I had similar problem with creating directory, but in order to work I had to simply put #"\?\" prefix behind the path. So is there any way to make it work, or maybe get around that?
Here are method that fails. Specifically it's
File.Create(PathToFile + #"\blank.txt").Dispose();
And part of code which makes string and invokes it:
string ChosenDirectoryPath = currDir.FullName + #"\";
for (int i = 0; i <= Position; i++)
{
ChosenDirectoryPath += ListsList[WhichList][i];
}
if (!File.Exists(ChosenDirectoryPath + #"\blank.txt"))
{
FileMaker(ref ChosenDirectoryPath);
}
Edit:
To be specific, directories are made by method:
public List<string> DirectoryList = new List<string>();
internal static List<List<string>> ListsList = new List<List<string>>();
private static DirectoryInfo currDir = new DirectoryInfo(".");
private string FolderName;
private static string DirectoryPath;
public void DeepDive(List<string> DirectoryList, int countdown)
{
FolderName = GuidMaker();
DirectoryList.Add(FolderName + #"\");
if (countdown <= 1)
{
foreach (string element in DirectoryList)
{
DirectoryPath += element;
}
Directory.CreateDirectory(#"\\?\" + currDir.FullName + #"\" + DirectoryPath);
Console.WriteLine("Folders were nested at directory {0} under folder {1}\n", currDir.FullName, DirectoryList[0]);
ListsList.Add(DirectoryList);
DirectoryPath = null;
return;
}
DeepDive(DirectoryList, countdown-1);
}
Which is pretty messy because of recursion (iteration would be better but i wanted to do it this way to learn something). The point is that directories are made and stored in list of lists.
Creating files works properly but only for the first three nested folders. So the problem is that it is somehow loosing it's path to file in 4th and 5th level, and can't even make those manually. Could it be too long path? And how to fix this.
Here is exception that throws out:
System.IO.DirectoryNotFoundException: „Can't find part of the path
„C:\Some\More\Folders\1b0c7715-ee01-4df8-9079-82ea7990030f\c6c806b0-b69d-4a3a-88d0-1bd8a0e31eb2\9671f2b3-3041-42d5-b631-4719d36c2ac5\6406f00f-7750-4b5a-a45d-cebcecb0b70e\bcacef2b-e391-4799-b84e-f2bc55605d40\blank.txt”.”
So it throws full path to file and yet says that it can't find it.
You problem is that File.Create doesn't create the corresponding directories for you, instead it throws a System.IO.DirectoryNotFoundException.
You have to create those directories yourself, by using System.IO.Directory.CreateDirectory()
If this exception occurs because of a too long path, you can still use the "long path syntax (\\?\)" like you did when creating your directories.
See also this question: How to deal with files with a name longer than 259 characters? there is also a good article linked
I want to make an exact copy of some files, directories and subdirectories that are on my USB drive I:/ and want them to be in C:/backup (for example)
My USB drive has the following structure:
(just to know, this is an example, my drive has more files, directories and subdirectories)
courses/data_structures/db.sql
games/pc/pc-game.exe
exams/exam01.doc
Well, I am not sure how to start with this but my first idea is to get all the files doing this:
string[] files = Directory.GetFiles("I:");
The next step could be to make a loop and use File.Copy specifying the destination path:
string destinationPath = #"C:/backup";
foreach (string file in files)
{
File.Copy(file, destinationPath + "\\" + Path.GetFileName(file), true);
}
At this point everything works good but not as I wanted cause this doesn't replicate the folder structure. Also some errors happen like the following...
The first one happens because my PC configuration shows hidden files for every folder and my USB has an AUTORUN.INF hidden file that is not hidden anymore and the loop tries to copy it and in the process generates this exception:
Access to the path 'AUTORUN.INF' is denied.
The second one happens when some paths are too long and this generates the following exception:
The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the
directory name must be less than 248 characters.
So, I am not sure how to achieve this and validate each posible case of error. I would like to know if there is another way to do this and how (maybe some library) or something more simple like an implemented method with the following structure:
File.CopyDrive(driveLetter, destinationFolder)
(VB.NET answers will be accepted too).
Thanks in advance.
public static void Copy(string src, string dest)
{
// copy all files
foreach (string file in Directory.GetFiles(src))
{
try
{
File.Copy(file, Path.Combine(dest, Path.GetFileName(file)));
}
catch (PathTooLongException)
{
}
// catch any other exception that you want.
// List of possible exceptions here: http://msdn.microsoft.com/en-us/library/c6cfw35a.aspx
}
// go recursive on directories
foreach (string dir in Directory.GetDirectories(src))
{
// First create directory...
// Instead of new DirectoryInfo(dir).Name, you can use any other way to get the dir name,
// but not Path.GetDirectoryName, since it returns full dir name.
string destSubDir = Path.Combine(dest, new DirectoryInfo(dir).Name);
Directory.CreateDirectory(destSubDir);
// and then go recursive
Copy(dir, destSubDir);
}
}
And then you can call it:
Copy(#"I:\", #"C:\Backup");
Didn't have time to test it, but i hope you get the idea...
edit: in the code above, there are no checks like Directory.Exists and such, you might add those if the directory structure of some kind exists at destination path. And if you're trying to create some kind of simple sync app, then it gets a bit harder, as you need to delete or take other action on files/folders that don't exist anymore.
This generally starts with a recursive descent parser. Here is a good example: http://msdn.microsoft.com/en-us/library/bb762914.aspx
You might want to look into the overloaded CopyDirectory Class
CopyDirectory(String, String, UIOption, UICancelOption)
It will recurse through all of the subdirectories.
If you want a standalone application, I have written an application that copies from one selected directory to another, overwriting newer files and adding subdirectories as needed.
Just email me.
I have a piece of code here that breaks if the directory doesn't exist:
System.IO.File.WriteAllText(filePath, content);
In one line (or a few lines), is it possible to check if the directory leading to the new file doesn't exist and if not, to create it before creating the new file?
I'm using .NET 3.5.
To Create
(new FileInfo(filePath)).Directory.Create() before writing to the file.
....Or, if it exists, then create (else do nothing)
System.IO.FileInfo file = new System.IO.FileInfo(filePath);
file.Directory.Create(); // If the directory already exists, this method does nothing.
System.IO.File.WriteAllText(file.FullName, content);
You can use following code
DirectoryInfo di = Directory.CreateDirectory(path);
As #hitec said, you have to be sure that you have the right permissions, if you do, you can use this line to ensure the existence of the directory:
Directory.CreateDirectory(Path.GetDirectoryName(filePath))
An elegant way to move your file to an nonexistent directory is to create the following extension to native FileInfo class:
public static class FileInfoExtension
{
//second parameter is need to avoid collision with native MoveTo
public static void MoveTo(this FileInfo file, string destination, bool autoCreateDirectory) {
if (autoCreateDirectory)
{
var destinationDirectory = new DirectoryInfo(Path.GetDirectoryName(destination));
if (!destinationDirectory.Exists)
destinationDirectory.Create();
}
file.MoveTo(destination);
}
}
Then use brand new MoveTo extension:
using <namespace of FileInfoExtension>;
...
new FileInfo("some path")
.MoveTo("target path",true);
Check Methods extension documentation.
You can use File.Exists to check if the file exists and create it using File.Create if required. Make sure you check if you have access to create files at that location.
Once you are certain that the file exists, you can write to it safely. Though as a precaution, you should put your code into a try...catch block and catch for the exceptions that function is likely to raise if things don't go exactly as planned.
Additional information for basic file I/O concepts.
var filePath = context.Server.MapPath(Convert.ToString(ConfigurationManager.AppSettings["ErrorLogFile"]));
var file = new FileInfo(filePath);
file.Directory.Create(); If the directory already exists, this method does nothing.
var sw = new StreamWriter(filePath, true);
sw.WriteLine(Enter your message here);
sw.Close();
In my program I need to check that several paths to files are inside system temporary files folder (for example C:\Users\Roman\AppData\Local\Temp).
I haven't found any method in System.IO.File, System.IO.Directory and System.IO.Path classes to do so. So I created my own:
public static bool IsSafeToDeleteFileOrDirectory(string path)
{
try
{
string tempPath = Path.GetFullPath(
Path.Combine(Path.GetTempPath(), ".\\")
);
string fullPath = Path.GetFullPath(path);
return fullPath.StartsWith(tempPath) &&
fullPath.Length > tempPath.Length;
}
catch (Exception ex)
{}
return false;
}
But I am not sure if it will always work.
Is there any other simple solution besides traversing the folders tree and checking that each child folder exists and its parent folder is TEMP?
System.IO.Directory.Exists() can take relative paths as well. I think that should do it for you.
I believe your code will work even for thomasrutter's example since the paths are resolved by Path.GetFullPath.
The StartsWith approach will fail to account for this sort of thing:
tempPath is: /tmp/
fullPath is: /tmp/../etc/evil.cnf
You will need to normalize the two paths first, which will resolve anything like ../
Apparently I can't move files on different volumes using Directory.Move.
I have read that I have to copy each file individually to the destination, then delete the source directory.
Do I have any other option?
Regardless of whether or not Directory.Move (or any other function) performed the move between volumes, it would essentially be doing a copy and delete anyway underneath. So if you want a speed increase, that's not going to happen. I think the best solution would be to write your own reusable move function, which would get the volume label (C:,D:) from the to and from paths, and then either perform a move, or copy+delete when necessary.
To my knowledge there is no other way however deleting a directory has a catch: Read Only Files might cause a UnauthorizedAccessException when deleting a directory and all of its contents.
This recurses a directory and unsets all the read only flags. Call before Directory.Delete:
public void removeReadOnlyDeep(string directory)
{
string[] files = Directory.GetFiles(directory);
foreach (string file in files)
{
FileAttributes attributes = File.GetAttributes(file);
if ((attributes & FileAttributes.ReadOnly) != 0)
{
File.SetAttributes(file, ~FileAttributes.ReadOnly);
}
}
string[] dirs = Directory.GetDirectories(directory);
foreach (string dir in dirs)
{
removeReadOnlyDeep(dir);
}
}
An easier option would be, to add a reference to the Microsoft.VisualBasic namespace and use the MoveDirectory method, which can move across volumes.
Microsoft.VisualBasic.FileIO.FileSystem.MoveDirectory(sourceDirName, destDirName);
Try to use this:
public static void RobustMove(string sourceDirectory, string destDirectory)
{
//move if directories are on the same volume
if (Path.GetPathRoot(source) == Path.GetPathRoot(destination))
{
Directory.Move(source, destination);
}
else
{
CopyDirectoryRecursive(source, destination);
Directory.Delete(source, true);
}
}
You will find CopyDirectoryRecursive function here:
This should be working until you use spanned volume or symbol links to another physical disk.
To be even more robust you can improve this function to use Move until System.IO .Exception is thrown and then to switch to copying and deleting.