File.Create is creating a directory and not a file - c#

As a short summary, I have a string constant for a file location that includes the file name and extension such as #"C:\foldername\subfolder\filename.json. When calling File.Create from System.IO, it's creating a directory rather than the file maybe 5% of the time.
Does anyone have any insight as to what needs done different to prevent this?
if (File.Exists(fileName))
{
return File.GetLastWriteTime(fileName);
}
else
{
try
{
File.Create(fileName).Close();
return File.GetLastWriteTime(fileName);
}
catch (Exception ex)
{
Logging.sharedLogging.Log(SharedLogging.LoggingLevel.Error, "[CacheDirectoryLogic.GetOverridesLastWriteTime] Failed to create Override.json. Reason: " + ex.Message);
return null;
}
}
where fileName is #"C:\WD\Data\Cache\Override.json";

I think there should be some exception thrown, but your catch block will just log somewhere and ignore the error, so you may want to check your logger to see any exception mentioned. It will be helpful for us to understand the issue.
There are few possible root causes for the issue.
1. Permission issue
I notice the file path is in C drive. If the directory requires admin privilege to write files in the folder, it may throw access denied exception.
Tried to change the file path to D drive, which less likely has admin privilege restriction.
2. Directory is not fully created
Better practice for file creation with a full path -- check for directory existence before creating the file.
File.Create() does not create directory automatically. And it will throw error if directory is not found.
Code sample:
var directory = #"C:\WD\Data\Cache\";
var fileName = Path.Combine(directory, "Override.json");
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
if (File.Exists(fileName))
...

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.

UWP StorageFolder access to the Downloads folder

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;
}

System.IO.File.Copy never overwrites

In my WinForm I would like to copy/overwrite files.
When the destination file does not exist, the file gets created OK. When the file already exists, it never gets overwriten. Path is on my local computer.
There are no exceptions thrown, and I gave full access to "Everyone". Same issue with FileInfo. Only when I delete the file first it gets created!
My Code:
//File.Delete(path + "gauche.png");
try
{
// FileInfo fi = new FileInfo(Path.Combine(path, Num_Gauche.Value + ".png"));
//fi.CopyTo(Path.Combine(path, "gauche.png"), true);
File.Copy(Path.Combine(path, Num_Gauche.Value + ".png"), Path.Combine(path, "gauche.png"), true);
}
catch (Exception ex )
{
Console.WriteLine(ex.Message);
throw;
}
It could be an ownership issue. If the file was created by another user, your process may not be able to overwrite it. Usually like if the file was created by a installer/MSI that you may have issue to overwrite it by another process.
I will just enable that piece of code to delete the file prior to create it if that works.

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.

Wait for .zip file to extract before copying it's contents elsewhere programmaticaly

I'm trying to write a small console app tool that unzips an archive containing multiple files/folders/other archives and arrange it's contents in another way.
I unzip the root file with ZipFile.ExtractToDirectory method from System.IO.Compression.FileSystem library:
public static void UnzipPackage(string packagePath, string targetPath)
{
try
{
ZipFile.ExtractToDirectory(packagePath, targetLocation);
Console.WriteLine("Unzipping file {0} complete.", packagePath);
}
catch (DirectoryNotFoundException)
{
Console.WriteLine("Directory was not found.");
}
catch (FileNotFoundException)
{
Console.WriteLine("File was not found.");
}
}
After running this method on my package, I want to copy a file which was in this package in a subfolder.
According to MSDN I do this:
if (!Directory.Exists(targetLocation + #"READY\PHOTO"))
{
Directory.CreateDirectory(targetLocation + #"\READY\PHOTO");
}
if (Directory.Exists(targetLocation + #"\MAIN\PHOTO"))
{
string[] files = Directory.GetFiles(targetLocation + #"\MAIN\PHOTO");
foreach (var file in files)
{
string fileName = Path.GetFileName(file);
string destFile = Path.Combine(targetLocation + #"\MAIN\PHOTO", fileName);
File.Copy(file, destFile, true);
}
}
Both MAIN and READY are my subdirectories where whole package goes ("main") and sorted files go ("ready").
However, when running this, the zip file is not yet unzipped - an exception occurs showing it can't access the file specified even though it grabbed it's name from Directory.GetFiles(). The folder created when unzipping the zip file shows only after I terminate my console app (no wonder it can't access it).
So the big question is - how can I wait for the unzipped folder to show up? I tried using Thread.Sleep(), but it doesn't affect the flow anyhow - an exception still occurs, and the folder shows only after I terminate the app.
Your error is here:
string destFile = Path.Combine(targetLocation + #"\MAIN\PHOTO", fileName);
Should be:
string destFile = Path.Combine(targetLocation + #"\READY\PHOTO", fileName);
You're attempting to copy the file to the same location.
I'm assuming your getting an IOException something like "The process cannot access the file...because it is being used by another process."
It looks to me that there is a problem in your copy method. It looks like your from and to paths are essentially the same. So the system cannot overwrite the file, because you currently have it open for reading.
Just to be clear - the issue is not related to unzipping! In the example you have written the variable file and destFile are going to be the same - and they need to be different.

Categories