C# File Caching - c#

I've got a class with a method "GetNewsFeed", that when a page is requested:
Check to see if a file exists & it is less than 30 minutes old
If it does exist, read contents of the file, push contents onto page
If it does not exist, go to a URL and write the contents of that page to a .txt file, push contents onto page
I am not very well versed with C#, so I'm trying to cobble together a few sources. I believe I am close, but I'm unable to get the files to refresh every 30 minutes if needed (I'm not getting any compliation errors or anything). Any help would be appreciated.
public static string GetNewsFeed(string url, string fileName)
{
// Set the path to the cache file
String filePath = HttpContext.Current.Server.MapPath("/cachefeed/" + fileName + ".txt");
string fileContents = "";
// If the file exists & is less than 30 minutes old, read from the file.
if (File.Exists(filePath) && (File.GetLastWriteTime(filePath) > DateTime.Now.AddMinutes(-30)))
{
fileContents = File.ReadAllText(filePath);
}
else
{
try
{
// If the file is older than 30 minutes, go out and download a fresh copy
using (var client = new WebClient())
{
// Delete and write the file again
fileContents = client.DownloadString(url);
File.Delete(filePath);
File.WriteAllText(filePath, fileContents);
}
}
catch (Exception)
{
if (File.Exists(filePath))
{
fileContents = File.ReadAllText(filePath);
}
}
}
return fileContents;
}
Finally, I've got some code elsewhere that will read these text files and manipulate their contents onto the page. I don't have any issues with this.

Odds are, you're catching an exception in the else block and it's only returning the fileContents. Try putting a breakpoint in the exception block to see what is going on.
You'll need to change it to:
catch( Exception e )
in order to get this information.
Also, you don't need this:
File.Delete(filePath);
The WriteAllText method will overwrite the file that is already there. Try removing that line and check your directory permissions.
You may also want to change
(File.GetLastWriteTime(filePath) > DateTime.Now.AddMinutes(-30)))
to
(DateTime.Now - File.GetLastWriteTime(filePath)).TotalMinutes > 30

I added a throw to my catch and believe it or not, one of the URL's I was passing into my method was invalid. So yes, the culprit in my code was the catch statement.
I fixed this and all is working properly.
Thanks for the tips everyone.

Related

How to delete the last character of a file with C#

