I have a .json file who handles the user's roles and I have wrote a Repository who's responsible of adding/removing roles to users. The pb is that when I modify the file I want to be sure that no one access it except me.
Here's (roughly) the code I use:
using (var fileStream = new FileStream(_rolesFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
using (var streamReader = new StreamReader(fileStream))
using (var streamWriter = new StreamWriter(fileStream))
{
var oldContent = streamReader.ReadToEnd();
var contentObject = Deserialize(oldContent);
Modify(contentObject)
var newContent = Serialize(contentObject);
fileStream.Seek(0, SeekOrigin.Begin);
streamWriter.Write(newContent);
}
The pb with this solution is that if newContent is a string shorter that oldContent some characters will be remaining in the file.
A solution I found is to add the following code:
using (var fileStream = new FileStream(_rolesFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
using (var streamReader = new StreamReader(fileStream))
using (var streamWriter = new StreamWriter(fileStream))
{
//...
var newContent = Serialize(contentObject);
var endPosition = fileStream.Position;
fileStream.Seek(0, SeekOrigin.Begin);
streamWriter.Write(newContent);
streamWriter.Flush();
while (fileStream.Position < endPosition)
{
streamWriter.WriteLine();
streamWriter.Flush();
}
}
It works well but does not look very clean to me. Are there any better solution who ensure that I keep the control of the file ?
Thanks in advance,
Thomas
You can do fileStream.SetLength(fileStream.Position) to truncate the remaining part of the file. This assumes that the FileStream is left correctly positioned by the StreamWriter after use, but that's an assumption your current code seems to be making too.
(This is a safer assumption than the corresponding usage of StreamReader where internal buffering may mean that the underlying stream's position is further advanced than the latest data returned by a call to a Read method)
Related
I have created the following code
var filename = "wwwroot/Counter/Counter.txt";
var counterStream = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
var reader = new StreamReader(counterStream);
var visits = Convert.ToInt32(reader.ReadLine());
visits = visits + 1;
var writer = new StreamWriter(counterStream);
writer.Write(visits);
counterStream.Dispose();
As you will realise, it is a hit counter for a website I am building. I am OK down to the line.
visits = visits + 1
Counter.txt is a file that just contains the one number and the above code has successfully read and updated it. However, the last three lines are not writing anything back to the file. I was half expecting it to write a new line in the file, although obviously I want it to replace the original. I am at a loss as to why it hasn't written anything. Could someone point me in the right direction, please.
Tip, you can use
using(FileStream counterStream=new FileStream())
{
}
Really handy as it will automatically dispose and clean up.
Here is an example that works, though it writes binary
using (FileStream counterStream = new FileStream("counter.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
uint counterVal = 0;
byte[] buffer= new byte[4];
if(counterStream.Read(buffer,0,buffer.Length)>0)
{
counterVal = BitConverter.ToUInt32(buffer, 0);
}
// Increment counter
counterVal++;
buffer = BitConverter.GetBytes(counterVal);
// Reset position (prevent writing a new line)
counterStream.Position = 0;
counterStream.Write(buffer, 0, buffer.Length);
}
But like Yeldar says, this will not work well with webservers due to collisions.
Also I think that your FileShare.None will prevent other users from opening the file, thus missing hits.
You could put it into a database instead, this will make it easier.
I have solved my problem with the following code
const string filename = "wwwroot/Counter/RLSBC.txt";
using (var counterStream = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
int visits;
using (var reader = new StreamReader(counterStream, System.Text.Encoding.UTF8, true, 4096, true))
{
string line = reader.ReadLine();
int.TryParse(line, out visits);
}
visits = visits + 1;
counterStream.Seek(0L, SeekOrigin.Begin);
using (var writer = new StreamWriter(counterStream))
{
writer.Write(visits);
}
}
I'm using a FileStream to lock the File to be not writeable for other processes and also read and write to it, I'm using following method for it:
public static void ChangeOrAddLine(string newLine, string oldLine = "")
{
string filePath = "C:\\test.txt";
FileMode fm = FileMode.Create;
//FileMode fm = FileMode.OpenOrCreate;
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))
using (StreamReader sr = new StreamReader(fs))
using (StreamWriter sw = new StreamWriter(fs))
{
List<string> lines = sr.ReadToEnd().Split(new string[] { "\r\n" }, StringSplitOptions.None).ToList();
bool lineFound = false;
if (oldLine != "")
for (int i = 0; i < lines.Count; i++)
if (lines[i] == oldLine)
{
lines[i] = newLine;
lineFound = true;
break;
}
if (!lineFound)
lines.Add(newLine);
sw.Write(string.Join("\r\n", lines));
}
}
I want to overwrite it with the new content but i don't find the right FileMode, using FileMode.OpenOrCreate just appends the new content to the old and FileMode.Create deletes the file-content at the time, the FileStream fm has been initialized, so the file is empty.
I need to just clear the old content at the moment, when i write the new content to it without losing the write-lock on it during the method is running.
OpenOrCreate just appends ...
Because you don't reposition after the reading.
That also shows the main problem with your approach: The FileStream only has one Position, and the Reader and the Writer heavily use caching.
However, as long as you want to replace everything and really need that locking scheme:
using (FileStream fs = new FileStream(filePath,
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read))
{
using (StreamReader sr = new StreamReader(fs))
{
... // all the reading
}
fs.Position = 0;
using (StreamWriter sw = new StreamWriter(fs))
{
sw.Write(string.Join("\r\n", lines));
}
fs.SetLength(fs.Position); // untested, something along this line
}
and maybe you have to convince the sw and sr to leave their stream open.
But I have to note that the FileShare.Read flag doesn't make too much sense in this scenario. A reader could see al sorts of inconsistent data, including torn lines and broken UTF8 characters.
I have a C# app that tries to read a log file which is being written to by another app. When I try to read the file, I get IOException
"The process cannot access the file ... because it is being used by
another process."
What I tried using so far are the following, but none of them fix the problem
var log = File.ReadAllText(logPath);
var stream = new FileStream(logPath, FileMode.Open);
using (var stream = File.Open(logPath, FileMode.Open))
{
}
try this:
FileStream logFileStream = new FileStream("c:\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader logFileReader = new StreamReader(logFileStream);
while (!logFileReader.EndOfStream)
{
string line = logFileReader.ReadLine();
// Your code here
}
// Clean up
logFileReader.Close();
logFileStream.Close();
edited with MethodMan's suggestions
using(FileStream logFileStream = new FileStream(#"c:\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using(StreamReader logFileReader = new StreamReader(logFileStream))
{
string text = logFileReader.ReadToEnd();
// Your code..
}
}
You can do nothing, if the "another app" does not use Share.Read while creating/opening the file.
I am working with IsolatedStorage in Windows Phone 7.5. I am trying to read some text from a file. But the debugger says the operation is not permitted on IsolatedStorageFileStream. Why?
//Read the file from the specified location.
fileReader = new StreamReader(new IsolatedStorageFileStream("info.dat", FileMode.Open, fileStorage));
//Read the contents of the file (the only line we created).
string textFile = fileReader.ReadLine();
//Write the contents of the file to the MEssageBlock on the page.
MessageBox.Show(textFile);
fileReader.Close();
UPD my new code
object _syncObject = new object();
lock (_syncObject)
{
using (var fileStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (FileStream stream = new FileStream("/info.dat", FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var reader = new StreamReader(stream))
{
string textFile = reader.ReadLine();
MessageBox.Show(textFile);
}
}
}
}
}
Try this, it works for me: Hope it works for you too
String sb;
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(fileName))
{
StreamReader reader = new StreamReader(new IsolatedStorageFileStream(fileName, FileMode.Open, myIsolatedStorage));
sb = reader.ReadToEnd();
reader.Close();
}
if(!String.IsNullOrEmpty(sb))
{
MessageBox.Show(sb);
}
}
If this doesn't work, then maybe your file doesn't exist.
Normally when I've used isolated storage, I've done something like:
using (var stream = fileStorage.OpenFile("info.dat", FileMode.Open))
{
using (var reader = new StreamReader(stream))
{
...
}
}
... rather than calling the constructor directly on IsolatedStorageFileStream. I can't say for sure whether that'll sort it out, but it's worth a try...
Just a guess:
WP emulator will reset all Isolatd Storage contents when it's closed
if you used FileMode.Open with a path to a non existing file you'll get Operation not permited exception.
You can use fileStorage.FileExists() to check if the file is there or use FileMode.OpenOrCreate.
I have posted a question previous regarding editing my xml document via c#
C# write to XML error
However im now having trouble with it again. Im using the exact code that worked then but getting problems again!
When I first click the button it seems to work however when I click it again I get the error
Data at the root level is invalid. Line 83, position 10
When you then open the XML document for some reason the characters "" get added to the start of the xml document so I get
"<?xml version="1.0" encoding="UTF-8"?>"
I dont understand why and its really driving me insane. I'm sure it was working before.
My code:
path = test.xml
using (FileStream READER = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
System.Xml.XmlDocument Temp = new System.Xml.XmlDocument();
Temp.Load(READER);
using (FileStream WRITER = new FileStream(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
Temp.Save(WRITER);
}
}
UPDATE #2:
I compiled your sample as is and it worked perfectly for me. I tested it with a file created straight from code and also with a xaml file created in Visual Studio. So it seems the file you're working with is corrupted or have an encoding problem.
As far as I know you can't do anything about corrupted file, but as for encoding you can specify it when reading by using a StreamReader object. You just pass a desired encoding and your reader stream to StreamReader's constructor. Also it has an option to detect the encoding from byte order marks.
using (TextReader txtreader = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Encoding.GetEncoding(1251 /*desired codepage here*/)))
{
document.Load(txtreader);
}
or
using (TextReader txtreader = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), true /*tries to detect the encoding*/))
{
document.Load(txtreader);
}
Of course, you should save the file using the same encoding or you'll have problems next time you run your loading code.
Also I'm attaching a code which creates a file if it doesn't exist or just modifies it if it already exists.
class Program
{
static readonly string path = #"C:\Users\Dmitry\Documents\test_3.xml";
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
test(path);
}
static void test(string path)
{
XmlDocument document = new XmlDocument();
if (File.Exists(path))
{
using (Stream readStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
document.Load(readStream);
}
}
else
{
document.AppendChild(document.CreateXmlDeclaration("1.0", "UTF-8", String.Empty));
document.AppendChild(document.CreateElement("Test"));
}
document.DocumentElement.AppendChild(document.CreateElement("Node"));
using (FileStream WRITER = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
document.Save(WRITER);
}
}
}
I hope it helps you.