I want to check if a list of files is in use or not writable before I start replacing files.
Sure I know that the time from the file-check and the file-copy there is a chance that one or more files is gonna to be locked by someone else but i handle those exceptions. I want to run this test before file copy because the complete list of files have a better chance to succeed than if a file in the middle of the operation fails to be replaced.
Have any of you an example or a hint in the right direction
There is no guarantee that the list you get, at any point of time, is going to stay the same the next second as somebody else might take control of the file by the time you come back to them.
I see one way though - "LOCK" the files that you want to replace by getting their corresponding FileStream objects. This way you are sure that you have locked all "available" files by opening them and then you can replace them the way you want.
public void TestGivenFiles(List<string> listFiles)
{
List<FileStream> replaceAbleFileStreams = GetFileStreams(listFiles);
Console.WriteLine("files Received = " + replaceAbleFileStreams.Count);
foreach (FileStream fileStream in replaceAbleFileStreams)
{
// Replace the files the way you want to.
fileStream.Close();
}
}
public List<FileStream> GetFileStreams(List<string> listFilesToReplace)
{
List<FileStream> replaceableFiles = new List<FileStream>();
foreach (string sFileLocation in listFilesToReplace)
{
FileAttributes fileAttributes = File.GetAttributes(sFileLocation);
if ((fileAttributes & FileAttributes.ReadOnly) != FileAttributes.ReadOnly)
{ // Make sure that the file is NOT read-only
try
{
FileStream currentWriteableFile = File.OpenWrite(sFileLocation);
replaceableFiles.Add(currentWriteableFile);
}
catch
{
Console.WriteLine("Could not get Stream for '" + sFileLocation+ "'. Possibly in use");
}
}
}
return replaceableFiles;
}
That said, you are better off trying to replace them one by one and and ignore the ones that you can't.
You must open each file for writing in order to test this.
Double
How to check For File Lock in C# ?
Can I simply ‘read’ a file that is in use?
Read one byte, write same byte?
Related
I have a requirement where
when uploading the files to the pick-up folder, files will be uploaded
with a .tmp (or)._ (or) .filepart extensions and after successful
upload files will be renamed to the original file name.
This is required to avoid any partial pick-up of .xml files by settings on SFTP folder side.
For eg. Upload with .xml.tmp and after successful upload, rename the files to .xml
Any idea on how to achieve this in MVC, C#.
I prefer to do this in a separate folder entirely. And then do a move to the pickup folder.
Then renaming is not required.
private bool IsFileLocked()
{
try
{
FileStream fs = File.OpenWrite(FilePath);
fs.Close();
return false;
}
catch (Exception)
{
Console.WriteLine("File locked: " + FileName);
return true;
}
}
To check if the file is locked prior to attempting to send, might also work, or in conjunction.
I was talking about generating a local file first, once its completely done being written, simply use the File.Move() method, so you move the newly generated file from its "safe" folder, into the pickup folder, that the SFTP is continually looking for files in.
If it is picking up a file you are receiving, then it's just the check prior to attempting to do anything with it.
First of all, once you receive the file stream from the post, the upload is "already" successful (most likely). Therefore, the moment you have the data from the post, you should already be good to write it. The only point I can remotely see here is that, the remote process either checks .xml files constantly so let's say if the .xml file is quite large, and let's assume (which wont be the case) it takes a while for you to write the stream to the remote destination, they do not want to check just part of the xml, they need all of it. If that is the case, something like the following should work (modify it for your needs);
[HttpPost]
public ActionResult Upload()
{
if (Request.Files.Count < 1)
{
ViewBag.Result = "No files were provided";
return PartialView("Error");
}
foreach (string F in Request.Files)
{
var FInfo = Request.Files[F];
var TemporaryFileName = $"{FInfo.FileName}.tmp";
try
{
using (var FStream = new FileStream(TemporaryFileName, FileMode.Create, FileAccess.Write))
{
FInfo.InputStream.CopyTo(FStream);
}
}
catch (Exception e)
{
ViewBag.Result = e.Message;
return PartialView("Error");
}
finally
{
System.IO.File.Move(TemporaryFileName, $"{FInfo.FileName}");
}
}
ViewBag.Result = "Files have been uploaded";
return View();
}
I have written a small program to monitor a folder. I have not used the folderWatcher in .NET because multiple files can be added to the folder simultaneously and folderWatcher can sometimes miss them. Basically, I am checking the folder for a file that starts and ends with certain characters. The timer is checking the folder every 10 seconds.
My issue: my program will read the same file multiple times. I need to set a condition or have a listener check if the file has already been read to ignore it.
The problem is I'm not sure how to go about this, can anyone shed some light on the best way to maybe implement a listener that will check if I have read this file and ignore it and move onto the next file, please?
I couldn't find another thread on here that helped me with the answer I am looking for.
Basicly, you sould keep somewhere (maybe in a text file) the last scan date and can check the creation time of files.
void ReadFiles()
{
try
{
DateTime lastScanDate = GetLastScanDate();
SetLastScanDate();
foreach (string filePath in System.IO.Directory.GetFiles(#"C:\Users\cozogul\Desktop\test"))
{
System.IO.FileInfo fi = new System.IO.FileInfo(filePath);
if (fi.CreationTime >= lastScanDate)
{
//read file
}
else
{
//Don't read file.
}
}
}
catch (Exception ex)
{
//Log your error
}
}
DateTime GetLastScanDate()
{
if (System.IO.File.Exists(#"C:\Users\cozogul\Desktop\test\datefile.txt"))
{
using (System.IO.FileStream fs = new System.IO.FileStream(#"C:\Users\cozogul\Desktop\test\datefile.txt", System.IO.FileMode.Open))
{
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
string dateString = System.Text.Encoding.UTF8.GetString(buffer);
DateTime date = Convert.ToDateTime(dateString);
return date;
}
}
//Will read all files.
else
return DateTime.MinValue;
}
public void SetLastScanDate()
{
using (System.IO.FileStream fs = new System.IO.FileStream(#"C:\Users\cozogul\Desktop\test\datefile.txt", System.IO.FileMode.Create))
{
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(DateTime.Now.ToString());
fs.Write(buffer, 0, buffer.Length);
}
}
Or you can keep filenames in a file or a database table to check a file is before read.
Thanks Coskun
That is a good option and it does work.
For simplicity i went a different route, I add the file names to a list then check the list pre-execution, if the list does not contain that file name it executes. I go on to count the file names in the list and output the total on each pass to a text field that tell me how many files i ignored.
I've got a problem that has apparently been encountered before, though the solutions then don't help with what I'm seeing.
I'm trying to write data to a file, based on a base 64 encoding of the contents of a Dictionary<string, object>. It's supposed to go like this: if the file doesn't exist,
It gets created
Some default values get added to the Dictionary
The Dictionary is turned into base 64 data
The data is written to the file
Steps 1 through 3 are working fine. Step 4 appears to be working fine, until you I open the file - when it's revealed that there's nothing in it. It gets created but not written to, and I end up with an empty file.
The code goes like this, where the top method (CreateDefaultConfigFile is called if the file doesn't exist):
private static void CreateDefaultConfigFile()
{
Console.WriteLine("AppConfig.CreateDefaultConfigFile");
File.Create(CONFIG_FILE_PATH);
Configuration.Clear();
Configuration.Add("ClientId", "5577");
Configuration.Add("RedirectUri", "https://stackexchange.com/oauth/login_success");
Configuration.Add("RequestKey", "2WQ5ksCzcYLeeYJ0qM4kHw((");
Save();
}
public static void Save()
{
Console.WriteLine("AppConfig.Save");
string data = "";
foreach (KeyValuePair<string, object> pair in Configuration)
{
Console.WriteLine(" {0}: {1}", pair.Key, pair.Value.ToString());
if (pair.Value.GetType() == typeof(string))
{
data += pair.Key + SC_SPLITTER + pair.Value + "\n";
}
else if (pair.Value.GetType() == typeof(Array))
{
data += pair.Key + SC_SPLITTER + "[" + string.Join(",", pair.Value) + "]\n";
}
else
{
Configuration.Remove(pair.Key);
}
}
if (data.EndsWith("\n"))
{
data.Remove(data.Length - 2);
}
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
string encoded = Convert.ToBase64String(dataBytes);
File.WriteAllText(CONFIG_FILE_PATH, encoded);
Console.WriteLine(" Written to file.");
}
Important fact to note: the "Written to file." message never gets logged to the console (though if I put a log directly before the File.WriteAllLines call, it does log). A breakpoint at the final Console.Log call never raises.
No exceptions are thrown, and it's not because data or encoded are empty - logging them just before the write reveals data in both.
CONFIG_FILE_PATH is a constant value of C:\Users\Me\Documents\longpath\bin\Debug\config\general.cgf.
I've also tried using a FileStream and FileStream.Flush(), as suggested in the question I linked at the top - this doesn't work.
The File.Create method doesn't do what you appear to think that it does.
It does create a file, but it also leaves the file open and returns a FileStream object to handle the open file.
If you just call Create and ignore the returned FileStream object, then the file will be left open until the object is disposed by the garbage collector.
Depending on when the garbage collection runs, the File.WriteAllText call will either be able to write to the file, or you will get an IOException. The fact that you don't see anything written to the file, and that you don't see the text that is written out after the call, suggests that you actually get an exception but catch that at some other level and ignore it.
If you want to use File.Create to create the file, you should get the FileStream object and dispose it to close the file:
using (FileStream stream = File.Create(CONFIG_FILE_PATH)) {
}
However, you don't have to create the file before calling File.WriteAllText, it will create the file if it doesn't exist.
I am working on this project still and I am running into a problem. Well here is what I need to do.
When the user clicks the “Save” button, write the selected record to
the file specified in txtFilePath (absolute path not relative) without
truncating the values currently inside and handle any exceptions that arise.
Ok here is my code:
private void Save_Click(object sender, EventArgs e)
{
string filePath = txtFilePath.Text;
if (!File.Exists(filePath))
{
FileStream fs = File.Create(filePath);
fs.Close();
}
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
using (StreamWriter sw = new StreamWriter(fs))
{
foreach (string line in employeeList.Items)
{
sw.WriteLine(line);
}
}
}
}
Now when I go onto my program and want to save something from the employeelist.text that its not being saved to the place I am saving it at. I don;t know if I am missing something in my code or what but it will not save. Here is an example:
I add a person name to this list in employeelist and in the textbox I
have a file called C:\employess\employeelist.txt I want to save it to.
I click the save button then I go to that employeelist and it is not
being saved.
I don't know what I am doing wrong I have been looking online for a solution but I haven't found anything yet. Thanks
Some things to double-check:
Make sure you don't have the employeelist.txt file open when you're testing
Make sure you don't have invalid characters in your file name
Make sure your application has permission to save the file to the location you specified
Use the debugger to step-through your code and look for swallowed exceptions -- there must be a reason the file is not created.
Check that your Save_Click event is wired up to your button -- is the code in your example even running?
Once you check those things, you may want to follow this example for the create vs. append requirement of your project:
string path = txtFilePath.Text;
// This text is added only once to the file.
if (!File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
foreach (var line in employeeList.Items)
sw.WriteLine(line.ToString());
}
}
else
{
using (StreamWriter sw = File.AppendText(path))
{
foreach (var line in employeeList.Items)
sw.WriteLine(line.ToString());
}
}
This will create the file if it doesn't exist, or append to it if it does.
Checking that the file exists and then creating it is a bit unnecessary as this can all be handled by the StreamWriter/FileStream parts. So your above function can be simplified into the following:
public void Save_Click(object sender, EventArgs e)
{
StreamWriter file =
new StreamWriter(txtFilePath.Text, true);//Open and append
foreach (object item in employeeList.Items) {
file.WriteLine(item.toString());
}
file.Close();
}
[Updated]
What are the types of txtFilePath and employeeList the former suggests it's a text box, the later suggests it's bound to a non-GUI element perhaps? (WAG)
You might also want to append a blank line at the end so that on further saves you can tell it was an append rather than one long list (depending on your needs of course)
Starting with .Net Framework 4 you can do it like this:
private void Save_Click(object sender, EventArgs e)
{
File.AppendAllLines(txtFilePath.Text, employeeList.Items);
}
Of course, you probably would want to add a check to have a valid path and a valid enumeration of strings.
If the path looks like a relative one (i.e. doesn't begin with a drive letter), then it will be interpreted that way.
If you put a full path in the text box, does the file get saved in the proper place? If so, perhaps this is what was intended.
If the user doesn't put in a full path, do you have a way to make it one (for example, just sticking C:\ at the beginning)? Or at least can you tell when there isn't a full path, and reject the request?
Why would the code below throw a io.system.directorynotfound exception? I can't recreate the problem myself but another user of my code does see it, any ideas why?
Thanks
try
{
//create path
string strAppData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData).ToString() + "\\MyApp\\Data\\logs";
//check path exists
if (!System.IO.File.Exists(strAppData))
{
System.IO.Directory.CreateDirectory(strAppData);
}
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(strAppData);
int count = dir.GetFiles().Length;
if (count > 100)
{
string[] files = System.IO.Directory.GetFiles(strAppData);
foreach (string file in files)
{
System.IO.File.Delete(file);
}
}
this.fileName = fileName;
// delete the file if it exists
if (File.Exists(fileName))
{
//delete the file
File.Delete(fileName);
}
// write the data to the file
fs = File.OpenWrite(fileName);
sWriter = new StreamWriter(fs);
sWriter.WriteLine(headerText);
sWriter.Flush();
sWriter.Close();
}
catch (Exception exp)
{
throw new Exception(exp.Message);
}
Have you tried using System.IO.Directory.Exists rather than System.IO.File.Exists when checking to see if the path exists?
You're checking for the existence of a directory using System.IO.File rather than System.IO.Directory. It probably works on your machine because that directory already exists, and so the check doesn't matter.
Either way, you need to remember that the file system is volatile. Rather than checking existence, try to open the resource and handle the exception when it fails.
Check that the directory exists, not the file...
Although you're checking it, and creating it if it doesn't exist. You don't know if they have privelages to create the directory. So your Directory.CreateDirectory call may well be failing too and then sub-sequently the rest of the code will fail
http://msdn.microsoft.com/en-us/library/system.io.file.exists.aspx
"Remarks
The Exists method should not be used for path validation, this method merely checks if the file specified in path exists. Passing an invalid path to Existsl returns false. "
That is your error right there. Your validation does not ensure that the path to the file exists