File Copy failing to create files with same name - c#

I am copying files from one location to another. I encountered an issue where sometimes a file with the same name would try to save and it would break the program. So I added the logic below to add a number to the filename before copying it:
int counter = 0;
try
{
File.Copy(FileToCopy, FileToSave);
}
catch (Exception)
{
string CurrentFileName = FileToSave.Split('\\', '\\')[4];
string CurretFilePrefix = CurrentFileName.Split('.')[0];
string CurrentFileSuffix = CurrentFileName.Split('.')[1];
string UpdatedFileName = CurretFilePrefix + "_" + counter + "." + CurrentFileSuffix;
File.Copy(FileToCopy, UpdatedFileName);
counter++;
}
However, this is now causing a crash saying the file already exists:
When I check the file does not exist:
Why am I getting this exception? How do I save copies of these files?

A good approach to this problem would include (i) a loop; (ii) use of Path expressions; (iii) avoidance of try-catch when you can test if the file exists; (iv) use of a specific Exception for the extremely unlikely case that two threads or processes are trying to do this same copy at the same time and each get past the File.Exists check; (v) avoidance of while-true-forever loops as even the best code can contain mistakes that could cause a spin-wait forever in production code on a server and it's better to instead have an exception that tells you when something has gone wrong.
int counter = 0;
string proposedDest = dest;
while(counter < 5000)
{
if (!File.Exists(proposedDest))
{
try
{
File.Copy(fileToCopy, proposedDest);
break;
}
catch (IOException ex) when ((uint)ex.HResult == 0x80070050)
{
}
}
counter++;
proposedDest = Path.Combine(Path.GetDirectoryName(dest),
Path.GetFileNameWithoutExtension(dest) +
"_" + counter + Path.GetExtension(dest));
}
;
if (counter == 5000)
throw new Exception($"Could not copy file {fileToCopy} too many retries");
[A better approach would also not use hard coded constants littered through the code ;)]

