StorageFolder File Not Found error - c#

I am having some issues in my app that can download a list of music files. I'm trying to setup the following folder structure. Music Library > Artist(s) > Release Name. When starting the download, the first song's folder structure is setup properly. Once the second download starts, I always get a File Not found exception when trying to create the second sub folder (release name). Here is my code.
private async Task StartDownload(List<DownloadData> data)
{
foreach (DownloadData song in data)
{
// Set the source of the download
Uri source = new Uri(song.downloadUrl);
// Create folder stucture
StorageFolder artistFolder;
try
{
artistFolder = await KnownFolders.MusicLibrary.CreateFolderAsync(song.artistName, CreationCollisionOption.OpenIfExists);
}
catch
{
throw;
}
StorageFolder releaseFolder;
try
{
releaseFolder = await artistFolder.CreateFolderAsync(song.releaseName, CreationCollisionOption.OpenIfExists);
}
catch
{
throw; // Exception Thrown here
}
// Create file
StorageFile destinationFile;
try
{
destinationFile = await releaseFolder.CreateFileAsync(song.fileName, CreationCollisionOption.GenerateUniqueName);
}
catch
{
throw;
}
BackgroundDownloader downloader = new BackgroundDownloader();
DownloadOperation download = downloader.CreateDownload(source, destinationFile);
List<DownloadOperation> requestOperations = new List<DownloadOperation>();
requestOperations.Add(download);
await HandleDownloadAsync(download, true);
}
}
I have no idea why it works the first time around but fails on the second song.

According to the documentation for CreateFileAsync it will throw FileNotFoundExcption if
The folder name contains invalid characters, or the format of the folder name is incorrect.
So you likely need to replace invalid characters with something else like underscore.
var fixedFolderName = string.Join(
"_",
song.releaseName.Split(Path.GetInvaildFileNameChars()));

Related

Where are the files created with CreateFileAsync?

When I call the following function, it should create a .txt file and write a sentence in it. It seems to execute without any errors. But I cannot find where this file is stored/located after being created. I ran a Windows Search to look for the file but nothing came up. Where is this file located? Also, what is the best folder/location to put a .txt file that the program uses? Should I put it in Solution Explorer of Visual Studio or Debug folder?
private async void CreateFile() {
try {
// Create sample file, replace if exists.
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.CreateFileAsync("sample.txt", CreationCollisionOption.ReplaceExisting);
sampleFile = await storageFolder.GetFileAsync("sample.txt");
await FileIO.WriteTextAsync(sampleFile, "Swift as a shadow!");
} catch (Exception ex) {
textBox.Text = ex.ToString();
}
}
You can have the code show the path:
sampleFile = await storageFolder.GetFileAsync("sample.txt");
await new MessageDialog(sampleFile.Path).ShowAsync();
On Windows 10, this will be a path like:
C:\Users\[username]\AppData\Local\Packages\[Package family name]\LocalState\sample.txt
where username is the name of the logged on user and package family name is the package family name of your application

No permission to delete / overwrite files in subfolders of ApplicationData.Current.LocalFolder

I am facing an problem with files that I create within my application in dedicated sub folders of ApplicationData.Current.LocalFolder. I can create sub folders with ApplicationData.Current.LocalFolder.CreateFolderAsync() and even place files in it. But as soon as I try to overwrite or delete files, I get an access violation exception. (Read Access is still possible)
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
I looked up the UWP pages but most of the time they simply create a new folder and that's it.
My attempt to overwrite the file:
public async Task WriteFileAsync(string filename, Stream fileContent)
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
var deepFolders = filename.Split('/');
if (deepFolders != null)
{
for (var i=0; i<deepFolders.Length - 1; i++)
{
folder = await folder.GetFolderAsync(deepFolders[i]);
}
filename = deepFolders[deepFolders.Length - 1];
}
try
{
StorageFile oldFile = await folder.GetFileAsync(filename);
await oldFile.DeleteAsync();
}
catch (FileNotFoundException) { }
StorageFile file = await folder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
}
The first time it runs just fine, but as soon as the overwrite should take place, I get the exception.
Running the whole stuff without sub-folders works like charm.
The attempt to first read and delete the file resulted in the same exception already during the deletion.

How to load, edit and save xml in uwp app

