Copying files network drive getting around 260 character limit C# - c#

Right now i'm attempting to take a screenshot and save it to the local machine, then later copy it to a network drive:
Q:\1234567890123456789012345\123456789012\12345\Screenshots\sc.jpg
^ for an idea about how many characters were talking. I have it setup to create a screenshots folder after that "12345" When the program gets to the point this error occurs:
How can I avoid this?
Also:
DirectoryInfo scdestinfo = new DirectoryInfo(scdest);
DirectoryInfo scinfo = new DirectoryInfo(sccpath);
CopyAll(scinfo, scdestinfo);
That's my code for copying the folders/files.
public void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
copyall = false;
try
{
//check if the target directory exists
if (Directory.Exists(target.FullName) == false)
{
Directory.CreateDirectory(target.FullName);
}//end if
//copy all the files into the new directory
foreach (FileInfo fi in source.GetFiles())
{
fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
}//end foreach
//copy all the sub directories using recursion
foreach (DirectoryInfo diSourceDir in source.GetDirectories())
{
DirectoryInfo nextTargetDir = target.CreateSubdirectory(diSourceDir.Name);
CopyAll(diSourceDir, nextTargetDir);
}//end foreach
//success here
copyall = true;
}//end try
catch (IOException ie)
{
MessageBox.Show(ie.Message);
//handle it here
copyall = false;
}//end catch
}//end CopyAll
And the copyall function.

This is a Windows limitation, though there are Unicode variants of many API functions that allow for a maximum path length of approximately 32,767 characters, though that is again limited by the specific file system you are dealing with. Typically a given path component is limited to 255 characters (check GetVolumeInformation for the specific limitation for a given volume).
To use the Unicode variants (e.g. CreateFileW) that allow for longer paths, I believe you will have to deal with the Windows API directly rather than using the .Net library functions.

There is no straightforward way to avoid this, unfortunately.
Solution1: most natural one: generate a path in a way that it doesn't go over that limit.(shorter directory and file names) , or just reorganize directories layout, if this is possible.
Solution2: Don't like this one (wired, imo), but can use WindowsAPI, that doesn't fail in that case, like from the .NET 2.0 Workaround for PathTooLongException (it's for 2.0, but valid for nowdays)
Solution3. Try, when you're going to read/write data to a file, move OS cursor to that directory, so you will not need to specify complete (260+) to access it, but just file name. You should try this to see if it works for you. (even if this works I would prefer the first solution, but it worths to try)
Hope this helps.

Related

How to always write to an existing local drive using C# StreamWriter

Apologies for the poor title wording,
I have a StreamWriter set up in my C# program that creates and then writes to multiple text files on a local storage drive. The issue is that as I test this program on multiple machines - the names of the drives are inconsistent from machine to machine and do not always have a C: , D: , etc. As a result I experience errors whilst trying to write to drives that do not exist.
I have attempted to not specify the drive to be written to in the hopes that it would default to an existing drive as the specific location is unimportant for my needs. I.e. "C:\\wLocation.txt" becomes "wLocation.txt" but this did not seem to fix anything.
Code:
public static string getWeatherLocation()
{
String locationFile = "C:\\wLocation.txt";
String location;
try
{
System.IO.StreamReader reader = new StreamReader(locationFile);
location = reader.ReadLine();
reader.Close();
return location;
}
catch (Exception ex)
{
return null;
}
}
I'm not particularly knowledgeable with regards to the StreamWriter so the solution may be fairly simple but any help would be appreciated.
You can use System.IO.DriveInfo.GetDrives to get a list of drives on the machine:
DriveInfo[] allDrives = DriveInfo.GetDrives();
foreach (DriveInfo d in allDrives)
{
Console.WriteLine(d.Name); //C:\ etc.
}
You can then simply compose the file name of the given volume label and your desired file name:
var filePath = d.Name + "filename.txt";
Or better:
var filePath = Path.Combine(d.Name, "filename.txt");
In order to cope with different drives on different machines, you have several options:
Use relative file paths, e.g. locationFile = "wLocation.txt" will write the file in the current directory.
Use a special folder, e.g. Documents or AppData. You can use the Environment.GetFolderPath method to get one of those directories and create the full path like this: locationFile = Path.Combine(sysFolderPath, "wLocation.txt");.
Make the folder configurable in your application.
Please note that besides getting a folder path that works on the specific machine, you also need to pay attention to the permissions of the directory. Especially the first option might not work due to permissions if you install your application under Program Files.

Move Directory to another directory using c# [duplicate]

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);