As CurrentFileName is relative, you are trying to save the file in the location of your executable. Check that directory if your file exists.
Also, there are much better ways to find if the file exists, and also to get the filename and extension of a given file.
i.e. you should use the File.Exists method to determine if the file exists, instead of using exceptions for flow control
if (File.Exists(FileToSave))
{
FileToSave = GetNewFileName(FileToSave)
}
try
{
File.Copy(FileToCopy, FileToSave);
}
catch (Exception)
{
//something went really wrong
}
You should also use the Path methods for getting parts of the name, instead of "knowing" to get the fifth part of the filename (Path.GetExtension, Path.GetFileNameWithoutExtension)
private string GetNewFileName(string oldFileName){
var counter = 0;
var extension = Path.GetExtension(oldFileName);
var directory = Path.GetDirectoryName(oldFileName);
var fileName = Path.GetFileNameWithoutExtension(oldFileName);
var newFileName = Path.Combine(directory,
string.Format("{1}_{2}{3}", filename, counter, extension);
while (File.Exists(newFileName)){
counter++;
newFileName = Path.Combine(directory,
string.Format("{1}_{2}{3}", filename, counter, extension);
}
return newFileName;
}

use this instead:
string CurrentFileName = System.IO.Path.GetFileName(FileToSave);
string CurretFilePrefix = System.IO.Path.GetFileNameWithoutExtension(FileToSave);
string CurrentFileSuffix = System.IO.Path.GetExtension(FileToSave);
string UpdatedFileName = CurretFilePrefix + "_" + counter + "." + CurrentFileSuffix;
Then you have
File.Copy(FileToCopy, UpdatedFileName);
Are you sure this shouldn't be
File.Copy(FileToSave, UpdatedFileName); // FileToSave instead of FileToCopy

Related

Make syntax shorter when using if & else with statements

I'm currently working on a dll library project.
if (!Directory.Exists(MenGinPath))
{
Directory.CreateDirectory(MenGinPath + #"TimedMessages");
File.WriteAllLines(MenGinPath + #"TimedMessages\timedmessages.txt", new string[] { "Seperate each message with a new line" });
}
else if (!File.Exists(MenGinPath + #"TimedMessages\timedmessages.txt"))
{
Directory.CreateDirectory(MenGinPath + #"TimedMessages");
File.WriteAllLines(MenGinPath + #"TimedMessages\timedmessages.txt", new string[] { "Seperate each message with a new line" });
}
As you can see if the statement Directory.Exists is false a specific directory (MenGinPath) will be created. However, if the same path, with another file in addition is false, the second functions will be called.
My question is the following: is there any way to make this shorter?
Because as you can see I'm calling 2 times the same functions:
Directory.CreateDirectory(MenGinPath + #TimedMessages\timedmessages.txt
and
File.WriteAllLines(MenGinPath + #"\TimedMessages\timedmessages.txt"))
Any help would be welcome!!
You don't need to check if directory exists because Directory.CreateDirectory automatically creates the directory if it does not exists and does nothing if the directory already exists.
Also, do not include the filename when creating the directory. Yes, it wont error but just for clarity sake.
Another one is to use Path.Combine instead of hardcoding the path. This will improve readability of your code.
So, here's what I can come up with:
string dir = Path.Combine(MenGinPath, #"Groups\TimesMessages");
string file = Path.Combine(dir, "timedmessages.txt");
// this automatically creates all directories in specified path
// unless it already exists
Directory.CreateDirectory(dir);
//of course, you still need to check if the file exists
if (!File.Exists(file) {
File.WriteAllLines(filePath, new string[] { "Seperate each message with a new line" });
}
/* or if file exists, do your stuff (optional)
* else {
* //do something else? maybe edit the file?
* }
*/
You can make your code shorter given the fact that CreateDirectory does nothing when the directory exists. Moreover do not pullute your code with all that string concatenations to create the path and the file names.
Just do it one time before entering the logic using the appropriate method to create filenames and pathnames (Path.Combine).
string messagePath = Path.Combine(MenGinPath, "TimedMessages");
string fileName = Path.Combine(messagePath, "timedmessages.txt");
// call the create even if it exists. The CreateDirectory checks the fact
// by itself and thus, if you add your own check, you are checking two times.
Directory.CreateDirectory(messagePath);
if (!File.Exists(fileName)
File.WriteAllLines(fileName, new string[] { "Seperate each message with a new line" });
Would something like this work?
string strAppended = string.Empty;
if (!Directory.Exists(MenGinPath))
{
strAppended = MenGinPath + #"Groups\timedmessages.txt";
}
else if (!File.Exists(MenGinPath + #"TimedMessages\timedmessages.txt"))
{
strAppended = MenGinPath + #"TimedMessages\TimedMessages.txt";
}
else
{
return;
}
Directory.CreateDirectory(strAppended);
File.WriteAllLines(strAppended, new string[] { "Seperate each message with a new line" });
I have found that it is a great idea to reuse blocks of code like this instead of hiding them in if statements because it makes code maintenance and debugging easier and less prone to missed bugs.
It seems the only difference between the 2 cases is the path. So just get only this path in your if-else
const string GroupsPath = #"Groups\timedmessages.txt";
const string TimedMessagesTxt = #"TimedMessages\TimedMessages.txt";
string addPath = null;
if (!Directory.Exists(MenGinPath)) {
addPath = GroupsPath;
} else if (!File.Exists(Path.Combine(MenGinPath, TimedMessagesTxt))) {
addPath = TimedMessagesTxt;
}
If (addPath != null) {
Directory.CreateDirectory(Path.Combine(MenGinPath, addPath));
File.WriteAllLines(Path.Combine(MenGinPath, TimedMessagesTxt),
new string[] { "Seperate each message with a new line" });
}
Note: Using Path.Combine instead of string concatenation has the advantage that missig or extra \ are added or removed automatically.

Drag Drop copy file

I've perhaps done something marginally stupid, but can't see what it is!!
string pegasusKey = #"HKEY_LOCAL_MACHINE\SOFTWARE\Pegasus\";
string opera2ServerPath = #"Server VFP\";
string opera3ServerPath = #"O3 Client VFP\";
string opera2InstallationPath = null;
string opera3InstallationPath = null;
//Gets the opera Installtion paths and reads to the string opera*InstallationPath
opera2InstallationPath = (string)Registry.GetValue(pegasusKey + opera2ServerPath + "System", "PathToServerDynamic", null);
opera3InstallationPath = (string)Registry.GetValue(pegasusKey + opera3ServerPath + "System", "PathToServerDynamic", null);
string Filesource = null;
string[] FileList = (string[])e.Data.GetData(DataFormats.FileDrop, false);
foreach (string File in FileList)
Filesource = File;
label.Text = Filesource;
if (System.IO.Directory.Exists(opera3InstallationPath))
{
System.IO.File.Copy(Filesource, opera3InstallationPath);
MessageBox.Show("File Copied from" + Filesource + "\n to" + opera3InstallationPath);
}
else
{
MessageBox.Show("Directory Doesn't Exist");
}
The user drags the file onto the window, I then get the installation path of an application which is then used as the destination for the source file.. When the application is runs, it throws the error directory not found. But surely if the directory doesn't exists is should step into the else statement? a simple application that is becoming a headache!!
Your Filesource must be invalid. Here's what I suggest:
Step your code, put a break point on first line of the if(Directory.Exists(...)) code block.
Examine Filesource by adding it to the watch window, check if it is what you expect
Open the "Immediate Window" Type File.Exists(Filesource) and check result (should be true). Or.. Directory.Exists(Path.GetDirectory(Filesource))
Also, I'm almost certain you have a logic error in this portion of your code.. (You are assigning a variable in a loop over and over again, did you mean to append it? This doesn't make sense).
string Filesource = null;
string[] FileList = (string[])e.Data.GetData(DataFormats.FileDrop, false);
foreach (string File in FileList)
Filesource = File;
label.Text = Filesource;

"Could not find part of the path" error when copying a file

I've googled about this all over the Internet and still haven't found a solution. As an ultimate try, I hope someone can give me an exact answer.
I get that error when I try to copy a file from a directory to another in an File Explorer I'm trying to do on my own. It has a treeview control to browse for directories and a listview control to display the contents of the directory. This is how the code would look like, partially:
private void copyToolStripMenuItem_Click(object sender, EventArgs e)
{
sourceDir = treeView1.SelectedNode.FullPath;
for (int i = 0; i < listView1.SelectedItems.Count; ++i)
{
ListViewItem l = listView1.SelectedItems[i];
toBeCopied[i] = l.Text; // string[] toBeCopied, the place where I save the file names I want to save
}
}
private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
{
targetDir = treeView1.SelectedNode.FullPath;
try
{
for (int i = 0; i < toBeCopied.Length; ++i)
{
File.Copy(sourceDir + "\\" + toBeCopied[i], targetDir + "\\" + toBeCopied[i], true);
refreshToolStripMenuItem_Click(sender, e);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.TargetSite);
}
}
The place where I got the error is at File.Copy(sourceDir + "\\" + toBeCopied[i] ....
I've read that it could be something that has to do with the mapping of devices, but I don't really know what that is.
Can you take a look at the Path.Combine method on MSDN? This will help make sure all your entire path doesn't have extra \'s where they shouldn't be.
i.e. Path.Combine(sourceDir, toBeCopied[i])
If you are still getting an error, let me know what the value if the above.
Does the target path up to the file name exist? File.Copy() will not create any missing intermediate path, you would need to do this yourself. Use the debugger to see both the source and target paths you are creating and make sure the source exists and the target exists at least up to the parent of the target file.
You do not show where toBeCopied is created. It looks like you are probably running past the end of the values that are set in the click event, and trying to copy a bunch of files with empty names.
You should add this to the beginning of your click event
toBeCopied = new string[listView1.SelectedItems.Count];
Also (as others have noted) instead of
sourceDir + "\\" + toBeCopied[i]
you should use
Path.Combine(sourceDir, toBeCopied[i])
Assuming both sourceDir and targetDir exist (which you can and should check), you might be doubling up a trailing \. When building paths, you should use Path.Combine.
File.Copy(Path.Combine(sourceDir, toBeCopied[i]), Path.Combine(targetDir, toBeCopied[i]), true);
Borrowing from Henk's loop, but I'd add the file & directory checks, since it is the path not found errors that need checking/creating that the OP has the problem with.
for (int i = 0; i < toBeCopied.Length; ++i)
{
string sourceFile = Path.Combine(sourceDir, toBeCopied[i]);
if(File.Exists(sourceFile))
{
string targetFile = Path.Combine(targetDir, toBeCopied[i]);
if(!Directory.Exists(targetDir))
Directory.CreateDirectory(targetDir);
File.Copy(sourceFile, targetFile, true);
}
refreshToolStripMenuItem_Click(sender, e)
}

When to use try/catch blocks?

I've done my reading and understand what a Try/Catch block does and why it's important to use one. But I'm stuck on knowing when/where to use them. Any advice? I'll post a sample of my code below in hopes that someone has some time to make some recommendations for my example.
public AMPFileEntity(string filename)
{
transferFileList tfl = new transferFileList();
_AMPFlag = tfl.isAMPFile(filename);
_requiresPGP = tfl.pgpRequired(filename);
_filename = filename.ToUpper();
_fullSourcePathAndFilename = ConfigurationSettings.AppSettings.Get("sourcePath") + _filename;
_fullDestinationPathAndFilename = ConfigurationSettings.AppSettings.Get("FTPStagePath") + _filename;
_hasBeenPGPdPathAndFilename = ConfigurationSettings.AppSettings.Get("originalsWhichHaveBeenPGPdPath");
}
public int processFile()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(" ");
sb.AppendLine(" --------------------------------");
sb.AppendLine(" Filename: " + _filename);
sb.AppendLine(" AMPFlag: " + _AMPFlag);
sb.AppendLine(" Requires PGP: " + _requiresPGP);
sb.AppendLine(" --------------------------------");
sb.AppendLine(" ");
string str = sb.ToString();
UtilityLogger.LogToFile(str);
if (_AMPFlag)
{
if (_requiresPGP == true)
{
encryptFile();
}
else
{
UtilityLogger.LogToFile("This file does not require encryption. Moving file to FTPStage directory.");
if (File.Exists(_fullDestinationPathAndFilename))
{
UtilityLogger.LogToFile(_fullDestinationPathAndFilename + " alreadyexists. Archiving that file.");
if (File.Exists(_fullDestinationPathAndFilename + "_archive"))
{
UtilityLogger.LogToFile(_fullDestinationPathAndFilename + "_archive already exists. Overwriting it.");
File.Delete(_fullDestinationPathAndFilename + "_archive");
}
File.Move(_fullDestinationPathAndFilename, _fullDestinationPathAndFilename + "_archive");
}
File.Move(_fullSourcePathAndFilename, _fullDestinationPathAndFilename);
}
}
else
{
UtilityLogger.LogToFile("This file is not an AMP transfer file. Skipping this file.");
}
return (0);
}
private int encryptFile()
{
UtilityLogger.LogToFile("This file requires encryption. Starting encryption process.");
// first check for an existing PGPd file in the destination dir. if exists, archive it - otherwise this one won't save. it doesn't overwrite.
string pgpdFilename = _fullDestinationPathAndFilename + ".PGP";
if(File.Exists(pgpdFilename))
{
UtilityLogger.LogToFile(pgpdFilename + " already exists in the FTPStage directory. Archiving that file." );
if(File.Exists(pgpdFilename + "_archive"))
{
UtilityLogger.LogToFile(pgpdFilename + "_archive already exists. Overwriting it.");
File.Delete(pgpdFilename + "_archive");
}
File.Move(pgpdFilename, pgpdFilename + "_archive");
}
Process pProc = new Process();
pProc.StartInfo.FileName = "pgp.exe";
string strParams = #"--encrypt " + _fullSourcePathAndFilename + " --recipient infinata --output " + _fullDestinationPathAndFilename + ".PGP";
UtilityLogger.LogToFile("Encrypting file. Params: " + strParams);
pProc.StartInfo.Arguments = strParams;
pProc.StartInfo.UseShellExecute = false;
pProc.StartInfo.RedirectStandardOutput = true;
pProc.Start();
pProc.WaitForExit();
//now that it's been PGPd, save the orig in 'hasBeenPGPd' dir
UtilityLogger.LogToFile("PGP encryption complete. Moving original unencrypted file to " + _hasBeenPGPdPathAndFilename);
if(File.Exists(_hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd"))
{
UtilityLogger.LogToFile(_hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd already exists. Overwriting it.");
File.Delete(_hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd");
}
File.Move(_fullSourcePathAndFilename, _hasBeenPGPdPathAndFilename + _filename + "original_which_has_been_pgpd");
return (0);
}
}
}
The basic rule of thumb for catching exceptions is to catch exceptions if and only if you have a meaningful way of handling them.
Don't catch an exception if you're only going to log the exception and throw it up the stack. It serves no meaning and clutters code.
Do catch an exception when you are expecting a failure in a specific part of your code, and if you have a fallback for it.
Of course you always have the case of checked exceptions which require you to use try/catch blocks, in which case you have no other choice. Even with a checked exception, make sure you log properly and handle as cleanly as possible.
Like some others have said, you want to use try-catch blocks around code that can throw an Exception AND code that you are prepared to deal with.
Regarding your particular examples, File.Delete can throw a number of exceptions, for example, IOException, UnauthorizedAccessException. What would you want your application to do in those situations? If you try to delete the file but someone somewhere else is using it, you will get an IOException.
try
{
File.Delete(pgpdFilename + "_archive")
}
catch(IOException)
{
UtilityLogger.LogToFile("File is in use, could not overwrite.");
//do something else meaningful to your application
//perhaps save it under a different name or something
}
Also, keep in mind that if this does fail, then the File.Move you do outside of your if block next will also fail (again to an IOException - since the file was not deleted it is still there which will cause the move to fail).
I was taught to use try/catch/finally for any methods/classes where multiple errors could occur and that you can actually handle. Database transactions, FileSystem I/O, streaming, etc. Core logic usually doesn't require try/catch/finally.
The great part about try/catch/finally is that you can have multiple catches so that you can create a series of exception handlers to deal with very specific error or use a general exception to catch whatever errors you don't see coming.
In your case, you're using File.Exists which is good, but their maybe another problem with the disk that may throw another error that File.Exists cannot handle. Yes, it's a boolean method, but say the File is locked and what happens if you try to write to it? With the catch, you can plan for a rare scenario, but without try/catch/finally, you may be exposing the code to completely unforeseen conditions.
The other guys have given quite a number of good pointers and references.
My input is a short one:
When to use it is one thing, equally or more importanly is how to use it properly.
PS: "it" is refeings to "trying-catching exceptions".

File operations

I wanted move the file from one folder to another(target) folder.If the same file is already exist in target folder i wants to rename .how to implement in C#.
Thanks in advance
Sekar
System.IO.File.* has everything you need.
System.IO.File.Exists = To check if the file exists.
System.IO.File.Move = To move (or rename a file).
Fundamentally, this is:
string source = ..., dest = ...; // the full paths
if(File.Exists(dest))
{
File.Move(dest, Path.GetTempFileName());
}
File.Move(source, dest);
You'll want to use the System.IO.File class and check for the file's existence ahead of time.
if(File.Exists("myfile.txt"))
File.Move("myfile.txt", "myfile.bak");
File.Move("myotherfile.txt","myfile.txt");
If you prefer a windows-style behavior, so there is the code I'm using for such an operation
public static void FileMove(string src,ref string dest,bool overwrite)
{
if (!File.Exists(src))
throw new ArgumentException("src");
File.SetAttributes(src,FileAttributes.Normal);
string destinationDir = Path.GetDirectoryName(dest);
if (!Directory.Exists(destinationDir))
{
Directory.CreateDirectory(destinationDir);
}
try
{
File.Move(src,dest);
}
catch (IOException)
{
//error # 183 - file already exists
if (Marshal.GetLastWin32Error() != 183)
throw;
if (overwrite)
{
File.SetAttributes(dest,FileAttributes.Normal);
File.Delete(dest);
File.Move(src,dest);
}
else
{
string name = Path.GetFileNameWithoutExtension(dest);
string ext = Path.GetExtension(dest);
int i = 0;
do
{
dest = Path.Combine(destinationDir,name
+ ((int)i++).ToString("_Copy(#);_Copy(#);_Copy")
+ ext);
}
while (File.Exists(dest));
File.Move(src,dest);
}
}
}

Categories

Resources