Title says it all really. I've been stuck on this one for days and would appreciate some help. I've a main page and a settings page when the main page loads first time it tests for settings.xml in local folder and copies it if not found. Then when the user opens settings page it's supposed to load details from local folder allowing the user to edit before saving them back to the local folder from OnNavigatedFrom event.
Code to load from installation folder to local folder
// Has the file been copied already?
bool blFileExist = false;
try
{
await ApplicationData.Current.LocalFolder.GetFileAsync("settings.xml");
// No exception means it exists
blFileExist = true;
btnSettings.Foreground = new SolidColorBrush(Windows.UI.Colors.White);
}
catch (System.IO.FileNotFoundException)
{
// The file obviously doesn't exist
blFileExist = false;
btnSettings.Foreground = new SolidColorBrush(Windows.UI.Colors.Red);
}
catch (Exception)
{
}
if (!blFileExist)
{
try
{
// Cant await inside catch, but this works anyway
StorageFile stopfile = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync("settings.xml");
await stopfile.CopyAsync(ApplicationData.Current.LocalFolder);
}
catch (System.IO.FileNotFoundException)
{
}
catch (Exception)
{
}
}
Code to load and save settings page
private void loadSettings()
{
try
{
doc = XElement.Load("settings.xml");
nAIPlayers = int.Parse(doc.Element("ai_players").Value);
strCardBack = doc.Element("back").Value;
comboBoxAIPlayers.SelectedIndex = nAIPlayers - 1;
}
catch (System.IO.FileNotFoundException)
{
}
catch (Exception ex)
{
}
}
private async void saveSettings()
{
//try
//{
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("settings.xml");
using (Stream fileStream = await file.OpenStreamForWriteAsync())
{
doc.SetElementValue("ai_players", nAIPlayers);
doc.SetElementValue("back", "Back_0");
doc.Save(fileStream);
}
/*}
catch (System.IO.FileNotFoundException)
{
}
catch (Exception ex)
{
}*/
}
I think the problem is I'm accessing the local file to save it and the installation file to load it. The result is no matter what I save it always reads the values in the original settings.xml
How do I load this from the local folder?
doc = XElement.Load("settings.xml");
Update
On the first iteration the code runs fine and the settings page code opens as it should. It's only after leaving the settings page and running saveSettings() method that it fails and throws an error when reloading the settings page and running loadSettings().
System.Xml.XmlException: Data at the root level is invalid. Line 5, position 12
You are doing it wrong because you are using XElement.Load(string) where string stands for URI, which in this case should be:
The Uri parameter must be a file system relative or absolute path.
and with that you will have a problem in UWP as normally you don't have the permission. It also won't work here with URIs like: "ms-appdata:///settings.xml".
Probably you can read a path to your LocalFolder and use it (may work, though haven't tested it), but much easier is to load the content from stream (or read string from file and then load XML from that string), for example like this:
var file = await ApplicationData.Current.LocalFolder.GetFileAsync("settings.xml");
using(var stream = await file.OpenStreamForReadAsync())
{
var doc = XElement.Load(stream);
// ...
}
Note also that there are other classes like XDocument or XmlDocument where you can load and manage you xml file. Everything depends on your needs.

Json serializer - how to create a stream

public static async Task Store(ObservableCollection<Product> list)
{
Uri path = new Uri("ms-appx:///ListCollection.json");
var store = await StorageFile.GetFileFromApplicationUriAsync(path);
var stream = File.OpenWrite(store.Path);
var serialize = new DataContractJsonSerializer(typeof(ObservableCollection<Product>));
serialize.WriteObject(stream, list);
}
Ok this is the piece of code that I used to serialize a collection , works very well , no problem with it , but what I want and tried and no success. I created a JSON file in my project. I want to store and stream data to that file. I tried some methods but no success , how do I open a stream to a file that is currently in my project?
EDITED : Commented the code that was working and wrote what I intend to do. Thanks for support.
When I get to this line
var stream = File.OpenWrite(store.Path); it says that is inaccesible.
What I intend to do is serialize some data to a file called ListCollection.json that is emtpy , that file is project file. It might be the stream or it might be the file that gives me that error. No idea.
My guess is that your project file is located in the installation directory of your application and as far as I know you can't just write to that directory.
You would have to put a deployment action in your solution that writes the desired project file to the application data directory. There you should be able to write it.
I looked through some of the documentation and came accross this:
MSDN
The app's install directory is a read-only location.
I found a Link which makes use of a little hack or so it seems.
I am not sure if this will work if the application is deployed etc.
but you can try this to write the file.
I am not sure if you need a stream or not but feel free to comment:
private void Button_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<string> list = new ObservableCollection<string>();
list.Add("Hallo");
list.Add("Welt");
Task t = Store(list);
}
public static async Task Store(ObservableCollection<string> list)
{
StorageFile file = await GetStorageFileFromApplicationUriAsync();
if (file == null)
{
file = await GetStorageFileFromFileAsync();
}
if (file != null)
{
await file.DeleteAsync();
await CreateFileInInstallationLocation(list);
}
}
private static async Task<StorageFile> GetStorageFileFromFileAsync()
{
StorageFile file = null;
if (file == null)
{
try
{
StorageFolder folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
file = await folder.GetFileAsync("ListCollection.json");
}
catch
{ }
}
return file;
}
private static async Task<StorageFile> GetStorageFileFromApplicationUriAsync()
{
StorageFile file = null;
try
{
Uri path = new Uri("ms-appx:///ListCollection.json");
file = await StorageFile.GetFileFromApplicationUriAsync(path);
}
catch
{ }
return file;
}
private static async Task CreateFileInInstallationLocation(ObservableCollection<string> list)
{
var pkg = Windows.ApplicationModel.Package.Current;
var installedLocationFolder = pkg.InstalledLocation;
try
{
var file = await installedLocationFolder.CreateFileAsync("ListCollection.json", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
var filePath = file.Path;
DataContractJsonSerializer serialize = new DataContractJsonSerializer(typeof(ObservableCollection<String>));
using (Stream stream = await file.OpenStreamForWriteAsync())
{
serialize.WriteObject(stream, list);
stream.Flush();
}
}
catch (Exception ex)
{
var msg = ex.Message;
}
}
What this basically does is:
Find the file
Delete the file
Create a new file
Write your JSON to the file
I am really not an expert on this matter and it even to me seems pretty hacky but it apparently does the job.
If you can avoid writing to the install directory do it and use the method Frank J proposed

Upload file to skydrive through SkyDrive API

I try to upload a text file to my skydrive or at least create new text file in SD and edit it's content, through SkyDrive API in my Windows 8 application.
How can I do that?
I tried to do something like that:
LiveConnectClient client = new LiveConnectClient(session);
var fileData = new Dictionary<string, object>();
fileData.Add("name", "new_file.txt");
try
{
LiveOperationResult fileOperationResult = await client.PutAsync("me/skydrive", fileData);
this.infoTextBlock.Text = fileOperationResult.ToString();
}
catch (LiveConnectException exception)
{
this.infoTextBlock.Text = exception.Message;
}
but I get error
"The provided request is not valid. The root SkyDrive folder cannot be updated."
If I write something like "me/skydrive/" I get
"The provided URL is not valid. The requested path '' is not supported".
Method LiveConnectClient.PutAsync allows me only to update existing properties (but not it's content).
How it should be done properly?
Btw - Is content on LCDC(http://msdn.microsoft.com/en-us/library/live/hh826531.aspx) updated? I'm asking because some methods, which are in documentation, doesn't exist in dlls (f.e. LiveConnectClient.Upload. There's only BackgroundUploadAsync).
Thanks for help in advance,
Micheal
Close but as I wrote: I can't use client.upload method because LiveConnectClient class doesn't contain it. That's why I asked about site content update.
Anyway - I've got an answer:
//create a StorageFile (here is one way to do that if it is stored in your ApplicationData)
StorageFile file = awaitApplicationData.Current.LocalFolder.GetFileAsync("yourfilename.txt");
try {
client = new LiveConnectClient(session);
LiveOperationResult operationResult = await client.BackgroundUploadAsync("me/skydrive", file.Name, file, OverwriteOption.Overwrite);
}
catch (LiveConnectException exception) {
//handle exception
}
You should use the Upload method on LiveConnectionClient. For example, see the Uploading Files example in the Live SDK. Something like ...
LiveOperationResult fileOperationResult =
await client.Upload("me/skydrive", /*file name here*/, /*file stream here*/);
Here's another way to upload a file from a console application using a SkyDriveApiClient downloaded from http://skydriveapiclient.codeplex.com/releases/view/103081
static void Main(string[] args)
{
var client = new SkyDriveServiceClient();
client.LogOn("YourEmail#hotmail.com", "password");
WebFolderInfo wfInfo = new WebFolderInfo();
WebFolderInfo[] wfInfoArray = client.ListRootWebFolders();
wfInfo = wfInfoArray[0];
client.Timeout = 1000000000;
string fn = #"test.txt";
if (File.Exists(fn))
{
client.UploadWebFile(fn, wfInfo);
}
}

Categories

Resources