UWP StorageFolder access to the Downloads folder - c#

I have done a ton of research on MSDN and SO but there seem to be a lot of mixed reviews on this topic and no straightforward answer. My UWP app needs to download some items for the user. It seems only logical that this goes into the "downloads" folder instead of Documents or Pictures.
What I gather from my reading is that an application is allowed to access the downloads folder and create files and sub folders within the downloads folder. However, it cannot access other files and folder (not created from your app) without the use of a picker. In this case, I should not need to use a picker because my app is using the and creating the folder for itself. I have also read, there is not need for special capabilities in the Manifest for this to work.
I can confirm that this does in fact work by creating a folder and a file in the downloads folder
StorageFile destinationFile;
StorageFolder downloadsFolder;
try
{
//Create a sub folder in downloads
try
{
downloadsFolder = await DownloadsFolder.CreateFolderAsync("AppFiles");
}
catch (Exception ex)
{
//HERE IS THE ISSUE. I get in here if the folder exists but how do i get it?
}
destinationFile = await downloadsFolder.CreateFileAsync(destination,CreationCollisionOption.GenerateUniqueName);
}
catch (FileNotFoundException ex)
{
rootPage.NotifyUser("Error while creating file: " + ex.Message, NotifyType.ErrorMessage);
return;
}
However, here is the major issue. This code works fine the first time through because the folder does not already exist and it creates it along with the file. Subsequent times through, it fails and throws an exception:
Cannot create a file when that file already exists. (Exception from HRESULT: 0x800700B7)
It does this on the line to create the folder in Downloads folder:
downloadsFolder = await DownloadsFolder.CreateFolderAsync("AppFiles");
The problem is that MSDN states that I cannot use the Collision options of "OpenIfExists" or "ReplaceExisting" which are the two collision options I would need to solve this problem. The two remaining options do no good for me. So, no matter what, it is going to throw an exception if the folder exists.
Then, the thought is that I could just catch the exception, like I am already doing in my snippet above and open the folder if it exists. The problem with this is that the "DownloadsFolder" class does not give any options to get or open a folder, only to create a folder.
So, it seems I can create the folder from my app but I cannot open or get the folder that my app created?
Thanks!

The problem with this is that the "DownloadsFolder" class does not give any options to get or open a folder, only to create a folder.
Actually, When you first run your code, you could create your folder successfully and get the folder instance to create file in this folder. But why you could not get it when it's existed, it's by design.
I believe you have checked the document:
Because the app can only access folders in the Downloads folder that it created, you can't specify OpenIfExists or ReplaceExisting for this parameter.
So, How to get the folder that you created? I will tell you in the following:)
In this case, I should not need to use a picker because my app is using the and creating the folder for itself.
As you said, the first option is to use a picker, but you've said that you do not want to use a picker. Then, I will give you another option.
When you first create the folder successfully, you could add this folder to the FutureAccessList. Then, you could get this folder directly in your code.
I've made a simple code sample for your reference:
StorageFile destinationFile;
StorageFolder downloadsFolder;
try
{
try
{
downloadsFolder = await DownloadsFolder.CreateFolderAsync("AppFiles");
string folderToken = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.Add(downloadsFolder);
ApplicationData.Current.LocalSettings.Values["folderToken"] = folderToken;
destinationFile = await downloadsFolder.CreateFileAsync("destination.txt", CreationCollisionOption.GenerateUniqueName);
}
catch (Exception ex)
{
if (ApplicationData.Current.LocalSettings.Values["folderToken"] != null)
{
string token = ApplicationData.Current.LocalSettings.Values["folderToken"].ToString();
downloadsFolder = await Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.GetFolderAsync(token);
destinationFile = await downloadsFolder.CreateFileAsync("destination.txt", CreationCollisionOption.GenerateUniqueName);
}
}
}
catch (FileNotFoundException ex)
{
rootPage.NotifyUser("Error while creating file: " + ex.Message, NotifyType.ErrorMessage);
return;
}