Hello I'm beginner with C# and I want to delete the last character of my file to inject JSON objects to this file manually (I know that's not the best way to do that), so I can get the right format I tried with multiple ways like open the file, manipulating the string (deleting the last character) and when I try to replace the text in that same file I have errors like IOException: The process cannot access the file 'file path' because it is being used by another process or System.UnauthorizedAccessException : 'Access to the path 'C:\Users\ASUS\Desktop\Root' is denied.
I'll show you the code :
StoreLogs Log = new StoreLogs()
{
Id = ID,
DateTime = dateT,
TaskName = task,
SrcAddress = srcPath,
DstAddress = path,
FileSize = DirSize(new DirectoryInfo(srcPath)),
DelayTransfer = ts.Milliseconds,
};
// Record JSON data in the variable
string strResultJson = JsonConvert.SerializeObject(Log);
// Show the JSON Data
// Console.WriteLine(strResultJson);
// Write JSON Data in another file
string MyJSON = null;
string strPath = #"C:\Users\ASUS\Desktop\Backup\logs\log.json";
if (File.Exists(strPath))
{
//FileInfo table = new FileInfo(strPath);
//string strTable = table.OpenText().ReadToEnd();
//string erase = strTable.Remove(strTable.LastIndexOf(']'));
//Console.WriteLine(erase);
//StreamReader r1 = new StreamReader(strPath);
//string strTable = r1.OpenText().ReadToEnd();
//string erase = strTable.Remove(strTable.LastIndexOf(']'));
//r1.Close();
using (StreamReader sr = File.OpenText(strPath))
{
string table = sr.ReadToEnd();
string erase = table.Remove(table.LastIndexOf(']'));
sr.Close();
File.WriteAllText(strPath, erase);
}
//MyJSON = "," + strResultJson;
//File.AppendAllText(strPath, MyJSON + "]");
//Console.WriteLine("The file exists.");
}
else if (!File.Exists(strPath))
{
MyJSON = "[" + strResultJson + "]";
File.WriteAllText(strPath, MyJSON);
Console.WriteLine("The file doesn't exists.");
}
else
{
Console.WriteLine("Error");
}
// End
Console.WriteLine("JSON Object generated !");
Console.ReadLine();
And that's the result I want :
[{"Id":"8484","DateTime":"26 novembre 2019 02:33:35 ","TaskName":"dezuhduzhd","SrcAddress":"C:\\Users\\ASUS\\Desktop\\Root","DstAddress":"C:\\Users\\ASUS\\Desktop\\Backup","FileSize":7997832.0,"DelayTransfer":0.0},{"Id":"8484","DateTime":"26 novembre 2019 02:33:35 ","TaskName":"dezuhduzhd","SrcAddress":"C:\\Users\\ASUS\\Desktop\\Root","DstAddress":"C:\\Users\\ASUS\\Desktop\\Backup","FileSize":7997832.0,"DelayTransfer":0.0},{"Id":"8484","DateTime":"26 novembre 2019 02:33:35 ","TaskName":"dezuhduzhd","SrcAddress":"C:\\Users\\ASUS\\Desktop\\Root","DstAddress":"C:\\Users\\ASUS\\Desktop\\Backup","FileSize":7997832.0,"DelayTransfer":0.0}]
Edit :
Thank you all for your advices
Solution:
FileStream fs = new FileStream(strPath, FileMode.Open, FileAccess.ReadWrite);
fs.SetLength(fs.Length - 1);
fs.Close();
In the code example you have posted you are opening a stream to read the file. A using block will dispose the stream after you exit the block. You are trying to write to the file, while the read stream is still accessing it (the read stream still exists). You've basically opened the file, you read from it, and are trying to write back to it while still holding it open. The reason this is a problem is that you are not using the stream to write. So your second, write, process is unable to access the file. I see you are closing the stream prior to write, but I'm willing to bet it's still holding the reference open.
I would try this method:
How to both read and write a file in C#
what it says is the access to the path (C:\Users\ASUS\Desktop\Root) denied for the user who is running the application. for ex: If you are running from Visual studio on user1 windows login then user1 should have appropriate rights to that root folder. If the code is running by itself (exe) then check the access for that user who is invoking that exe.
Based on the errors you posted seems that:
Maybe you're leaving some stream open pointing to the file you want to edit, use the 'using' statement to avoid this (see this link for more info)
You're trying to access a file when you don't have needed permissions (you aren't a system admin or file is read-only), try changing file ubication or setting it to be writeable (see this link for mor info about the UnauthorizedAccessException exception)
Hope this helps you!

How can I write to a file and remove the file afterwards in C#?

I need to create a file, write one line of text in the file and then delete the file and estimate how long it will take to do it.
Unfortunately, I am running in couple of problems, first I cannot write in the file, it succesfully creates it but nothing is written to it.
Secondly, I cannot delete the file because it has been used by another process.
Please help.
I have been trying to delete it for quite some time.
I have also tried wrapping it in usings, to no avail.
Writing to the file is the same situation. I even changed it so the file ends in .txt but that does not make any difference.
public static void ProcessFile(string path)
{
string fullpath = path;
int lastBracket = path.LastIndexOf("\\");
// the filename does not contain .html so it can be named to .txt
string newFileName = path.Substring(lastBracket + 1, path.Length - lastBracket - 6) + " hrefcount.txt";
string newPath = Path.Combine(fullpath.Substring(0, lastBracket), newFileName);
Console.WriteLine(newPath);
int counter = 0;
foreach (var line in File.ReadAllLines(path))
{
if (line.Contains("href="))
{
counter++;
}
}
var fileCreated = File.CreateText(newPath);
fileCreated.WriteLine("The number of times href appears is " + counter);
Console.WriteLine();
File.Delete(newPath);
}
File created, nothing written to it, unable to delete due to has been used by another process.
Instead of File.CreateText() use File.WriteAllText(path, content). It writes the text and then closes the file allowing you to delete it if necessary
Instead of the following
var fileCreated = File.CreateText(newPath);
fileCreated.WriteLine("The number of times href appears is " + counter);
You may write
File.WriteAllText(newPath, $"The number of times href appears is {counter}");
Refer documentation here
The issue with your approach is that CreateText() is used to write to a stream. But in your case, it is not necessary since you're writing all the text at once to the file and that text is small in size.
The cause of your error is the fact that you don't close and dispose the variable fileCreated. This, is a FileStream and until you close and dispose this variable the file is not available to anyone, even your own code that has opened the file.
So the first thing to do is
using (var fileCreated = File.CreateText(newPath))
{
fileCreated.WriteLine("The number of times href appears is " + counter);
}
The using block ensure the proper disposal of the variable.
However there are other parts of your code that you can simplify
public static void ProcessFile(string path)
{
string folder = Path.GetDirectoryName(path);
string file = Path.GetFileName(path);
// Keep the first 6 characters from the source file?
string newFile = file.Substring(0, 6) + " hrefcount.txt";
string newPath = Path.Combine(folder, newFile);
// A single line to retrieve your counter thanks to IEnumerables and Linq
int counter = File.ReadLines(path).Count(x => x.Contains("href="));
// Create, but dispose also the file
using (var fileCreated = File.CreateText(newPath))
{
fileCreated.WriteLine("The number of times href appears is " + counter);
}
// Now you should be free to delete the file
File.Delete(newPath);
}
I cannot delete the file because it has been used by another process.
Probably you're not disposed your files after creating. To do that, you should additionally use FileStream.Dispose method:
File.Create(path).Dispose();
estimate how long it will take to do it
You can use System.Diagnostics.Stopwatch class to do that:
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
/*
do the magic
*/
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
You can use File.WriteAllText method instead of File.CreateText to write your text to file:
File.WriteAllText(path, myText);
Remember that since the .NET 4 you can use this method with array or List<T> too instead of string.
File.Create() supports Dispose method which help you to release that file resource to perform further operations
To perform operations on file follow below steps:
Create file and free up the resource using Dispose().
File.Create(newPath).Dispose();
or Use StreamWriter to create file and write text to it.
using( StreamWriter sw = new StreamWriter(newPath, true)
{
sw.Write($"The number of times href appears is {counter}"); //Used string interpolation
}
StreamWriter calls Dispose() function to release file resource.
When Writer release control over file, then you will not face issue related to I cannot delete the file because it has been used by another process.
Now you can delete file using,
File.Delete(newPath);
MSDN : IDisposable.Dispose Method
Performs application-defined tasks associated with freeing, releasing,
or resetting unmanaged resources.

An unhandled exception of type 'System.OutOfMemoryException' occurred in mscorlib.dll in LINQ Search

Using this article from MSDN, I'm trying to search through files in a directory. The problem is, every time I execute the program, I get:
"An unhandled exception of type 'System.OutOfMemoryException' occurred in mscorlib.dll".
I have tried to some other options like StreamReader, but I can't get it to work. These files are HUGE. Some of them range in upwards to 1.5-2GB each and there could be 5 or more files per day.
This code fails:
private static string GetFileText(string name)
{
var fileContents = string.Empty;
// If the file has been deleted since we took
// the snapshot, ignore it and return the empty string.
if (File.Exists(name))
{
fileContents = File.ReadAllText(name);
}
return fileContents;
}
Any ideas what could be happening or how to make it read without memory errors?
Entire code (in case you don't want to open the MSDN article)
class QueryContents {
public static void Main()
{
// Modify this path as necessary.
string startFolder = #"c:\program files\Microsoft Visual Studio 9.0\";
// Take a snapshot of the file system.
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(startFolder);
// This method assumes that the application has discovery permissions
// for all folders under the specified path.
IEnumerable<System.IO.FileInfo> fileList = dir.GetFiles("*.*", System.IO.SearchOption.AllDirectories);
string searchTerm = #"Visual Studio";
// Search the contents of each file.
// A regular expression created with the RegEx class
// could be used instead of the Contains method.
// queryMatchingFiles is an IEnumerable<string>.
var queryMatchingFiles =
from file in fileList
where file.Extension == ".htm"
let fileText = GetFileText(file.FullName)
where fileText.Contains(searchTerm)
select file.FullName;
// Execute the query.
Console.WriteLine("The term \"{0}\" was found in:", searchTerm);
foreach (string filename in queryMatchingFiles)
{
Console.WriteLine(filename);
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
// Read the contents of the file.
static string GetFileText(string name)
{
string fileContents = String.Empty;
// If the file has been deleted since we took
// the snapshot, ignore it and return the empty string.
if (System.IO.File.Exists(name))
{
fileContents = System.IO.File.ReadAllText(name);
}
return fileContents;
}
}
The problem you're having is based on trying to load multiple gigabytes of text at the same time. If they're text files, you can stream them and just compare one line at a time.
var queryMatchingFiles =
from file in fileList
where file.Extension == ".htm"
let fileLines = File.ReadLines(file.FullName) // lazy IEnumerable<string>
where fileLines.Any(line => line.Contains(searchTerm))
select file.FullName;
I would suggest that you are getting an out of memory error because the way the query is written I believe that you will need to load the entire text of every file into memory and none of the objects can be released until the entire file set has been loaded. Could you not check for the search term in the GetFileText function and then just return a true or false?
If you did that the file text at least falls out of scope at the end of the function and the GC can recover the memory. It would actually be better to rewrite as a streaming function if you are dealing with large files/amounts then you could exit your reading early if you come across the search term and you wouldn't need the entire file in memory all the time.
Previous question on finding a term in an HTML file using a stream

How to get "date modified" file properties to be copied line by line onto text file?

I am working on a code that copies information line by line from one text file and pastes it onto another. Each line contains "|" and after that symbol the timestamp of the date modified of each line is displayed. I am having trouble with finding a way that will allow me to access the date modified property from a build server when I run my utility and replaces the old date modified in the old text file with the new date modified property in the new text file. Here is what I have so far:
class Program
{
class NewTime
{
public DateTime Current { get; set; }
}
static void Main(string[] args)
{
int counter = 0;
string line;
// Read the file and display it line by line.
System.IO.StreamReader file = new System.IO.StreamReader(args[0]);
System.IO.StreamWriter filewriter = new System.IO.StreamWriter(args[1], false);
while ((line = file.ReadLine()) != null)
{
Thread.Sleep(10);
string [] pieces = line.Split(new char[] { '|' });
if(pieces.Length == 2)
{
*DateTime outDate;
if(DateTime.TryParse(pieces[1], out outDate))
{
string outputstring = string.Format(" {0:yyyy-MM-dd-hh-mm-ss-ff-tt}", DateTime.Now);
filewriter.WriteLine(pieces[0] + "|" + outputstring);
}*
else
filewriter.WriteLine(line);
}
else
filewriter.WriteLine(line);
System.Console.WriteLine(line);
counter++;
}
file.Close();
filewriter.Close();
System.Console.ReadLine();
}
}
The portion in between the stars was my first attempt, but that didn't give me what I want. It simply replaced the old time with the current time of when I ran the utility on my computer.
Any help is appreciated =)
If I'm understanding correctly, its looks like a timing problem: You want to replace the old date modified in the old text file with the new date modified property in the new text file. But DateTime.Now is not close enough, and you can't get the filesystem-generated DateModified (actually LastWriteTime) until the file has been saved on the file system.
If so, since you have to flush and close the new file for the file system to write the LastModified value, even re-reading the LastAccessTime on the newly created file FileInfo may not give you the value you are after.
It's a little messy because NTFS updates to the last write access time for a file can take an indeterminate amount of time to resolve after the last access. Even FAT systems have a write time resolution of ~2 seconds, according to Windows SDK
[http://msdn.microsoft.com/en-us/library/windows/desktop/ms724933(v=vs.85).aspx]
So, if replacing the old time with the current time (as you are doing) is not close enough, you would need to complete your initial file-creation loop, then derive a FileSystemInfo object on the file and call its Refresh method to get the latest value, and then re-write the value (LastWriteTime or LastAccessTime) into the file.
Refer to .Net Framework documentation for FileSystemInfo.LastAccessTime property for details: http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.lastaccesstime(v=vs.110).aspx

The process cannot access the file because error while trying to write to a file

I am getting this error when i am trying to write to a file in this manner:
try
{
if (!File.Exists(path))
File.CreateText(file.ToString());
sw = flag == 1 ? File.CreateText(file.ToString()) : File.AppendText(file.ToString()); //Exception here
sw.WriteLine(textToWrite);
status = true;
}
But before to this file i made a delete attempt to it also in another function in this way:
try
{
File.Delete(path);
status = true;
}
Exception:
{"The process cannot access the file ... because it is being used by another process."}
Now as it seems file is still either taken by delete process of by the File.CreateText function, how can i make them release the file so i can start writing to it?
File.CreateText will return a streamwriter which has a file stream still opened. You should be using that. That is the reason why you get exception in successive calls to open the file.
Try this.
using(var sw = File.CreateText(...))
{
//Do whatever
}
If you want to delete the file before writing a new file, try this:
File.WriteAllText(path, textToWrite);
If you want to append to a file (or create), try this:
File.AppendAllText(path, textToWrite);
Both of these methods close the file after writing.
CreateText returns StremWriter which you miss it. That's why it remains opened. You can easily close it:
StreamReader sr = File.OpenText(path);
sr.WriteLine(textToWrite);
sr.Close();

Categories

Resources