File remains in use until client refreshes page - c#

I am attempting to delete image files from the server file system but I get a 'file in use by another process' error. I am trying to delete the image and it's associated thumbnail in one fowl swoop. I can get one to delete but the other will try and fail forever, as far as I've seen. However, if I refresh the page while it's looping through a try-catch, the operation succeeds immediately. I am accessing the files via ajax requests and API controllers (my site is mostly one page) when I load the page, but I am doing so with 'using' statements. This should close the handle after use, but it seems to be staying open. I have to get the files on the client side to be able to have the option to delete them(browser content manager), so I don't see how they are still in use. I load the thumbnail and use it's ID to get it's name from the database, and use the name to find it in the file system.
I have tried putting a try-catch in a loop and waiting for the last process to finish, but it never does, until I refresh the page.
I also tried deleting one at a time with separate ajax requests, but it seems that if I have opened a file at all, I can't delete it (until I refresh the page).
Code for deleting file (method 1):
public static void DeleteFromFileSystem(string path)
{
for (int i = 1; i <= 10000; ++i)
{
try
{
System.IO.File.Delete(path);
break;
}
catch (IOException e) when (i <= 10000)
{
Thread.Sleep(3);
}
}
}
Code for deleting file (method 2):
public static void DeleteFromFileSystem(string path)
{
using(FileStream f = System.IO.File.OpenWrite(path))
{
System.IO.File.Delete(path);
}
}
Code for getting image (works fine, but could be the problem):
public static byte[] ImageToByte(string name, bool getThumbnail)
{
if (name == null) name = "user.png"; //default profile picture
string path = getThumbnail ? "wwwroot/ImgThumb/" : "wwwroot/ImgFull/";
// this line is my best suspect
System.Drawing.Image img = System.Drawing.Image.FromFile(path + name);
byte[] b;
using (MemoryStream mStream = new MemoryStream())
{
img.Save(mStream, img.RawFormat);
b = mStream.ToArray();
mStream.Close(); // I added this as a precautionary. It didn't help.
}
return b;
}
I expect that the images get deleted, but I get the 'file X cannot be accessed because it is in use by another process' error.

SOLVED - I added img.Dispose() after the using statement.

Related

Problem in overwriting Application.persistentDataPath

I have a JSON file which contains data for my Localization in my game. The file is stored in Application.persistentDataPath . I read in the documentation that the file is not deleted or overwritten when updating the game. Currently I launch the app and the file is there but when I add more content and send an update to save again, it only has the original content.
But is there a way to update this file when I update the game?
What if in the future I want to add more content to this JSON file, I would have to delete it and upload it again?
Any way to update and overwrite it?
private static void Request(string file)
{
var loadingRequest = UnityWebRequest.Get(Path.Combine(Application.streamingAssetsPath, file));
loadingRequest.SendWebRequest();
while (!loadingRequest.isDone)
{
if (loadingRequest.isNetworkError || loadingRequest.isHttpError)
{
break;
}
}
if (loadingRequest.isNetworkError || loadingRequest.isHttpError)
{
// Some error happened.
}
else
{
File.WriteAllBytes(Path.Combine(Application.persistentDataPath, file), loadingRequest.downloadHandler.data);
}
}
// Loads key from "StreamingAssets/language_data.json" file.
public static string LoadJson(string key)
{
Request("data.json");
var jsonPath = Path.Combine(Application.persistentDataPath, jsonFileName);
using (StreamReader r = new StreamReader(jsonPath))
{
var N = JSON.Parse(r.ReadToEnd());
string result = N[GetLanguage()][key];
return result;
}
}
I think the issue is the following:
You are only creating a file named data.json NOT languages_data.json!
In Request (you pass in "data.json") you copy
Application.streamingAssetsPath, file
to
Application.persistentDataPath, file
where file = "data.json"
Then in LoadJson you are trying to load values from
Application.persistentDataPath, jsonFileName
where - as to your comments - jsonFileName = "languages_data.json"
=> It is a different path!
In the Editor/on your PC you probably still have an old languages_data.json in the persistent data from some time ago. Therefore there is no error but it is also never updated. Delete the persistent/languages_data.json on your PC and it will throw a FileNotFoundException.
To solve this make 100% sure the file names are the same everywhere. The simplest fix code wise would be to simply rather use
Request(jsonFileName);
and make sure that jsonFileName matches the name of your existing file in StreamingAssets.

