How to Handle Multiple Image Uploads in ASP.NET MVC Website? - c#

I am using dropzone.js to upload multiple images to a .NET MVC website. When the images come into the server, I resize them, save information to the database, and then save the actual image on the server in a folder. When I upload them one at a time this works, but if I do multiple uploads at once, I get this error every few uploads:
A generic error occurred in GDI+
Googling this seems that this is a generic error when you are unable to save a file as some other process is using the location. I assume that the multiple uploads are trying to save in the same folder at the same time and one dies. They are not the same filename.
How can I avoid this? Is there a way to have the threads wait for one to finish before it tries saving in the same folder? I'm wondering if there is a method that is like the await call where it waits until it can save.
Edit More Code
I can't copy my whole function as it's very long etc but the part that saves is the following:
//Now we will try saving the actual file. Generate the file name.
String savedFileName = PhotoTools.GenerateImageFileName(photo.image_base_file_name);
String thumbnailSavedFileName = PhotoTools.GenerateImageFileName(photo.image_base_file_name, true);
//Store the file path (directory) that we are going to save the image to.
String directoryToSave = Server.MapPath(Constants.ImageDirectory + $"/{house_id}");
//Create the directory. This function will not do anything if it already exists.
Directory.CreateDirectory(directoryToSave);
//Save the images to the filesystem.
mainImage.Save(Path.Combine(directoryToSave, savedFileName), ImageFormat.Jpeg);
thumbnailImage.Save(Path.Combine(directoryToSave, thumbnailSavedFileName), ImageFormat.Jpeg);
They should not be the same filename as the file name is created as such:
/// <summary>
/// Generates the base property image filename based on the passed in information.
/// </summary>
/// <param name="propName">The house name that we will use for the file name.</param>
/// <returns>The generated file name.</returns>
public static String GenerateBaseImageFileName(String propName)
{
//Save the current datetime to create the filename with.
DateTime now = DateTime.Now;
//Generate the name.
return $"{CommonTools.RemoveSpecialCharacters(propName).Replace(" ","-")}-{now.Hour}{now.Second}{now.Millisecond}";
}