Related

System.UnauthorizedAccessException when writing to the install path

I'm trying to download multiple files from an SFTP server and save them to the install path (or actually, ANY path at the moment just to get it working). However, I get an UnauthorizedAccess Exception no matter where I try to save the files.
As far as was aware, there are no special permissions required to save files to the install dir (Hence why I chose this folder).
Thread myThread = new Thread(delegate() {
string host;
string username;
string password;
// Path to folder on SFTP server
string pathRemoteDirectory = "public_html/uploads/17015/";
// Path where the file should be saved once downloaded (locally)
StorageFolder localFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
string pathLocalDirectory = localFolder.Path.ToString();
var methods = new List<AuthenticationMethod>();
methods.Add(new PasswordAuthenticationMethod(username, password));
//TODO - Add SSH Key auth
var con = new ConnectionInfo(host, 233, username, methods.ToArray());
using (SftpClient sftp = new SftpClient(con))
{
try
{
sftp.Connect();
var files = sftp.ListDirectory(pathRemoteDirectory);
// Iterate over them
foreach (SftpFile file in files)
{
Console.WriteLine("Downloading {0}", file.FullName);
using (Stream fileStream = File.OpenWrite(Path.Combine(pathLocalDirectory, file.Name)))
{
sftp.DownloadFile(file.FullName, fileStream);
Debug.WriteLine(fileStream);
}
}
sftp.Disconnect();
}
catch (Exception er)
{
Console.WriteLine("An exception has been caught " + er.ToString());
}
}
});
Connection to the server is all fine, the exception occurs on this line.
using (Stream fileStream = File.OpenWrite(Path.Combine(pathLocalDirectory, file.Name)))
I'm must be missing something obvious here but it's worth noting that I've also tried writing to Special Folders like the Desktop, the users Document folder and also direct to the C:/ drive, all with the same exception. I'm also running with Administrator privileges and I have the correct permissions set in the folders.
It turns out that SFTP was counting '.' and '..' as files and trying to download those, when obviously '.' is the set SFTP folder and '..' is the previous folder. This was causing a permissions exception, not 100% sure why. Simply iterating over the files to make sure they're not named '.' or '..' fixed the issue. Code below.
sftp.Connect();
var files = sftp.ListDirectory(pathRemoteDirectory);
// Iterate over them
foreach (SftpFile file in files)
{
if (!file.IsDirectory && !file.IsSymbolicLink)
{
using (Stream fileStream = File.OpenWrite(Path.Combine(pathLocalDirectory, file.Name)))
{
sftp.DownloadFile(file.FullName, fileStream);
Debug.WriteLine(pathLocalDirectory);
}
}
else if (file.Name != "." && file.Name != "..")
{
Debug.WriteLine("Directory Ignored {0}", file.FullName);
}
else if (file.IsSymbolicLink)
{
Debug.WriteLine("Symbolic link ignored: {0}", file.FullName);
}
}
sftp.Disconnect();
You have multiple problems here. The parent folder ("..") reference you answered is one blocker, but that doesn't address the deeper problem that the InstalledLocation is read-only.
UWP apps do not have direct access to most file system locations. By default they can read and write to their ApplicationData directory and they can read from (but not write to) the InstalledLocation. The failures you saw for Desktop, Documents, and C:\ are all expected.
Other locations (including Desktop, Documents, and C:) may be granted access by the user either explicitly or via the app's declared capabilities. They can be accessed via the file broker through the StorageFile object.
See the UWP File access permissions documentation:
The app's install directory is a read-only location. You can't gain
access to the install directory through the file picker.
For the long term you'll want to download your files somewhere else: probably into one of the ApplicationData folders. These folders are the only ones with no special permission requirements for UWP apps.
So why does this work for you now?
You're running into a debugging quirk where your app is not fully installed but is staged from your VS project directory. This allows the app to write to the staged install directory, but once it is properly deployed into Program Files\WindowsApps writing to the InstalledLocation will fail.
Try Path.GetTempPath();. You should have permission there.
When it says you don't have permission, you don't. 8-)
Also, there's no such thing as "no special permissions". Everything requires some level of permission for access.