StreamReader Could not find file (Because it is not being created)

I am trying to create a file for the further write to and read from.
I use Directory.CreateDirectory and File.Create but neither path nor file are being created.
On the Page which part I show here below I check if File exists, and if not, I create a File. On Second Page (that I dont show here) I add new lines to the file using StreamWrite. When saved, first Page comes to focus again and lists the content of the File(only one row in this study).
Here is my code for the part in question:
public async Task ReadFileAsync()
{
string directoryName = Path.GetDirectoryName(#"C:\Users\...\DataBase\");
Task.Run(async() => Directory.CreateDirectory(directoryName));
Task.Run(async() => File.Create(directoryName + "ProductsDatabase.txt"));
//code for reading from file
string path = (directoryName + "ProductsDatabase.txt");
using (StreamReader ProductsDatabaseRead = new StreamReader(File.OpenRead(path)))
{
ProductOneTextBlock.Text = ProductsDatabaseRead.ReadLine();
}
if (ProductOneTextBlock.Text == "")
{
ProductOneTextBlock.Text = "Nothing to show";
}
}
The file and folder are not being created.
I don't get any error.
I tried also different folders on the drive in case if there was READ ONLY folder in solution folder. No difference.
Anyone could help?
(I found many threads about this problem but here I cannot resolve it with none of the solutions.
Physical file is not being created.
When I attempt to write to it (from another page) I get error that the file could not be found(because it is not there indeed).
It seems that program loses itself somewhere between
Task.Run(async() => Directory.CreateDirectory(directoryName));
Task.Run(async() => File.Create(directoryName + "ProductsDatabase.txt"));
and:
using (StreamReader ProductsDatabaseRead = new StreamReader(File.OpenRead(path)))
{
ProductOneTextBlock.Text = ProductsDatabaseRead.ReadLine();
}
, as TextBlock is not being updated even if ProductsDatabaseRead is null.
If I put
ProductOneTextBlock.Text = "Nothing to show";
a the begining of the method, TextBlock gets updated.
SO, why the
using (StreamReader ProductsDatabaseRead = new StreamReader(File.OpenRead(path)))
does not work?
You're not waiting for Task.Run to complete. Your directory creation, file creation and attempt to open a "as you think newly created file" are out of order. That's why you're probably not able to open a file (it still does not exist at this point).
Task.Run returns a task that will be completed when the work is done. You need to wait for completion.
public void ReadFile()
{
string folderPath = #"C:\Users\patri\source\repos\DietMate\";
string fileName = "ProductsDatabase.txt";
string fullPath = Path.Combine(folderPath, fileName);
//insert code to check whether file exists.
// use Exists()
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
File.Create(fullPath);
}
//if yes, follow with code below
//insert code for reading from file
using (StreamReader ProductsDatabaseRead = new StreamReader(fullPath))
{
ProductTest.Text = ProductsDatabaseRead.ReadLine();
}
}

The action can't be completed because the file is open in IIS Worker Process

I am uploading a image for a user & saving it on a specified location. The code is working fine in normal scenario i.e. user selects a image & save it, it works.
Challenge occurs when user selects a image( probably wrong image due to mistake) & don't save it, but again selects a new image & then saves it.
This scenario gives me the error :
"The process cannot access the file because it is being used by another process."
When I try to delete image from the location at the time of error, file can't be deleted with message:
"The action can't be completed because the file is open in IIS Worker Process
Close the file and try again."
Code is like this:
try
{
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
msf = new MemoryStream();
bytes=FileUpload1.FileBytes;
msf.Write(bytes, 0, bytes.Length);
using (FileStream stream = new FileStream(folder + "/" + filename, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
{
//converting any graphic file type to jpeg format
Image img = Image.FromStream(msf);
img.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
msf.WriteTo(stream);
IsuploadSuccess = true;
img.Dispose();
}
}
catch
{
IsuploadSuccess = false;
}
finally
{
if (msf != null)
{
msf.Close();
msf.Dispose();
}
}
I have tried adding "FileAccess.ReadWrite" & "FileShare.ReadWrite" in file stream, but doesn't work either with all options in File stream.
Please help...
Instead of getting solution of problem, I finally change approach to get rid to source of challenge. I change file name with UserID (unique) appended with CurrentDateTime converted to string & store it to temp folder until user save the change. This forms a different file each time for the challenge scenario. After save each file in temp folder created by the user (preceded by unique userid) is deleted & last changes are saved to respective directory.
I had this problem and took me few hours to figure it out.
I my case the Bitmap new(path) had locked the file. I had to dispose of it before exiting the using block.
However it is the method by which I debugged it is important.
My approach in pinpointing the source of the problem was to open the folder where the image was saved and tracing the code line by line. After each line I deleted the file and did Ctrl+Z to put it back. If it gave an error when I tried to delete it, then that is the line that locked it. So I got to that line within 5 min.
using (MemoryStream memoryStream = new())
{
if (System.IO.File.Exists(path))
{
using(Bitmap bitmap = new(path)) <-- this solved it
{
ImageConverter imageConverter = new();
bitmap.Save(memoryStream, ImageFormat.Bmp);
bytes = memoryStream.ToArray();
}
}
}

Deleting a bitmap from local directory

Alright it has come to this. I searched this website among many others and no one can seem to give me a straight answer so I'm going to try just asking outright. Been on this issue for about a solid 3 days and I can't afford to waste any more time on it.
Goal: The app I am building is in WPF and is going to be used as a bug tracker for a project my design team and I will be undertaking soon. Since we are going to be building a game in C++ most of the errors that occur will have a visual element to them so I inlcuded functionality to provide an image of the error in question when the user adds a bug to the list. I then take that image and save it to a local directory (for testing). Now the image path in the Error object points to a path that leads to the local directory. This functionality has been tested and works fine. My problem showes up when I want to delete a bug from the list. I am getting that very infamous "IO Exception" saying that the image I want to delete is being used by another process.
So Far: At first I tried very elegant solutions, but as with all things you get to a point where you just want to see if you can get the thing to even work at all. So I am at the point where most of the code I am using is experimental and radical. So please when looking at it note that the code being used is out of desperation, so any "simple" solutions have probably already been tried (I did research this a lot becuase I hate having to do this). Things i can think of off the top of my head are the obsurd amount of disposes and forced garbage collections being called so please to not comment on the negative nature of this practice, I am well aware :).
The Code
Saving image to local directory
public void OnBrowseClick()
{
Microsoft.Win32.OpenFileDialog openBox = new Microsoft.Win32.OpenFileDialog();
// Show dialog box to user and grab output
Nullable<bool> result = openBox.ShowDialog();
if (result == true)
{
// Create temp variable to hold local path string
string localPath = Directory.GetCurrentDirectory();
// Grab the extension of the specified file path
string extension = openBox.FileName.Substring(openBox.FileName.LastIndexOf("\\"));
// Add extension to local path
localPath += extension;
// Create local copy of image at given file path (being ridiculous at this point)
using (Stream stream = new FileStream(openBox.FileName, FileMode.Open, FileAccess.ReadWrite))
{
using (Bitmap bmp = LoadImage(stream))
{
using (Bitmap temp = (Bitmap)bmp.Clone())
{
temp.Save(localPath);
temp.Dispose();
}
bmp.Dispose();
}
stream.Dispose();
}
// Set the URL in the image text box (UI stuff)
LocalError.ImagePath = localPath;
}
}
The following is the LoadImage function that is used in the function above
private Bitmap LoadImage(Stream stream)
{
Bitmap retval = null;
using (Bitmap bitmap = new Bitmap(stream))
{
retval = new Bitmap(bitmap.Width, bitmap.Height, bitmap.PixelFormat);
using (Graphics gdi = Graphics.FromImage(retval))
{
gdi.DrawImageUnscaled(bitmap, 0, 0);
gdi.Flush();
gdi.Dispose();
bitmap.Dispose();
}
}
// Garbage collection here to be safe
GC.WaitForPendingFinalizers();
GC.Collect();
return retval;
}
And finally we come to where I try to delete the image
public void OnDeleteClick()
{
// Ask user to make sure they want to delete selected item(s)
MessageBoxResult result = MessageBox.Show("Are you sure you want to delete selected item(s) from the list?",
"Delete", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
for( int i = 0; i < Parent.ErrorListControl.ErrorDataGrid.SelectedItems.Count; ++i)
{
// Get path to image
string path = (Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i] as Error).ImagePath;
// Even tried calling garbage collection here!!!!!
System.GC.WaitForPendingFinalizers();
System.GC.Collect();
File.Delete(path);
// Remove the error from the error list
Parent.ErrorListVM.ErrorList.Remove((Error)Parent.ErrorListControl.ErrorDataGrid.SelectedItems[i]);
// Decrement counter because we altered the list while in a loop
i--;
}
}
}
Notes: If anyone would like me to explain anything further or if you need to know something I left out please just ask I will get back to you ASAP! Any suggestions are helpful at this point I have absolutley no idea what I am doing wrong. I generally only program in a C++ environment so I tend to manage my own memory this whole "garbage collection" thing is really throwing a wrench in our project! (Off topic note: I do not know why I am not getting any color highlighting so I apologize to anyone who takes the time to read this).
Here's a simple way to do what you want. In this example, I'm using Path.GetTempFileName() to generate a random file name in the local user's temp directory. If you don't need to persist the files then it's a good place to store them temporarily. Also, the user could theoretically import two files with the same name. So you want to use some kind of random filename generation or other mechanism to avoid conflicts.
private void browseButton_Click(object sender, RoutedEventArgs e)
{
var openFileDialog = new Microsoft.Win32.OpenFileDialog();
if (openFileDialog.ShowDialog(this) == true)
{
using (Bitmap originalImage = new Bitmap(openFileDialog.FileName))
{
string tempFileName = System.IO.Path.GetTempFileName();
originalImage.Save(tempFileName);
// LocalError.LocalPath
LocalPath = tempFileName;
}
}
}
private void deleteButton_Click(object sender, RoutedEventArgs e)
{
if (File.Exists(LocalPath))
{
File.Delete(LocalPath);
}
}
Although a simple File.Copy should suffice as long as you have the right paths, I was just providing a solution that matched your question.
EDIT:
Actually the current directory does not seem to be changed by the OpenFileDialog. I could swear that it did at some point. So I don't think this is your problem. Regardless, this code still works for me and you shouldn't require anything more complicated than this.
EDIT #2:
It seems the lock is actually caused by the image being databound to the view and presumably locked by the BitmapSource. You should be able to create it without locking the file. Generally, this is slower so don't do it this way unless you need to be able to modify or delete the file.
bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.CacheOption = BitmapCacheOption.OnLoad;
bitmapSource.CreateOption = BitmapCreateOptions.IgnoreImageCache;
bitmapSource.UriSource = new Uri(ImagePath, UriKind.Absolute);
bitmapSource.EndInit();
Since your LoadImage method does simple copy of the image, why not use File.Copy(source, dest) and avoid all the bitmaps, drawings, etc? Your goal might be to modify local bitmap after it's created, but it can still be done after copy.
Also, when using the using block, explicit .Dispose() is not required, as using block does it for you:
using (var obj = new SomeDisposableObject())
{
// code here
// obj.Dispose(); <-- not needed, since...
} // ...at this point obj.Dispose is called automatically.

Manipulating freshly uploaded file causes IOException

My Asp.net MVC app requires a file upload. In the course of the upload I'd like to manipulate the freshly uploaded file.
public ActionResult Edit(int id, FormCollection collection) {
Block block = userrep.GetBlock(id);
foreach (string tag in Request.Files) {
var file = Request.Files[tag] as HttpPostedFileBase;
if (file.ContentLength == 0)
continue;
string tempfile = Path.GetTempFileName()
file.SaveAs(tempfile);
// This doesn't seem to make any difference!!
// file.InputStream.Close();
if (FileIsSmallEnough(file)) {
// Will throw an exception!!
File.Move(tempfile, permanentfile);
} else {
GenerateResizedFile(tempfile, permanentfile);
// Will throw an exception!!
File.Delete(tempfile);
}
block.Image = permanentfile;
}
userrep.Save();
The problem with this snippet is that any attempt to manipulate the initially uploaded file generates an IOException("The process cannot access the file because it is being used by another process.") Of course I can bypass the problem by copying rather than moving uploaded file but I still can't delete it once I have a better alternative.
Any advice?
Duffy
As you have mentioned in your comments, you load an Image from file. The MSDN documentation states that the file remains locked until the image is disposed.
http://msdn.microsoft.com/en-us/library/stf701f5.aspx
To dispose your image, you can either call the Dispose method on the instance, or use the preferred mechanism of the using statement:
private bool FileIsSmallEnough()
{
using (Image i = Image.FromFile())
{
}
}
This should solve the problem.

Categories