Loading TXT file in Windows Phone 8.1 - c#

Thanks to the below link, I've found out how to load a TXT file into my Windows Phone 8.1 app. My question is specifically tied to WHY my code doesn't work. (My code begins after link to the other StackOverflow question).
This is the same question, with a working answer.
Read text file in project folder in Windows Phone 8.1 Runtime
I set the StorageFile as the relative location of the file on the phone. I then try and use that location to open a StreamReader. This small snippet compiles fine, however on execution, encounters a runtime error, and "firstFile" is empty/null.
const string filename = "FileToRead.txt";
StorageFile firstFile = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
using (StreamReader streamFirstFile = new StreamReader(await firstFile.OpenStreamForReadAsync()))
{
loadText_Button.Text = await streamFirstFile.ReadToEndAsync();
}
To hopefully further clarify this specific question, are the following two lines of code the same, and if not, how am I using it wrong?
ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
StorageFile.GetFileFromApplicationUriAsync(new Uri(#"ms-appx:///Assets/FileToRead.txt"))
I even tried to set it as follows:
ApplicationData.Current.LocalFolder.GetFileAsync(String.Format("{0}{1}", "Assets/", fileName);
Thank you all in advance.

If you want to read a file from your package then you should refer to Package.Current.InstalledLocation, not LocalFolder. You should also check if your resource has Build Action set to Content.
You can access your file in two ways - either getting it from InstalledLocation folder:
StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(#"Data\" + fileName);
or by Uri:
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(#"ms-appx:///Data/"+fileName));
Few notes:
files in package are read-only,
you can also access files in shared projects by providing its name before folder name
watch out for Build Action - some files are by default set as Content by VS and some not.
You can also find some useful information at this post.

A note for the unwary.
If the name of the file you placed in your Assets folder ends with ".json", the file won't be accessible from your code using the above given function calls. Simply rename the file so that it ends with ".txt".
I would have made this a comment but I don't have the required reputation.

Related

OpenWrite UnauthorizedAccessException

I am working on a desktop application that internally creates a StringBuilder that errors get appended to and ultimately gets written to a txt file.
I get an exception that says 'Access to the path 'C:\Users\Me\Documents\test_dir\5_hundred_thousand_rows_Logs.txt' is denied.'
Below is the code that performs the creation of the .txt file. The exception catches on the File.OpenWrite(tempfile)) line
string tempfile="C:\\Users\\Me\\Documents\\test_dir\\5_hundred_thousand_rows_Logs.txt";
using (Stream fileStream = File.OpenWrite(tempfile))//exception here
{
string data = logFileContent.ToString();
Byte[] filecontent = new UTF8Encoding(true).GetBytes(data);
fileStream.Write(filecontent, 0, filecontent.Length);
}
Process.Start(tempfile);
I have double checked and the tempfile does indeed have the file extension and is not attempting to create a directory.
I've tried wrapping the using statement with the following to attempt to "grant" access but to no avail:
var permissionSet = new PermissionSet(PermissionState.None);
var writePermission = new FileIOPermission(FileIOPermissionAccess.AllAccess, Path.GetDirectoryName(tempfile));
writePermission.Demand();
permissionSet.AddPermission(writePermission);
FileAttributes attributes = File.GetAttributes(Path.GetDirectoryName(tempfile));
if (permissionSet.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet))
{
// using statement
}
I am wanting this desktop application to be able to be handed to anyone and they be able to use this without having the user deal with folder permissions or something. Granted I'm really only expecting it to try to access MyDownloads, MyDocuments, directories on flash drives or other similar devices.
I would greatly appreciate any help provided.
EDIT:
It appears that this may be a Windows issue. The file is set to readonly in its properties (was not this way originally), and attempting to set the file as not readonly doesn't work as Windows apparently just resets it back to readonly. However if the file is not in a folder and is just sitting on the bare drive (such as a different drive eg: D:/5_hundred_thousand_rows_Logs.txt) it seems to work just fine. If there's a programmatic way to get around this I would appreciate it, but if this is a Windows issue this may require making this a different question.
According to the documentation the exception is thrown when you don't have the permissions, or the file is readonly.
To test if you don't have permissions, try and run your application as administrator. To verify the second case, right click the file in explorer, choose properties, and make sure it's not readonly.
Besides that string tempfile="C:\\Users\\Me\\Documents\\test_dir\\5_hundred_thousand_rows_Logs.txt"; is a weird path, because it's using the user me which definitelly doesn't exist on every machine. If you like to get the documents folder of the current user you can use: Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); This will create the correct path for you.

How do I generate the folder that's in the path name of the file I am downloading

So I am currently downloading files from my Web Server and the links to the files are something along the lines of
var url = "https://website.com/Game/patches/FooGame/App.config"
and I am currently using
webClient.DownloadFile(new Uri(url),Path.Combine("C:\\GAME\\THE GAME FOLDER", filename));
The filename is "FooGame\\App.config"
in hopes of it actually creating the folder FooGame and then downloading the file that it got from the url and place it there with the name App.config but it's not creating the directory, if I recall correcly it should create a folder based on the name and I SHOULD NOT have to use Directory.Create()
It's currently throwing an exception saying "Could not find part of the path" followed by the path you saw above. Why is that and is there a good way of downloading files and let it create a folder where it should me.
As a general rule you do not, as you can not be sure there even is a path in the URL. my_servername.com/http_downloadhandler?file= can be totally where you get your file from.
If you want to copy a filestructure, usually you download the folder as a zip archive and unpack it. That is how everyone down to Github does it. And the .zip file can be generated on demand, thanks to the ZipArchive class and the HTTP Filehandlers. Even HFS has support for that much.
If you do control the server but somehow can not get .zip download of a folder working, I guess you could use the URL class to extract the path and the path class to work it into the write order. But really, just use the .zip route.
If you call before download file to:
Directory.CreateDirectory(Path.Combine("C:\\GAME\\THE GAME FOLDER", filename))
You are going to get:
Creates all directories and subdirectories in the specified path unless they already exist.
Source: Microsoft Documentation
If you create the full path first, then you can get the directory name from that string and create it:
var filePath = Path.Combine("C:\\GAME\\THE GAME FOLDER", filename);
Directory.CreateDirectory(Path.GetDirectoryName(filePath);

What is a way to open file for writing wihout having to use OpenFilePicker in WinRT, Win8

I've been at this for a few days and been fighting with this for two years. Roughly, I have an App where the user opens a file, edits for a long period of time and then saves or suspends until later. I have an automatic save feature that works, but, I want an auto-archiving feature too (in case the user wants to auto-save to a backup file, and not overwrite the current file).
Roughly speaking, I want to create a new file when a file is being edited, hopefully where the original file is. However, I've quickly given that up as Microsoft will not allow access to Skydrive, other directories, etc for writing without explicit picker access.
However, I thought maybe I could use the Documents Library. But any use here seems to give me the NullReferenceException. Here's a sample of what I was trying (noting that .ged files are associated with my App):
string backupFileName = openStorageFile + "-backup.ged";
StorageFolder currentFolder;
StorageFile fileCopy;
try
{
// try to set up Backup file in the same directory as the source file
currentFolder = await selectedFile.GetParentAsync();
fileCopy = await openStorageFile.CopyAsync(currentFolder, backupFileName, NameCollisionOption.ReplaceExisting);
// Success return
return;
}
catch
{
// The failed... try something else
Debug.WriteLine("INFO: Failed to set up backup file in the source file's directory, trying backup option");
}
try
{
// try to set up Backup file in the same directory as the source file
fileCopy = await openStorageFile.CopyAsync(KnownFolders.DocumentsLibrary, backupFileName, NameCollisionOption.ReplaceExisting);
}
catch
{
// The failed... try something else
Debug.WriteLine("INFO: Failed to set up backup file in the source document library, no other option available");
}
}
So roughly, The first the first call for GetParentAsync form the original file works. But CopyAsync to that directory fails with NullReferenceException.
The second try uses DocumentsLibrary.which also fails on CopyAsync with a NullReferenceException.
I tried this:
Windows 8 StorageFile.GetFileFromPathAsync Using UNC Path
According to MSDN, I shouldn't have an issue as long as my App is associated with .ged files (and it is). Is there anything else to try?
You need to add the documentsLibrary capability for access to the documents library. This capability is discouraged and is not exposed in Visual Studio's manifest designer. You'll have to edit the package.appxmanifest's xml directly
<Capabilities>
<Capability Name="internetClient" />
<Capability Name="documentsLibrary" />
</Capabilities>
I confirmed that your code saves to the documents library once I added that and fixed backupFileName setter to use the openStorageFile's Name. From the other references in the code openStorageFile is a StorageFile not a string.
string backupFileName = openStorageFile.Name + "-backup.ged";
See the Special use capabilities section of the capabilities documentation for more restrictions on using documents library if you need the app to be certified for the store rather than side-loaded.
I would save the backup in the app's local data rather than in the documents library. The app can expose a way to restore from the backup to a picked location when the user needs it.

How to get a StorageFolder from an user-friendly (localized) path?

How to get a StorageFolder from a user-friendly (localized) path?
Folders can have a user-friendly (localized) name. The name can be read via:
StorageFolder.DisplayName
Example: The Folder 'C:\Users' is shown on a 'German' Windows as 'C:\Benutzer'.
I would like to get the StorageFolder from a user-friendly path like the Windows Explorer. However, calling the following method throws an exception:
var folder = await StorageFolder.GetFolderFromPathAsync(#"C:\Benutzer");
Is there some support in WinRT API to achieve this?
Edit: I'm not explicitly answering the localised part of your question, but it's entirely possible that you're falling foul of a permissions issue that I describe below.
It's not possible in WinRT to access the file system** without it being user initiated.
The mechanism they have in WinRT for what you're describing is to ask the user to pick the location via a folder picker, then to add that chosen folder to the FutureAccessList for programmatic access later.
StorageFile folder = await folderPicker.PickSingleFolderAsync();
folderToken = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.Add(folder);
//Keep this folder token to access the folder programmatically later
You can access that folder later using the following.
StorageFolder folder = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(folderID);
Note that there's absolutely no exception handling in this example. Check the links (particularly the latter) for more details.
** As in your example, however there are standard folder / file resources you can access without this.
you can get the StorageFolder located into app install path - Package.Current.InstalledLocation.Path by specifying the path manually, for example you downloaded a folder with subfolders and you want to get a file from there, you'll use:
StorageFolder customAppFolder = await StorageFolder.GetFolderFromPathAsync(Package.Current.InstalledLocation.Path + #"\yourFolder\yourSubfolder");
now you can iterate files in that subfolder:
IReadOnlyList<StorageFile> filesInFolder =await appFolder.GetFilesAsync();
foreach (StorageFile file in filesInFolder){
Debug.WriteLine(file.Name);
}

How to read text from a text file in the XAP?

I'm working on an out-of-browser Silverlight program, and I have successfully gotten it to open local files by means of an OpenFileDialog. However, now I need it to open a file from within its own XAP (no browsing necessary, the file to open is hard-coded). I am trying to use this code, but it's not working:
using (StreamReader reader = new StreamReader("Default.txt"))
{
TextBox1.Text = reader.ReadToEnd();
}
This code throws a SecurityException that says "File operation not permitted. Access to path 'Default.txt' is denied." What am I doing wrong?
Your code is trying to open a file called "Default.txt" that is somewhere out in the user's file system. Where exactly I don't know, as it depends on where the Silverlight app's executing from. So yes, in general you don't have permission to go there.
To pull something out of your XAP, you need ton construct the stream differently. It will be along these lines:
Stream s = Application.GetResourceStream(
new Uri("/MyXap;component/Path/To/Default.txt", UriKind.Relative)).Stream;
StreamReader reader = new StreamReader(s);
Note, this means your Default.txt should be set to 'Resource', not 'Embedded Resource'. By being a 'Resource' it will get added to the XAP. Embedded Resource will add it to the assembly.
More info: http://nerddawg.blogspot.com/2008/03/silverlight-2-demystifying-uri.html
Note: In cases where your Silverlight program has multiple assemblies, check that the "/MyXap" part of the Uri string references the name of assembly containing the resource. For example if you have two assemblies "ProjectName" and "ProjectName.Screens", where "ProjectName.Screens" contains your resource, then use the following:
new Uri("ProjectName.Screens;component/Path/To/Default.txt", UriKind.Relative))

Categories