Creating Files and Folders in UWP

Ive looked at at so many stackoverflow posts and articles but still didnt manage to create a file in UWP. In WPF it was really easy but UWP works differently.
I added the following in my manifest file:
<Capabilities>
<uap:Capability Name="documentsLibrary" />
</Capabilities>
Now im not sure what to do next. Inside my documents folder I have a subfolder named "Project Files". I want to create folders and files in there. How is this done in UWP? I really dont understand.
As microsoft states in their docs, its recommenced not to use the documents Library through an UWP app, instead opt for the built in storage unless its absolutely necessary.
There is an easy way to get around that if you use a folder picker
private async void buttonClick(){
FolderPicker folderPicker = new FolderPicker();
folderPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
folderPicker.FileTypeFilter.Add("*");
StorageFolder folder= await folderPicker.PickSingleFolderAsync();
if (folder != null) {
// do Things On Folder
}
else
{
MessageDialog dialog = new MessageDialog("you selected nothing");
await dialog.ShowAsync();
}
}
The above opens up a folder select dialog, it returns the folder the user picked, its the recommended way for accessing locations outside your app's folder.
Here is how to Create a new file in this folder:
string name ="myTitle.txt";
await folder.CreateFileAsync(name, CreationCollisionOption.GenerateUniqueName);
here is how to open and write a file:
try {
StorageFile myFile = await folder.GetFileAsync(name);
await Windows.Storage.FileIO.WriteTextAsync(myFile, "myStringContent");
}
catch (Exception e)
{
Debug.WriteLine("Failure: "+e.Message);
return;
}
remember you can always avoid opening up a dialog if you use the local storage instead, it returns you app's storage folder in one line, like this:
var folder= ApplicationData.Current.LocalFolder;
I believe in UWP using the documents library is neither recommended or permitted. See https://blogs.msdn.microsoft.com/wsdevsol/2013/05/09/dealing-with-documents-how-not-to-use-the-documentslibrary-capability-in-windows-store-apps/
If you side load the app and use the documents library capability the app gets access only to declared file types, not to everything in documents.
See https://msdn.microsoft.com/en-us/library/windows/apps/hh464936.aspx#special_capabilities
Note that this special capability will not let you pass through app certification in Store, unless you go through some special procedure contacting MS first.
To create folder, use StorageFolder. To create file, use StorageFile.
See https://learn.microsoft.com/en-us/windows/uwp/files/quickstart-reading-and-writing-files

C# Access to path denied

