I'm building a Windows Phone 7 app in Silverlight. I'm having difficulty using IsolatedStorageFile.
The following method is supposed to write some data to a file:
private static void writeToFile(IList<Story> stories)
{
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream stream = storage.OpenFile(STORIES_FILE, FileMode.Append))
{
using (StreamWriter writer = new StreamWriter(stream))
{
StringBuilder toJson = new StringBuilder();
IList<StoryJson> storyJsons = (from story in stories
where !storageStories.Contains(story)
select story.ToStoryJson()).ToList();
writer.Write(JsonConvert.SerializeObject(storyJsons));
}
}
#if DEBUG
StreamReader reader = new StreamReader(storage.OpenFile(STORIES_FILE, FileMode.Open));
string contents = reader.ReadToEnd();
#endif
}
The DEBUG at the end is for me to check that the data is actually being written. I have verified that it is. This method is called 6+ times. Each time, more data is appended.
However, when I go to read the data, the only JSON I get back is that which I wrote in one call of writeToFile(). Here is my method to read:
private static IList<Story> storageStories;
private static IList<Story> readFromStorage()
{
if (storageStories != null)
{
return storageStories;
}
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
if (! storage.FileExists(STORIES_FILE))
{
storage.CreateFile(STORIES_FILE);
storageStories = new List<Story>();
return storageStories;
}
string contents;
using (IsolatedStorageFileStream stream = storage.OpenFile(STORIES_FILE, FileMode.OpenOrCreate))
{
using (StreamReader reader = new StreamReader(stream))
{
contents = reader.ReadToEnd();
}
}
JsonSerializer serializer = new JsonSerializer();
storageStories = JArray.Parse(contents).Select(storyData => storyOfJson(serializer, storyData)).ToList();
return storageStories;
}
What could I be doing wrong here? Am I writing to the file incorrectly? I'm pretty sure that the only data that is able to be read back is from the first write.
Update: I added two Flush() calls, but it crashes:
private static void writeToFile(IList<Story> stories)
{
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream stream = storage.OpenFile(STORIES_FILE, FileMode.Append))
{
using (StreamWriter writer = new StreamWriter(stream))
{
StringBuilder toJson = new StringBuilder();
IList<StoryJson> storyJsons = (from story in stories
where !storageStories.Contains(story)
select story.ToStoryJson()).ToList();
writer.Write(JsonConvert.SerializeObject(storyJsons));
writer.Flush();
}
// FAILS
// "Cannot access a closed file." {System.ObjectDisposedException}
stream.Flush();
}
}
If I comment out the stream.Flush() but leave writer.Flush(), I have the same problem.
Update 2: I added some print statements. Looks like everything is getting serialized:
Serializing for VID 43
Serializing for VID 17
Serializing for VID 6
Serializing for VID 33
Serializing for VID 4
Serializing for VID 5
Serializing for VID 3
But only the first set is actually being read back:
Deserializing stories with vid: 43
I have run the test a few more times. I'm pretty sure that only the first item is ever being read back.
At first glance it sounds like your stream data is not being flushed to disk.
You are probably thinking that the using block will perform a flush when it Disposes the stream. However I have found that is not always the case and sometimes it is best to force a Flush() at the end.
I remember recently in a codebase that we received from a Microsoft team to port to WP7 that they were forcing a Flush. I questioned it initially, thinking that the Dispose should handle that, however as it was working and we were on a short deadline I did not investigate it further.
Give it go, see what happens... :)
Have you tried explicitly calling
writer.Close()
rather than relying on writer.Dispose()
Related
in my Hololens app i want to write data into a file which i can then view over the Device Portal. The Data contains just the time from one airtap on a special object to another airtap.
The problem ist that there will be no file created in the Device Portal under /LocalAppData/YourApp/LocalState
Thanks in advance
Jonathan
public void StopTime()
{
TimeSpan ts = time.Elapsed;
time.Stop();
path = Path.Combine(Application.persistentDataPath, "Messdaten.txt");
using (TextWriter writer = File.CreateText(path))
{
writer.Write(ts);
}
}
I usually use a FileStream and a StreamWriter and make sure the FileMode.Create is set.
See also How to write text to a file for more approaches
using (var file = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
using (var writer = new StreamWriter(file, Encoding.UTF8))
{
writer.Write(content);
}
}
With that I never had any trouble on the HoloLens so far.
You also might want to use something like ts.ToString() in order to format the value to your needs.
Alternatively you could also try Unity's Windows.File (only available for UWP) but than you need to have byte[] as input. E.g. from here
long c = ts.Ticks;
byte[] d = BitConverter.GetBytes(c);
File.WriteAllBytes(path, d);
The simplest thing to do is to use File.WriteAllText method https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writealltext?view=netframework-4.7.2
Obviously there are many ways that could work but it is good to stick to the simplest solution.
I have a method that saves an instance of my custom class to a file. One time I noticed that my application fails to start, because this file is filled with 0-value bytes (null characters). This has never happened before, it seemed to work just fine. Does anyone see something odd with this code? Something that can cause the serializer or the memory stream to return an array of zero values? Or should I suspect it's the work of another application?
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(MySettings));
public void Save(MySettings config)
{
using (var stream = new MemoryStream())
{
_serializer.Serialize(stream, config);
byte[] binaryConfig = stream.ToArray();
File.WriteAllBytes(_configFilePath, binaryConfig);
}
}
Wouldn't it be simpler to use something like this?
XmlSerializer x = new XmlSerializer(typeof(MySettings));
using (FileStream stream = new FileStream(_configFilePath, FileMode.Create, FileAccess.Write))
{
x.Serialize(stream, config);
stream.Close();
}
The XML file should not contain any 0-bytes or nul characters, as your object is translated to XML text during serialization. You can simply open the XML file using a text editor to have a look at the file contents.
I'm writing a Windows Phone Silverlight app. I want to save an object to a JSON file. I've written the following piece of code.
string jsonFile = JsonConvert.SerializeObject(usr);
IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream("users.json", FileMode.Create, isoStore);
StreamWriter str = new StreamWriter(isoStream);
str.Write(jsonFile);
This is enough to create a JSON file but it is empty. Am I doing something wrong? Wasn't this supposed to write the object to the file?
The problem is that you're not closing the stream.
File I/O in Windows have buffers at the operating system level, and .NET might even implement buffers at the API level, which means that unless you tell the class "Now I'm done", it will never know when to ensure those buffers are propagated all the way down to the platter.
You should rewrite your code just slightly, like this:
using (StreamWriter str = new StreamWriter(isoStream))
{
str.Write(jsonFile);
}
using (...) { ... } will ensure that when the code leaves the block, the { ... } part, it will call IDisposable.Dispose on the object, which in this case will flush the buffers and close the underlying file.
I use these. Shoud work for you as well.
public async Task SaveFile(string fileName, string data)
{
System.IO.IsolatedStorage.IsolatedStorageFile local =
System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
if (!local.DirectoryExists("MyDirectory"))
local.CreateDirectory("MyDirectory");
using (var isoFileStream =
new System.IO.IsolatedStorage.IsolatedStorageFileStream(
string.Format("MyDirectory\\{0}.txt", fileName),
System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite,
local))
{
using (var isoFileWriter = new System.IO.StreamWriter(isoFileStream))
{
await isoFileWriter.WriteAsync(data);
}
}
}
public async Task<string> LoadFile(string fileName)
{
string data;
System.IO.IsolatedStorage.IsolatedStorageFile local =
System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
using (var isoFileStream =
new System.IO.IsolatedStorage.IsolatedStorageFileStream
(string.Format("MyDirectory\\{0}.txt", fileName),
System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read,
local))
{
using (var isoFileReader = new System.IO.StreamReader(isoFileStream))
{
data = await isoFileReader.ReadToEndAsync();
}
}
return data;
}
I'm trying to process part of a text file, and write the remainder of the text file to a cloud blob using UploadFromStream. The problem is that the StreamReader appears to be grabbing too much content from the underlying stream, and so the subsequent write does nothing.
Text file:
3
Col1,String
Col2,Integer
Col3,Boolean
abc,123,True
def,3456,False
ghijkl,532,True
mnop,1211,False
Code:
using (var stream = File.OpenRead("c:\\test\\testinput.txt"))
using (var reader = new StreamReader(stream))
{
var numColumns = int.Parse(reader.ReadLine());
while (numColumns-- > 0)
{
var colDescription = reader.ReadLine();
// do stuff
}
// Write remaining contents to another file, for testing
using (var destination = File.OpenWrite("c:\\test\\testoutput.txt"))
{
stream.CopyTo(destination);
destination.Flush();
}
// Actual intended usage:
// CloudBlockBlob blob = ...;
// blob.UploadFromStream(stream);
}
When debugging, I observe that stream.Position jumps to the end of the file on the first call to reader.ReadLine(), which I don't expect. I expected the stream to be advanced only as many positions as the reader needed to read some content.
I imagine that the stream reader is doing some buffering for performance reasons, but there doesn't seem to be a way to ask the reader where in the underlying stream it "really" is. (If there was, I could manually Seek the stream to that position before CopyingTo).
I know that I could keep taking lines using the same reader and sequentially append them to the text file I'm writing, but I'm wondering if there's a cleaner way?
EDIT:
I found a StreamReader constructor which leaves the underlying stream open when it is disposed, so I tried this, hoping that the reader would set the stream's position as it's being disposed:
using (var stream = File.OpenRead("c:\\test\\testinput.txt"))
{
using (var reader = new StreamReader(stream, Encoding.UTF8,
detectEncodingFromByteOrderMarks: true,
bufferSize: 1 << 12,
leaveOpen: true))
{
var numColumns = int.Parse(reader.ReadLine());
while (numColumns-- > 0)
{
var colDescription = reader.ReadLine();
// do stuff
}
}
// Write remaining contents to another file
using (var destination = File.OpenWrite("c:\\test\\testoutput.txt"))
{
stream.CopyTo(destination);
destination.Flush();
}
}
But it doesn't. Why would this constructor be exposed if it doesn't leave the stream in an intuitive state/position?
Sure, there's a cleaner way. Use ReadToEnd to read the remaining data, and then write it to a new file. For example:
using (var reader = new StreamReader("c:\\test\\testinput.txt"))
{
var numColumns = int.Parse(reader.ReadLine());
while (numColumns-- > 0)
{
var colDescription = reader.ReadLine();
// do stuff
}
// write everything else to another file.
File.WriteAllText("c:\\test\\testoutput.txt", reader.ReadToEnd());
}
Edit after comment
If you want to read the text and upload it to a stream, you could replace the File.WriteAllText with code that reads the remaining text, writes it to a StreamWriter backed by a MemoryStream, and then sends the contents of that MemoryStream. Something like:
using (var memStream = new MemoryStream())
{
using (var writer = new StreamWriter(memStream))
{
writer.Write(reader.ReadToEnd());
writer.Flush();
memStream.Position = 0;
blob.UploadFromStream(memStream);
}
}
You should never access the underlying stream of a StreamReader. Trying to use both is going to have an undefined behavior.
What's going on here is that the reader is buffering the data from the underlying stream. It doesn't read each byte exactly when you request it, because that's often going to be very inefficient. Instead it will grab chunks, put them in a buffer, and then provide you with data from that buffer, grabbing a new chunk when it needs to.
You should continue to use the StreamReader throughout the remainder of that block, instead of using stream. To minimize the memory footprint of the program, the most effective way of doing this would be to read the next line from the reader in a loop until it his the end of the file, writing each line to the output stream as you go.
Also note that you don't need to be disposing of both the stream reader and the underlying stream. The stream reader will dispose of the underlying stream itself, so you can simply adjust your header to:
using (var reader = new StreamReader(
File.OpenRead("c:\\test\\testinput.txt")))
I have a bit of a problem with the client.UploadAsync method of Live SDK (SkyDrive SDK). My code for some reason doesn't work or more specifically it uploads an empty file. It doesn't throw any error and the serialization to stream works (I know that for sure).
It even seems that the Memory Stream is OK. (since I have no tool to really see the data in it I just guess it is OK by looking at its 'Length' property).
The UploadAsync method is fine as well or at least it worked well when I first serialized the data into a .xml file in IsolatedStorage then read it with IsolatedStorageFileStream and then eventualy send that stream. (then it uploaded the data)
Any advice on why this may be happening?
public void UploadFile<T>(string skyDriveFolderID, T data, string fileNameInSkyDrive)
{
this.fileNameInSkyDrive = fileNameInSkyDrive;
{
try
{
memoryStream = new MemoryStream();
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
{
serializer.Serialize(xmlWriter, data);
}
client.UploadAsync(skyDriveFolderID, fileNameInSkyDrive, true, memoryStream, null);
}
catch (Exception ex)
{
if (memoryStream != null) { memoryStream.Dispose(); }
}
}
}
You have to "rewind" the memorystream to the start before calling the UploadAsync method. Imagine the memorystream being like a tape which you record things on. The "read/write-head" is always floating over some point of the tape, which is the end in your case because you just wrote all serialized data onto it. The uploading method tries to read from it by moving forward on the tape, realizing it is already at its end. Thus you get an empty file uploaded.
The method you need for rewinding is:
memoryStream.Seek(0, SeekOrigin.Begin);
Also, it is good practice to use the using directive for IDisposable objects, which the memorystream is. This way you don't need a try {...} finally { ...Dispose(); } (this is done by the using).
Your method could then look like:
public void UploadFile<T>(string skyDriveFolderID, T data, string fileNameInSkyDrive)
{
this.fileNameInSkyDrive = fileNameInSkyDrive;
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (var memoryStream = new MemoryStream())
{
using (var xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
{
serializer.Serialize(xmlWriter, data);
}
memoryStream.Seek(0, SeekOrigin.Begin);
client.UploadAsync(skyDriveFolderID, fileNameInSkyDrive, true, memoryStream, null);
}
}