In my class "DiaryManager" I got two List with two different types (T) and I want to save it to file, then I want to load it.
I got it to work with one of my list's as I am going to show you.
My list that I save and load in my working code is named "m_diary". The save method is this one:
/// <summary>
/// Saves the object information
/// </summary>
public void Save()
{
// Gain code access to the file that we are going
// to write to
try
{
// Create a FileStream that will write data to file.
FileStream writerFileStream =
new FileStream(DATA_FILENAME, FileMode.Create, FileAccess.Write);
// Save our dictionary of friends to file
m_formatter.Serialize(writerFileStream, m_diary);
// Close the writerFileStream when we are done.
writerFileStream.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
And my load method is this one:
/// <summary>
/// Load the object to the program
/// </summary>
public void Load()
{
// Check if we had previously Save information of our friends
// previously
if (File.Exists(DATA_FILENAME))
{
try
{
// Create a FileStream will gain read access to the
// data file.
FileStream readerFileStream = new FileStream(DATA_FILENAME,
FileMode.Open, FileAccess.Read);
// Reconstruct information of our friends from file.
m_diary = (List<Diary>)
m_formatter.Deserialize(readerFileStream);
// Close the readerFileStream when we are done
readerFileStream.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
The "DATA_FILENAME" is this constant:
private const string DATA_FILENAME = "TrainingDiary.dat";
This code works perfect from my windows form class.
But now Iv added one more list with a different type.
How do I save and load that second list to?? :)
Best regards
Cyrix
You can do it using similar code for the second list, or you can write a generic method:
public static void Save<T>(string fileName, List<T> list)
{
// Gain code access to the file that we are going
// to write to
try
{
// Create a FileStream that will write data to file.
using (var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, list);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
And a load method:
public static List<T> Load<T>(string fileName)
{
var list = new List<T>();
// Check if we had previously Save information of our friends
// previously
if (File.Exists(fileName))
{
try
{
// Create a FileStream will gain read access to the
// data file.
using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var formatter = new BinaryFormatter();
list = (List<T>)
formatter.Deserialize(stream);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return list;
}
Usage of Load:
var list = new List<string> {"one", "two", "three"};
Save("first.dat", list);
var list2 = Load<string>("first.dat");
foreach (var VARIABLE in list2)
{
Console.WriteLine(VARIABLE);
}
Also see using Statement to handle open/close streams;
You should create a class that contains all the data (lists) you want to save. Then just save that class to file.
Related
I have a web application that checks for the modification of a config.json file on each page load. It checks the modified date of the file and compares it against the last processing time that was recorded. If it differs, it proceeds to allow the reading of the file contents, processing those contents and updating config.json as well as writing to a separate file. I want to ensure that multiple simultaneous connections both reading and writing to these two files won't cause an issue.
var lastWriteTime = File.GetLastWriteTimeUtc(ConfigJsonPath);
if (CacheTime != lastWriteTime)
{
var config = new ConfigWriter().ReadData(ConfigJsonPath);
var model = JsonConvert.DeserializeObject<StyleModel>(config);
// This method writes to a another file
ProcessConfig(model, page);
var serialized = JsonConvert.SerializeObject(model, Formatting.Indented);
new ConfigWriter().WriteData(serialized, ConfigJsonPath);
CacheTime = File.GetLastWriteTimeUtc(ConfigJsonPath);
}
ConfigWriter Class
public class ConfigWriter
{
private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
public void WriteData(string data, string path)
{
Lock.EnterWriteLock();
try
{
using (var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
{
fs.SetLength(0);
var dataAsByteArray = new UTF8Encoding(true).GetBytes(data);
fs.Write(dataAsByteArray , 0, dataAsByteArray .Length);
}
}
finally
{
Lock.ExitWriteLock();
}
}
public string ReadData(string filePath)
{
Lock.EnterReadLock();
try
{
string config;
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
using (var r = new StreamReader(fs, Encoding.UTF8))
{
config = r.ReadToEnd();
}
}
return config;
}
finally
{
Lock.ExitReadLock();
}
}
}
So in the end there is one file being read, and two files being altered (write), and I'm using the same lock on the write.
Did I overdo it here? I started with a simple filestream with FileShare set to read/write, and then I got paranoid and second guessing myself. Is there a better way to implement this?
I want to deserialize encrypted objects (multiple instances of the same object type) from a file, that were added during execution of a program. The objects were encrypted using CryptoStream.
However, when deserializing i cannot loop through cryptoStream as I intended.
I can serialize and encrypt the objects and the file grows with every object added.
I can also deserialize the first encrypted object. However, following objects cannot be restored.
I have tried different approaches to loop though the stream. Also I tried other cryptostreams and working around that problem.
Serializing:
private void EncryptObjectToFile(TraceMessage pMessage, FileInfo pFileInfo)
{
if (pFileInfo == null) { pFileInfo = m_LogFile; }
m_Formatter.Serialize(m_SerializedStream, pMessage);
m_SerializedStream.Position = 0;
using (FileStream logfile = new FileStream(m_LogFile.FullName, FileMode.Append))
{
using (CryptoStream cryptoStream = new CryptoStream(logfile, m_Algorythm.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
cryptoStream.Write(m_SerializedStream.ToArray(), 0, (int)m_SerializedStream.Length);
cryptoStream.FlushFinalBlock();
}
}
}
Deserializing:
private List<TraceMessage> DecryptObjectsFromFile(FileInfo pFileInfo)
{
List<TraceMessage> tmp = new List<TraceMessage>();
try
{
READWRITELOCK.AcquireReaderLock(READERTIMEOUTS);
Interlocked.Increment(ref READS);
try
{
if (pFileInfo == null) { pFileInfo = m_LogFile; }
m_SerializedStream.Position = 0;
using (FileStream logfile = new FileStream(m_LogFile.FullName, FileMode.Open))
{
using (var cryptoStream = new CryptoStream(logfile, m_Algorythm.CreateDecryptor(key, iv), CryptoStreamMode.Read))
{
if (cryptoStream.CanRead)
{
while (cryptoStream.Position < cryptoStream.Length)
{
tmp.Add((TraceMessage)m_Formatter.Deserialize(cryptoStream));
}
}
}
}
}
catch(Exception exp) { }
finally
{
READWRITELOCK.ReleaseReaderLock();
Interlocked.Decrement(ref READS);
}
}
catch (Exception) { }
return tmp;
}
In the end what i am trying to achieve is, to encrypt objects (multiple instances of the same object type) and add them to a file. As files can be added any time and cannot be collected in a list and then written to file, I want to add those objects to an already existing file.
Later I want to recreate each object that was encrypted and serialized to the file.
This question already has answers here:
Sharing violation IOException while reading and writing to file C#
(4 answers)
Closed 9 years ago.
I'm using this code for saving logs in may C# monotouch application:
public static void writeExeption(string message){
string path= StorageClass .LogsPath ;
string filepath= Path.Combine (path ,"Log.txt");
if(!File.Exists (filepath )){
using (StreamWriter sw = File.CreateText(filepath))
{
sw.WriteLine ("--------------------------------------------------------------------------" +
"--------------------");
sw.WriteLine("blahblah...");
sw.WriteLine ("--------------------------------------------------------------------------" +
"--------------------");
}
}
using (StreamWriter w = File.AppendText(filepath ))
{
Log(message , w);
}
}
public static void Log(string logMessage, TextWriter w)
{
w.Write("\r\nLog Entry : ");
w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
w.WriteLine(" :");
w.WriteLine(" :{0}", logMessage);
w.WriteLine ("--------------------------------------------------------------------------" +
"--------------------");
}
But in the application I get this error:
Sharing violation on path 'File Path'
Try adding to enclose the StreamWriter which appends the data in a lock statement.
First, add an object to refer to:
static object _locker = new object();
Then, modify the writeExeption method by locking the last using statement:
lock (_locker)
{
using (StreamWriter w = File.AppendText(filepath))
{
Log(message, w);
}
}
If this still doesn't work, it means that some other application is using your file. If it works, it means that you are logging on multiple threads.
Seems like you're accessing one file in two or more places (could be in different threads).
Use these methods to read/write files in multi-thread apps to avoid such error:
/// <summary>
/// Writes the file exclusively. No one could do anything with file while it writing
/// </summary>
/// <param name="path">Path.</param>
/// <param name="data">Data.</param>
public static void WaitFileAndWrite(string path, byte[] data)
{
while (true) {
Stream fileStream = null;
try {
// FileShare.None is important: exclusive access during writing
fileStream = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
fileStream.Write(data, 0, data.Length);
break;
} catch (IOException ex) {
Console.WriteLine (ex);
Thread.Sleep(10);
} finally {
if (fileStream != null) {
fileStream.Close();
}
}
}
}
/// <summary>
/// Waits the file and read.
/// </summary>
/// <returns>The file and read.</returns>
/// <param name="fileName">File name.</param>
public static byte [] WaitFileAndRead(string fileName)
{
byte[] result = null;
if (File.Exists(fileName)) {
while (true) {
Stream fileStream = null;
try {
fileStream = File.OpenRead(fileName);
var length = fileStream.Length;
result = new byte[length];
fileStream.Read(result, 0, Convert.ToInt32(length));
break;
} catch (IOException ex) {
Console.WriteLine (ex);
Thread.Sleep(10);
} finally {
if (fileStream != null) {
fileStream.Close();
}
}
}
}
return result;
}
}
Yet you should be accurate. If someone open file for read/write operations and don't close it, these method will try to open infinitely.
I need to read csv file twice. but after first reading:
using (var csvReader = new StreamReader(file.InputStream))
{
fileFullText += csvReader.ReadToEnd();
file.InputStream.Seek(0, SeekOrigin.Begin);
csvReader.Close();
}
using file in enother function:
public static List<string> ParceCsv(HttpPostedFileBase file)
{
//file.InputStream.Seek(0, SeekOrigin.Begin);
using (var csvReader = new StreamReader(file.InputStream))
{
// csvReader.DiscardBufferedData();
// csvReader.BaseStream.Seek(0, SeekOrigin.Begin);
string inputLine = "";
var values = new List<string>();
while ((inputLine = csvReader.ReadLine()) != null)
{
values.Add(inputLine.Trim().Replace(",", "").Replace(" ", ""));
}
csvReader.Close();
return values;
}
}
The file.Length is 0.
Can anybody help?
The reason is that SteramReader's Dispose() method also closes the underlying stream; In your case file.InputStream. The using statement calls Dispose() implicitly. Try to replace using with disposes of both your StreamReaded-s after you finished both read operations. As I remember some stream classes have a bool option to leave underlying stream open after dispose.
.NET 4.5 fixed this issue by introducing leaveOpen parameter in SteamReader constructor. See: MSDN
public StreamReader(
Stream stream,
Encoding encoding,
bool detectEncodingFromByteOrderMarks,
int bufferSize,
bool leaveOpen
)
One more thing. You do not need to close SteramReader yourself (the line with csvReader.Close();) when you wrap it in using statement, thus Dispose() and Close() are the same in case of StreamReader.
if your using HttpPostedFileBase you need to clone it first,
use the code this git here
or just add this as a class in your namespace:
public static class HttpPostedFileBaseExtensions
{
public static Byte[] ToByteArray(this HttpPostedFileBase value)
{
if (value == null)
return null;
var array = new Byte[value.ContentLength];
value.InputStream.Position = 0;
value.InputStream.Read(array, 0, value.ContentLength);
return array;
}
}
now you can read the HttpPostedFileBase like so:
private static void doSomeStuff(HttpPostedFileBase file)
{
try
{
using (var reader = new MemoryStream(file.ToByteArray()))
{
// do some stuff... say read it to xml
using (var xmlTextReader = new XmlTextReader(reader))
{
}
}
}
catch (Exception ex)
{
throw ex;
}
}
after using this you can still write in your main code:
file.SaveAs(path);
and it will save it to the file.
I've noticed that if I persist an object back into file using a Datacontractserializer, if the length of the new xml is shorter than the xml originally present in the file the remnants of the original xml outwith the length of the new xml will remain in the file and will break the xml.
Does anyone have a good solution to fix this?
Here's the code I am using to persist the object:
/// <summary>
/// Flushes the current instance of the given type to the datastore.
/// </summary>
private void Flush()
{
try
{
string directory = Path.GetDirectoryName(this.fileName);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
FileStream stream = null;
try
{
stream = new FileStream(this.fileName, FileMode.OpenOrCreate);
for (int i = 0; i < 3; i++)
{
try
{
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream, new System.Text.UTF8Encoding(false)))
{
stream = null;
// The serializer is initialized upstream.
this.serializer.WriteObject(writer, this.objectValue);
}
break;
}
catch (IOException)
{
Thread.Sleep(200);
}
}
}
finally
{
if (stream != null)
{
stream.Dispose();
}
}
}
catch
{
// TODO: Localize this
throw;
//throw new IOException(String.Format(CultureInfo.CurrentCulture, "Unable to save persistable object to file {0}", this.fileName));
}
}
It's because of how you are opening your stream with:
stream = new FileStream(this.fileName, FileMode.OpenOrCreate);
Try using:
stream = new FileStream(this.fileName, FileMode.Create);
See FileMode documentation.
I believe this is due to using FileMode.OpenOrCreate. If the file already exits, I think the file is being opened and parts of the data are being overwritten from the start byte. If you change to using FileMode.Create it forces any existing files to be overwritten.