Hey guys so I'm working on a program it deletes certain directories files, mostly temp files, except I get an error even know I added a catch block. The System.UnauthorizedAccessException. on the catch ioexception I get the error there:
private void DeleteInternetFiles(string internetDirectory)
{
DirectoryInfo internetTempStorage = new DirectoryInfo(internetDirectory);
try
{
//this will delete files
foreach (FileInfo getNetFileInfo in internetTempStorage.GetFiles())
{
getNetFileInfo.Delete();
}
//this will loop through and delete folders
foreach (DirectoryInfo tempDirectoryInformation in internetTempStorage.GetDirectories())
{
tempDirectoryInformation.Delete();
}
}
//catch io exception and try delete file again
catch (IOException)
{
//delete file in this directory
File.Delete(internetDirectory);
//delete folders in this directory
Directory.Delete(internetDirectory);
}
//catch access exception and delete file again
catch (UnauthorizedAccessException)
{
//delete file in this directory
File.Delete(internetDirectory);
//delete folders in this directory
Directory.Delete(internetDirectory);
}
}
And this one below is how I call the method:
if (checkBox1.Checked)
{
DeleteInternetFiles(#"C:\Users\" + Environment.UserName + #" \AppData\Local\Microsoft\Windows\Temporary Internet Files");
}
Your second call to File.Delete(internetDirectory);, inside the catch block, seems likely to be the problem. The program has already encountered an error while trying to delete the file, and then you tried again. Two things could be happening:
The user account executing the program doesn't have permission to
delete files in another user's directory.
Some file is still in use and therefore can't be deleted (e.g.
currently open in Internet Explorer.
You might want to study the responses in C# - How to Delete temporary internet files. Note the comments about possibly having to "kill IE".
The problem I see here is that the delete action you're performing requires Administrator privileges.
What you can do is try to right click > Run as Administrator the application and then perform the action.
If you want to prompt the user to elevate your application, you can do this.
Force application to Run as Administrator [Winforms only]
You get this error because the file or folder you attempt to delete have not this access right.
It may happen in your case due to some file is being currently in use while you perform a delete operation.
There are more possibilities of file being used because you delete from a folder that windows os uses for the temporary use.

System.Exception: Shape file not found: [PATH]

When this method is invoked I get the following stack trace when it reaches OpenAsync():
System.Exception: Shape file not found:
C:\Users\Laura\Desktop\shapes\TOTALMAP\OH_Line_6600v_Expired.shp at
RuntimeCoreNet.Interop.HandleException(Boolean retVal) at
RuntimeCoreNet.CoreFeatureSource.FromShapefile(String filename) at
Esri.ArcGISRuntime.Data.ShapefileTable.OpenAsync(String filename)
at ShapeSQLiteGISDemo.MainPage.d__3.MoveNext()
I have a .dbf and .shx file in the same folder with the same name and I've been running Visual Studio in Administrator Mode.
private async void ImportShapes(object sender, RoutedEventArgs e)
{
try
{
//Get path from file picker
var picker = new FileOpenPicker { SuggestedStartLocation = PickerLocationId.Desktop };
picker.FileTypeFilter.Clear();
picker.FileTypeFilter.Add(".shp");
var file = await picker.PickSingleFileAsync();
//convert folder contents to a ShapefileTable
var shapefile = await ShapefileTable.OpenAsync(file.Path);
//save object to database
_DatabaseConnection.Insert(shapefile);
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
//call a method that loads shapes from the database
LoadDatabaseOntoMap();
}
Any help much appreciated.
I believe the problem is that with Store and UWP apps the .shp file has to be moved to a folder in the local storage before the application can open it.
Will set as the accepted answer if this solves the problem.
EDIT: This is indeed the case, because I was choosing a single file with the the file picker I only had access to that one file. To get multiple files I used the folder picker and filtered away any files that weren't useful.
Try using an app like notepad to open the file by pasting in the path:
C:\Users\Laura\Desktop\shapes\TOTALMAP\OH_Line_6600v_Expired.shp
Does the app open the file?
Are there other examples of shape files that do open? Could this file simply be corrupted?

How to Delete Files and Application Data Container Values in One Go?

I have a reset function in my app which brings the app back to default state. There for I need to remove the four files I created and remove the settings I created in the ApplicationDataContainer.
This is how I remove the files
try
{
StorageFile file = await localfolder.GetFileAsync("HistoryFile");
if (file != null)
{
await file.DeleteAsync();
}
}
catch
{
//Catch Process
}
Is there a function which removes all the files together? When I tried the following code
localfolder.DeleteAsync()
It removed the LocalState folder along with the files, I jus need to remove the files not the folder.
And is there anyway in which I jus can remove all the values stored in ApplicationDatacontainer in one go? rather than removing them one by one like this?
localSettings.DeleteContainer("exampleContainer");
If you want to remove application data from its local data store, try this.
await Windows.Storage.ApplicationData.Current.ClearAsync(
Windows.Storage.ApplicationDataLocality.Local);

Categories