Usually this error A generic error occurred in GDI+ mean that the file or the path doesn't exist:
so try for example to check if the directory exist or create it if not .. and then double check if you have already a file with same name or if the full path exist:
try:
var guid = Guid.New().ToString(); //<-- generate new random guid
//Store the file path (directory) that we are going to save the image to.
String directoryToSave = Server.MapPath(Constants.ImageDirectory + $"/{house_id}");
//Create the directory. This function will not do anything if it already exists.
if(!Directory.Exist(directoryToSave)
Directory.CreateDirectory(directoryToSave);
//Save the images to the filesystem.
mainImage.Save(Path.Combine(directoryToSave, savedFileName + "_" + guid), ImageFormat.Jpeg);
Hope it helps you

Related

Spax EA best way to export diagrams into Word document

Got an old module which is generates reports with data from sparx ea project.
There is a part where you need to insert diagrams as pictures in the document.
Now it looks like that
public static void copyDiagram(
EA.Diagram diagram,
EA.Repository eaRepository)
{
eaRepository.App.Project.PutDiagramImageOnClipboard(diagram.DiagramGUID, 0);
eaRepository.CloseDiagram(diagram.DiagramID);
}
copying it to clipboard, and after that there goes something like
currentDocumentRange.Paste()
Looks strange for me.
I think it's not really good to use clipboard like that, so I want to rewrite it in future.
So, only other function I found there looks like that PutDiagramImageToFile(diagrammGUID, path, type)
If there are no better option is it okay to create new file, after that get it by it's path insert into word document, and then delete it?
Or, maybe there are some other SparxEA function, which get image from diagram in byte[] format or like Image format?
What way is better?
I'm using this code (on a diagram wrapper class) to get the image of a diagram without having to use the clipboard.
This code is used primarily in a custom written document generator and is surprisingly fast.
/// <summary>
/// returns diagram image
/// </summary>
public Image image
{
get
{
EA.Project projectInterface = this.model.getWrappedModel().GetProjectInterface();
string diagramGUID = projectInterface.GUIDtoXML(this.wrappedDiagram.DiagramGUID);
string filename = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".png";
//save diagram image to file (format ".png")
projectInterface.PutDiagramImageToFile(diagramGUID, filename, 1);
//load the contents of the file into a memorystream
MemoryStream imageStream = new MemoryStream(File.ReadAllBytes(filename));
//then create the image from the memorystream.
//this allows us to delete the temporary file right after loading it.
//When using Image.FromFile the file would have been locked for the lifetime of the Image
Image diagramImage = Image.FromStream(imageStream);
//delete the temorary file
System.IO.File.Delete(filename);
return diagramImage;
}
}

C# - Unable to close file, resulting in 'file being used in another process' Exception [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have a program that searches through a folder at a given path, and pulls out the paths of any files inside the folder or any subfolders inside it.
From here, it uses the individual file path to create an object of a custom class called ImageData. The method that handles this is shown below:
public void saveLatestImages(string chosenPath)
{
//if there is a Pictures folder
if (Directory.Exists(chosenPath))
{
//get number of files in folder
int fileCount = Directory.GetFiles(chosenPath).Count();
//more than one file in folder
if (fileCount > 0)
{
//create data structures to store file info
//filePaths holds path of each file represented as a string
string[] filePaths = Directory.GetFiles(chosenPath);
//for each file in Pictures...
for (int index = 0; index < fileCount; ++index)
{
//get name of image at current index
imageName = filePaths[index];
//separate the part relating to the patient name (everything before (DD/MM/YYYY))
string subSpecifier = imageName.Split('\\').Last();
subSpecifier = subSpecifier.Split('_')[0];
//add to root directory to form subfolder name
subDirectory = Path.Combine(rootDirectory, subSpecifier);
//subdirectory name formulated, check for pre-existing
//subfolder does not exist
if (!Directory.Exists(subDirectory))
{
//create it
Directory.CreateDirectory(subDirectory);
}
//otherwise, file will be added to existing directory
//take everything from end and folder\file division to get unique filename
string fileName = imageName.Split('\\').Last();
//add this to the existing subDirectory
fileName = Path.Combine(subDirectory, fileName);
//copy the image into the subfolder using this unique filename
File.Copy(imageName, fileName, true); //true gives instruction to overwrite any existing file with the same path
//construct new instance with created filename
imageData.Add(new ImageData(fileName));
}
}
}
}
So far, so good.
The problem comes afterwards when the ImageData object created is being displayed on a PictureBox (using a Bitmap attribute). When this image is on the picture box, a number of options are available through buttons.
For example, one button is in place that would remove the ImageData object from the picturebox and delete the file.
This is done using the method below:
private void btnDeleteImage_Click(object sender, EventArgs e)
{
///////////////////////////////////////////////////////////////////////////////////
//imageData is List<ImageData> that contains all ImageData objects currently in use
//imageSlider is the PictureBox where the images are displayed/////////////////////
///////////////////////////////////////////////////////////////////////////////////
//identify image currently on picturebox
Image displayImage = imageData[displayImageIndex()].getThumbnailImage();
//get the file path of this image
string displayImagePath = imageData[displayImageIndex()].getImagePath();
//move to next or previous image in list
//then remove image that was just viewed
//current image not last in list
if (!(imageSlider.Image.Equals(imageData.Last().getThumbnailImage())))
{
displayImage = imageData[displayImageIndex() + 1].getThumbnailImage();
//display the next image in the list
imageSlider.Image = displayImage;
//delete the image just moved on from from list
imageData.RemoveAt(displayImageIndex() - 1);
//delete the file path at this index in the paths list
File.Delete(displayImagePath);
}
//current image is last in list
else
{
displayImage = imageData[displayImageIndex() - 1].getThumbnailImage();
//display previous image in list
imageSlider.Image = displayImage;
//delete the image just moved on from from list
imageData.RemoveAt(displayImageIndex() + 1);
//delete the file path at this index in the paths list
File.Delete(displayImagePath); <--- ////ERROR OCCURS////
}
//check for prior and successive elements in list
checkFirstLast(displayImage);
updateImageInfo();
}
On the File.Delete() command, an exception occurs to inform me that the 'File cannot be accessed because it is being used by another process'.
Basically, the file is opened when it is brought into the program, and is never closed. This means that when I try to access the file to delete (or do something else with it), this cannot be done as the program currently stands.
I know that if I was using a FileStream object then I could call the .Close() method once the object was finished with. But seeing as all file access is done using string variables which are then used to create images, there does not seem to be an equivalent method available to me.
Does anyone know of any other way to implement this behaviour? If this is not going to be possible, is it possible to manage the image files using something like FileStream?
Any advice on where to go from here would be great.
Thanks,
Mark
As stated in my comment, you're probably creating Bitmap objects in your ImageData class, using the constructor that takes a string (the filename):
Bitmap b = new Bitmap(filename);
A Bitmap created with this constructor will create a FileStream from the file at the given path and will keep that FileStream open until the Bitmap is disposed, as mentioned in the documentation:
The file remains locked until the Bitmap is disposed.
To get around this, you can instead build the Bitmap from a MemoryStream that you populate from the file yourself:
byte[] data = File.ReadAllBytes(filename);
MemoryStream stream = new MemoryStream(data);
Bitmap b = new Bitmap(stream);
This way, the stream the Bitmap keeps open is the MemoryStream you created rather than a FileStream that keeps the file locked.
The usual norm is -
Create a staging folder. Like a temp folder
Make a copy of your file there
Display the image from that temp file
For each operation make a copy (1.jpg, 2.jpg)
Finally when done, copy the latest final file back to the original folder
Delete staging folder
Advantages -
Original data is never lost
Accidental corruption is very less
Easy to have undo operations
If you can't delete staging folder immediately, you can background job for this

Using an image in the resource folder in a function which takes file path to image as parameter

I'm developing a winform application. It has a reference to a dll library. I want to use a function 'PDFImage' in this library. This function is used to put images into a PDF documnent. The function 'PDFimage' has an argument 'FileName' of type String which takes the file location of the image.
Now I have to put the image as a separate file with the .exe file created after the project is built. This is not convenient for me. What I do now is I mention the file name of the image as the function parameter like 'Flower.jpg'. And I have kept the image in the \bin\debug folder.
I don't want to do it like this as this needs the image file to be placed seperately with the executable file.
What I am trying to do is as follows:
I added the image files to the Resources folder as existing item. Now, to call the function PDFImage, I need to pass the file name as argument. How can I do this?
I have the source code of dll with me. Is it better to modify the source code as required and create another dll rather than what I am doing now?
See if this helps;
string apppath = Application.StartupPath;
string resname = #"\Resource.bmp";
string localfile = "";
localfile = apppath + resname;//Create the path to this executable.
//Btw we are going to use the "Save" method of Bitmap class.It
//takes an absolute path as input and saves the file there.
//We accesed the Image resource through Properties.Resources.Settings and called Save method
//of Bitmap class.This saves the resource as a local file in the same folder as this executable.
Properties.Resources.Image.Save(localfile);
MessageBox.Show("The path to the local file is : " + Environment.NewLine + localfile + Environment.NewLine +
"Go and check the folder where this executable is.",
this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
//localfile is the path you need to pass to some function like this;
//SomeClass.Somefunction(localfile);
Hope this helps and here is a sample if you need.
All you can do with that is get the resource, save it to a file (temporary one may be) and then pass the filename to the function. Most function that take a file in .net also take a stream, so if you have control of both sides, I'd do that and then you don't have to mess about with the file system.

Creating Image object using System.Drawing.Image.FromFile

I am trying to get the image dimensions of an image that user selects from list box. Image files are available on FTP server. I am displaying file names in a list box for users to select. Upon selection, I want to show the preview of image, for that I want to get dimensions so that I can resize it if i need to.
I am storing file name that is linked to currently selected list item into a string variable. I know that path on the server. I am using following code to create the Image object, but having no luck
try
{
string dir = Session["currentUser"].ToString();
System.Drawing.Image img = System.Drawing.Image.FromFile("~/Uploads/"+dir+"/"+fName, true); //ERROR here, it gives me file URL as error message!
}
catch(Exception ex)
{
lbl_Err.Text = ex.Message;
}
Not sure what is going wrong. Any ideas?
use Server.MapPath to fetch the image from the server.
As follows
System.Drawing.Image img =
System.Drawing.Image.FromFile(Server.MapPath("Uploads/"+dir+"/"+fName), true);
You can use following as well
Server.MapPath(".") returns the current physical directory of the file (e.g. aspx) being executed
Server.MapPath("..") returns the parent directory
Server.MapPath("~") returns the physical path to the root of the application
Server.MapPath("/") returns the physical path to the root of the domain name (is not necessarily the same as the root of the application)
References
Server.MapPath("."), Server.MapPath("~"), Server.MapPath(#"\"), Server.MapPath("/"). What is the difference?

How to retrieve "Last Modified Date" of uploaded file in ASP.Net

I am developing a website, in which client uploads some document files like doc, docx, htm, html, txt, pdf etc. I want to retrieve last modified date of an uploaded file. I have created one handler(.ashx) which does the job of saving the files.
Following is the code:
HttpPostedFile file = context.Request.Files[i];
string fileName = file.FileName;
file.SaveAs(Path.Combine(uploadPath, filename));
As you can see, its very simple to save the file using file.SaveAs() method. But this HttpPostedFile class is not exposing any property to retrieve last modified date of file.
So can anyone tell me how to retrieve last modified date of file before saving it to hard disk?
Today you can access to this information from client side using HTML5 api
//fileInput is a HTMLInputElement: <input type="file" multiple id="myfileinput">
var fileInput = document.getElementById("myfileinput");
// files is a FileList object (simliar to NodeList)
var files = fileInput.files;
for (var i = 0; i < files.length; i++) {
alert(files[i].name + " has a last modified date of " + files[i].lastModifiedDate);
}
Source and more information
You can't do this. An HTTP post request does not contain this information about an uploaded file.
Rau,
You can only get the date once it's on the server. If you're ok with this, then try:
string strLastModified =
System.IO.File.GetLastWriteTime(Server.MapPath("myFile.txt")).ToString("D");
the further caveat here being that this datetime will be the date at which it was saved on the server and not the datetime of the original file.
It is not possible, until you save the file to disk.
You typically cannot get the last modified date because the date is not stored in the file.
The Operating System actually stores file attributes like Created, Accessed, and Last Modified. See Where are “last modified date” and “last accessed date” saved?
(I say typically because certain file types like images may have EXIF tag data like the date/time the photo was taken.)

Categories