clarification on how to access numerous files from server in c#

Basically I want to access 1000s of textfiles, input their data, store them in sqlite databases, parse them then show the output to users. So far I've developed a program that does this for only ONE textfile.
What I want to do: There is a Directory on our server which has about 15 folders. In each folder there are about 30-50 textfiles. I want to Loop through EACH FOLDER, and in each folder, loop through EACH file. A nice user helped me with doing this for 1000s of textfiles but I needed further clarification his method. This was his approach:
private static void ReadAllFilesStartingFromDirectory(string topLevelDirectory)
{
const string searchPattern = "*.txt";
var subDirectories = Directory.EnumerateDirectories(topLevelDirectory);
var filesInDirectory = Directory.EnumerateFiles(topLevelDirectory, searchPattern);
foreach (var subDirectory in subDirectories)
{
ReadAllFilesStartingFromDirectory(subDirectory);//recursion
}
IterateFiles(filesInDirectory, topLevelDirectory);
}
private static void IterateFiles(IEnumerable<string> files, string directory)
{
foreach (var file in files)
{
Console.WriteLine("{0}", Path.Combine(directory, file));//for verification
try
{
string[] lines = File.ReadAllLines(file);
foreach (var line in lines)
{
//Console.WriteLine(line);
}
}
catch (IOException ex)
{
//Handle File may be in use...
}
}
}
My problems/questions:
1) topLevelDirectory - what should I exactly put there? The 15 folders are located on a server with the format something like this \servername\randomfile\random\locationoftopleveldirectory. But how can I put the double slashes (at the begining of the path name) in this? Is this possible in c#? I thought we could only access local files (example :"c:\" - paths with single, not double slashes)
2) I dont understand what the purpose of the first foreach loop is. "readAllFilesStartingFromDirectory(subDirectory)" , yes we are looping the folders, but we aren't even doing anything with that loop. It's just reading the folders.
I'm not going to know your top level directory, but essentially if your files are in C:\tmp, then you would pass it #"C:\tmp". Escape your string with the # character to get double-slashes (or escape each slash individually).
string example0 = #"\\some\network\path";
string example1 = "\\\\some\\network\\path";
With ReadAllFilesStartingFromDirectory you're recursively calling IterateFiles, and it's doing whatever IterateFiles does in each directory. With the code you pasted above, that happens to be doing nothing, since Console.Writeline(line) is commented out.
Lets get to clarify the topLevelDirectory: This is a folder, which has items in it. It does not matter if these are files or other directories. These caontained other "subfolders" can contain folders themselves.
What toplevelDirectory means to you: take the folder which encapsulates all your files you need at the lowest level possible.
Your toplevelfolder is the directory which contains the 15 folders you want to crawl.
ReadAllFilesStartingFromDirectory(string topLevelDirectory)
You need to realise what recursion means. Recursion describes a method which calls itself.
Compare the name of the function (ReadAllFilesStartingFromDirectory), with the name of the function called in the foreach loop - they are the same.
In you case: The method gets all folders located in your topfolder. He then loops through all subfolders. Each subfolder then becomes the toplevel folder, which in turn can contain subfolders, who will become toplevelfolders in the next method call.
This is a nice way to loop through the whole file structure. If there are no more subfolders, there won't be any recursion and the method ends.
Your path problem: You need to mask the backslashes. You mask them by adding a backslash in front of them.
\path\randfolder\file.txt will become \\path\\randfolder\\file.txt
Or you set an # before the string. var path = #"\path\randfolder\file.txt", which also does the trick for you. Both ways work
1) Yes it is possible in C#. If your program has access permission to the network location you can use: "\\\\servername\\randomfile\\random\\locationoftopleveldirectory" - double slash in string interpreated as one slash. or you can use # before the string, it means 'ignore escape character' which is slash, then your string will look like this: #"\\servername\randomfile\random\locationoftopleveldirectory"
2) ReadAllFilesStartingFromDirectory is recursive function. Directories structure is hierarchical, therefore it is easy to traverse them recursively. This function looks for files in the root directory and in its sub-directories and in all their sub directories...
Try to put comment on this loop and you will see that only the files of the root directory parsed by the IterateFiles function

How to copy files from one disk to another location with the same folder structure?

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.

Moving files on different volumes in .NET

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.

Categories