views:

929

answers:

4

I am trying to create a utility in C# using the MVC framework where a user uploads a picture which is used to crop pieces out of to be used as thumbnail icons (only one user at a time will be doing this at any given time). The user can then upload a different picture and continue cropping.

I have a controller action that handles the file upload taking the picture, converting it from whatever format it's in to a jpeg and saving it as "temp.jpg" (I know it's not a thin controller, but this is just for testing purposes). When they upload the next picture I want to replace that temp.jpg with this new image. This controller works fine in development on my machine, but in production after the user uploads the first image and tries to replace it with another image they get the following error:

"the process cannot access the file because it is being used by another process"

It seems to me the the "temp.jpg" file is being locked after the first upload and I can't figure out how to avoid this.

Any suggestions or alternative ideas are welcome.

Break down of what my code does:

  • Checks if the picture to be uploaded exists on the server and deletes it if found
  • Saves the picture as is with it's original file name and extension
  • Checks for the "temp.jpg" file and deletes it if found
  • Opens the original image in a System.Drawing.Image object to convert to jpeg
  • Save it as a new "temp.jpg" to replace the deleted file.

My Code:

        [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult PicUpload(DateTime sd, FormCollection collection)
    {
            foreach (string file in Request.Files)
            {
                HttpPostedFileBase hpf = Request.Files[file] as HttpPostedFileBase;
                    if (hpf.ContentLength == 0)
                        continue;
                    string savedFileName = Path.Combine(
                       AppDomain.CurrentDomain.BaseDirectory + "Content\\AdContent\\",
                       Path.GetFileName(hpf.FileName));

                    FileInfo temp = new FileInfo(savedFileName);
                    if (temp.Exists) temp.Delete();

                    hpf.SaveAs(savedFileName);

                    string tempFileName = AppDomain.CurrentDomain.BaseDirectory + "Content\\AdContent\\temp.jpg";

                    temp = new FileInfo(tempFileName);
                    if (temp.Exists) temp.Delete();

                    EncoderParameters codecParams = new EncoderParameters(1);
                    codecParams.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
                    ImageCodecInfo[] encoders;
                    encoders = ImageCodecInfo.GetImageEncoders();

                    Image newPic = Image.FromFile(savedFileName);
                    newPic.Save(tempFileName, encoders[1], codecParams);
                    newPic.Dispose();

                    FileInfo tmp = new FileInfo(savedFileName);
                    if (tmp.Exists) tmp.Delete();
                    return RedirectToAction("Create", new { startdate = String.Format("{0:MM-dd-yyyy}", sd) });
            }
            return RedirectToAction("Create", new { startdate = String.Format("{0:MM-dd-yyyy}", sd) });
    }
A: 

You can use a uniquely named temporary file for tempFileName instead of "temp.jpg." System.IO.Path has a GetTempFileName() method that you can use for this.

Another option would be to use the GetRandomFileName() method (also in System.IO.Path) which returns a random path or filename, then it's just a matter of making a small change to your code:

- string tempFileName = AppDomain.CurrentDomain.BaseDirectory + "Content\\AdContent\\temp.jpg";
+ string tempFileName = AppDomain.CurrentDomain.BaseDirectory + "Content\\AdContent\\" + System.IO.Path.GetRandomFileName();
Sean Bright
This may work, although I'd have to store the file name and have my View reference it.
hotbot86
That is most likely what you will have to end up doing.
Sean Bright
One question though, if the file is getting locked, even if I get a random file name, wouldn't that file still be locked when I try to clean it up later after the user has uploaded a new file with a new random name?
hotbot86
+1  A: 

System.Drawing.Image.Save() cannot save to the same location the file was opened from:

From the docs: "Saving the image to the same file it was constructed from is not allowed and throws an exception."

John Sheehan
I'm not saving it to the same location. I edited my question to make what my code is trying to do more clear. In short I save the file, then try to create a copy named "temp.jpg" but if that file's already locked from the previous upload I can't delete it to overwrite it.
hotbot86
+2  A: 

It turns out I was not freeing the resources of the image properly in another section of my code that was returning width and height properties of the image. Also instead using the Dispose(); method, I put the code into a "using block" instead like so:

            using (Image newPic = Image.FromFile(savedFileName))
            {
                newPic.Save(tempFileName, encoders[1], codecParams);
            }

This worked like a charm and I can now delete the temp.jpg file with impunity.

hotbot86
+1  A: 

Use a "panel" control , and save it as BMP , PNG ,whatever

Alynuzzu