Imagefrom.File(). Out of memory exception - c#

I am trying to load about 60 pictures in a list. Each picture is about 1MB. For 20 pictures no problem but above that I get Out of memory exception on the code line below. I have searched vastly of related issues, some stating about "using" key word and stream but since I am a beginner can someone please help me.
Image image = Bitmap.FromFile(Filename);
Here is my code
private void LoadBtn_Click_1(object sender, EventArgs e)
{
OpenFileDialog newDialog = new OpenFileDialog();
if (newDialog.ShowDialog() == DialogResult.OK)
{
images.Clear();
string dirPath = System.IO.Path.GetDirectoryName(newDialog.FileName.ToLower());
DirectoryInfo di = new DirectoryInfo(dirPath);
FileInfo[] finfos = di.GetFiles("*.*");
foreach (FileInfo fi in finfos)
{
string ext = fi.Extension.ToLower();
if ((ext.Equals(".png")) || (ext.Equals(".jpg")) || (ext.Equals(".tif")) || (ext.Equals(".gif")))
{
string Filename = fi.FullName;
Image image = Bitmap.FromFile(Filename); //exception occurs HERE
images.Add(image);
//this.imageList1.Images.Add(image);
//image.Dispose();
}
}
}
pictureBox3.Image = images[0];
}
I am using C#, windows forms. thanks

After taking a look at the software you mentioned, as I told you you don't need to load the whole image in memory if you only need a thumbnail.
So I'd create I class
class ImageAndThumb
{
public Image Thumb;
public Image Big;
private string ImagePath;
public ImageAndThumb(string fileName)
{
ImagePath = fileName;
Image image = Image.FromFile(fileName)
Image thumb = img.GetThumbnailImage(200, 200, ()=>false, IntPtr.Zero);
}
public Image LoadBigImage()
{
Big = Image.FromFile(ImagePath);
return Big;
}
public void UnloadImage()
{
Big = null;
}
}
Now we use that class:
List<ImageAndThumb> Images = new List<ImageAndThumb>();
private void LoadBtn_Click_1(object sender, EventArgs e)
{
OpenFileDialog newDialog = new OpenFileDialog();
if (newDialog.ShowDialog() == DialogResult.OK)
{
Images.Clear();
string dirPath = System.IO.Path.GetDirectoryName(newDialog.FileName.ToLower());
DirectoryInfo di = new DirectoryInfo(dirPath);
FileInfo[] finfos = di.GetFiles("*.*");
foreach (FileInfo fi in finfos)
{
string ext = fi.Extension.ToLower();
if ((ext.Equals(".png")) || (ext.Equals(".jpg")) || (ext.Equals(".tif")) || (ext.Equals(".gif")))
{
string Filename = fi.FullName;
ImageAndThumb image = new ImageAndThumb(Filename);
Images.Add(image);
}
}
}
pictureBox3.Image = Images[0].Thumb; // << Much less memory usage;
}
And now whenever you need to use an image load it first
For example:
void ShowPicture(int index)
{
Images[index].LoadBigImage();
PictureBoxBig.image = Images[index].Big;
}
void ClosePicture(int index)
{
Images[index].UnloadImage();
}
one good idea is to unload an image once you load another:
int currentPictureIndex = -1;
void ShowPicture(int index)
{
Images[index].LoadBigImage();
PictureBoxBig.image = Images[index].Big;
if(CurrentPictureIndex > -1) ClosePicture(CurrentPictureIndex);
currentPictureIndex = index;
}

First of all, are you running out of memory ? Cause if you are then the error is valid.
If you are not running out of memory the first thing you will need to do is wrap the code in your foreach loop in a try/catch block as follows:
foreach (FileInfo fi in finfos)
{
string ext = fi.Extension.ToLower();
if ((ext.Equals(".png")) || (ext.Equals(".jpg")) || (ext.Equals(".tif")) || (ext.Equals(".gif")))
{
try
{
string Filename = fi.FullName;
Image image = Bitmap.FromFile(Filename); //exception occurs HERE
images.Add(image);
//this.imageList1.Images.Add(image);
//image.Dispose();
}
catch {}
}
}
The reason for that is, as Jason Watkins mentioned in the comments it might as well be another form of error that just appears as an out of memory exception because of the lack of error messages in the Class.

Related

Image not uploading and getting "Cannot access a closed file."

I have been trying to upload images via a file upload control. The file is not being uploaded and I keep getting the following error:"Cannot access a closed file."
I am unsure as to why I am getting this error, any assistance would be appreciated.
My code: First is the code that grabs the image and creates the 'preview'
protected void btnupload_Click(Object sender, EventArgs e)
{
Session["Image"] = flupGalImg.PostedFile;
Stream fs = flupGalImg.PostedFile.InputStream;
BinaryReader br = new BinaryReader(fs);
byte[] bytes = br.ReadBytes((Int32)fs.Length);
string base64String = Convert.ToBase64String(bytes, 0, bytes.Length);
imgGalImg.ImageUrl = "data:image/png;base64," + base64String;
imgGalImg.Visible = true;
}
My code that calls my business logic code:
else if(btnaddedit.Text == "Add item")
{
if(txtItemTitle.Text != "")
{
if(txtItemDescription.Text != "")
{
if(Session["Image"] != null)
{
HttpPostedFile postedFile = (HttpPostedFile)Session["Image"];
int alb;
int.TryParse(hdnAlbId.Value, out alb);
if(newsLogic.CreateNewGalleryItem(txtItemTitle.Text, txtItemDescription.Text, alb, postedFile) == true)
{
// Do something.
}
}
}
}
}
And finally my business logic code:
// create gallery item
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Insert, true)]
public bool CreateNewGalleryItem(string itemTitle, string itemDescription, int itemAlbum, HttpPostedFile itemFile)
{
string folderpath = #"~/Images/GalleryImages/";
string filename = Path.GetFileName(itemFile.FileName.ToString());
bool itemCreated = false;
string itemImgPath;
try
{
itemImgPath = Path.Combine(folderpath + filename);
string itemImgUploadPath = Path.Combine(HttpContext.Current.Server.MapPath(folderpath) + filename);
itemFile.SaveAs(itemImgUploadPath);
galAdp.CreateGalleryItem(itemTitle, itemDescription, itemImgPath, itemAlbum);
itemCreated = true;
}
catch (Exception er)
{
itemCreated = false;
string consmsg = er.Message;
}
return itemCreated;
}
My 'image preview' works just fine. The error is thrown on this line of the business logic: itemFile.SaveAs(itemImgUploadPath);
When I did a google search (well lots of google searches) I read that it may be the file size so I increased the max allowed content in the web.config but nothing changed.
Any ideas as to what I am doing wrong?

Overwrite file only works once

I have a simple program that copies files and directories from one place to another. I have it set-up that if there are any exceptions (such as if access to the path is denied) it will create a log file with the error.
I have a button that when pressed, performs the copy action. Everything works fine the first time I press the button and the log file is either created or overwritten with the appropriate error messages.
However, if I press the button a second time, the text file is not overwritten and instead the error messages append. If I close out of my program and run it again, the file is overwritten on the first button press. Any thoughts would be greatly appreciated.
target is a string filepath which I'm getting from a FolderBrowserDialog and taking the selected path and setting it to a textbox. loglist is just a simple List<string> I'm using to store the error messages from any exceptions that occur during the copy process.
public partial class Form1 : Form
{
static List<string> logList = new List<string>();
public Form1()
{
InitializeComponent();
}
private static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
if (source.FullName.ToLower() == target.FullName.ToLower())
return;
if (Directory.Exists(target.FullName) == false)
{
Directory.CreateDirectory(target.FullName);
}
foreach (FileInfo fi in source.GetFiles())
{
try
{
fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
}
catch (Exception ex)
{
logList.Add(ex.Message);
}
}
foreach (DirectoryInfo diSourceSub in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir = target.CreateSubdirectory(diSourceSub.Name);
CopyAll(diSourceSub, nextTargetSubDir);
}
}
private void directoryPickerBtn1_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderDialog = new FolderBrowserDialog();
DialogResult folderResult = folderDialog.ShowDialog();
if (folderResult == DialogResult.OK)
{
directoryTextbox1.Text = folderDialog.SelectedPath;
}
}
private void directoryPickerBtn2_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderDialog = new FolderBrowserDialog();
DialogResult folderResult = folderDialog.ShowDialog();
if (folderResult == DialogResult.OK)
{
directoryTextbox2.Text = folderDialog.SelectedPath;
}
}
private void copyBtn_Click(object sender, EventArgs e)
{
string source = (directoryTextbox1.Text);
string target = (directoryTextbox2.Text);
DirectoryInfo dirSource = new DirectoryInfo(source);
DirectoryInfo dirTarget = new DirectoryInfo(target);
try
{
CopyAll(dirSource, dirTarget);
if (logList.Count > 0)
{
using (StreamWriter sw = new StreamWriter(target + #"\log.txt", false))
{
foreach (string error in logList)
{
sw.WriteLine(error);
}
}
}
DialogResult result = MessageBox.Show("Copy Succeeded", "Success");
if (result == DialogResult.OK)
{
string myPath = dirTarget.ToString();
System.Diagnostics.Process prc = new System.Diagnostics.Process();
prc.StartInfo.FileName = myPath;
prc.Start();
}
}
catch (Exception)
{
MessageBox.Show("Copy Failed", "Failed");
}
}
}
}
As #Reza Aghaei pointed out in comments, the problem is that you do not clear the logList.
The file gets created anew every time, but each time you click the Copy button, the loglist still contains the results of the previous copy action.
So you need to clear the list when starting a new copy:
private static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
logList.Clear();
// ...
From your code it seems that you never clear the logList, this means that it appears the file is being appending because the logList still contains all of the old entries.
You'll need to clear the list between copies if you only want relevant entries to that copy, either before you start copying or after you finish writing the file.
This would be better as a separate method
try
{
CopyAll(dirSource, dirTarget);
SaveLog(target + #"\log.txt");
ClearLog();
//...
}
private void SaveLog(string filename)
{
if (logList.Count > 0)
{
FileStream fs = File.Open(target + #"\log.txt", FileMode.Create);
using (StreamWriter sw = new StreamWriter(fs))
{
foreach (string error in logList)
{
sw.WriteLine(error);
}
}
}
}

Reduce size of image after Scan?

I have code that scan file and save it on my path:
public ImageFile Scan()
{
var device = this._deviceInfo.Connect();
var item = device.Items[1];
var imageFile = (ImageFile)item.Transfer(FormatID.wiaFormatJPEG);
return imageFile;
}
protected void btnscan_Click(object sender, EventArgs e)
{
foreach (Object scanner in scanners)
{
if (ddlSelectDevice.SelectedItem.ToString().Equals(scanner.ToString()))
{
//var d = ddlSelectDevice.SelectedIndex as Scanner;
var device = scanner as Scanner;
if (device == null)
{
return;
}
var image = device.Scan();
var path = "~/Scanner/scan.jpeg";
if (File.Exists(Server.MapPath(path)))
{
File.Delete(Server.MapPath(path));
}
image.SaveFile(Server.MapPath(path));
break;
}
}
}
I save this image in database by convert to byte:
private byte[] image2Byte()
{
string filePath = null;
Byte[] bytes = null;
if (File.Exists(Server.MapPath("~/Scanner/scan.jpeg")))
{
filePath = Server.MapPath("~/Scanner/scan.jpeg");
return bytes = File.ReadAllBytes(filePath);
}
else
{
filePath = Server.MapPath("~/Scanner/None.jpg");
return bytes = File.ReadAllBytes(filePath);
}
}
But my images usually have large size (up 400kB) and makes problem on load. How I can reduce image size?
You can use ImageProcessor to reduce the quality of the JPEG file. A quality of 80% is accaptable for most use cases.
using (ImageFactory imageFactory = new ImageFactory(preserveExifData:true))
{
imageFactory.Load(inStream)
.Quality(80)
.Save(outStream);
}

Deleting File which is displayed in picturebox

I am selecting file from openfiledialoge and displaying it in picturebox and its name in textbox when I click on delete button I am getting exception The process cannot access the file because it is being used by another process.
I searched a lot for this exception to get resolved but i didn't fine any of them working, when i tried closing file with imagename which is in textbox i.e the file i am displaying in picturebox ; using IsFileLocked method,this closes and deletes all files of particular directory path ,but how can I delete the only file shown in picturebox,where I am going wrong
public partial class RemoveAds : Form
{
OpenFileDialog ofd = null;
string path = #"C:\Users\Monika\Documents\Visual Studio 2010\Projects\OnlineExam\OnlineExam\Image\"; // this is the path that you are checking.
public RemoveAds()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (System.IO.Directory.Exists(path))
{
ofd = new OpenFileDialog();
ofd.InitialDirectory = path;
DialogResult dr = new DialogResult();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Image img = new Bitmap(ofd.FileName);
string imgName = ofd.SafeFileName;
txtImageName.Text = imgName;
pictureBox1.Image = img.GetThumbnailImage(350, 350, null, new IntPtr());
ofd.RestoreDirectory = true;
}
}
else
{
return;
}
}
private void button2_Click(object sender, EventArgs e)
{
//Image img = new Bitmap(ofd.FileName);
string imgName = ofd.SafeFileName;
if (Directory.Exists(path))
{
var directory = new DirectoryInfo(path);
foreach (FileInfo file in directory.GetFiles())
{ if(!IsFileLocked(file))
file.Delete();
}
}
}
public static Boolean IsFileLocked(FileInfo path)
{
FileStream stream = null;
try
{ //Don't change FileAccess to ReadWrite,
//because if a file is in readOnly, it fails.
stream = path.Open ( FileMode.Open, FileAccess.Read, FileShare.None );
}
catch (IOException)
{ //the file is unavailable because it is:
//still being written to or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
}
Thanks in advance for any help
The (previously) accepted answer to this question is very poor practice. If you read the documentation on System.Drawing.Bitmap, in particular for the overload that creates a bitmap from a file, you will find :
The file remains locked until the Bitmap is disposed.
in your code you create the bitmap and store it in a local variable but you never dispose of it when you are done. This means your image object has gone out of scope but has not released its lock on the image file you are trying to delete. For all objects that implement IDisposable (like Bitmap) you must dispose of them yourself. See this question for example (or search for others - this is a very important concept!).
To correct the problem properly you simply need to dispose of the image when you are done with it :
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Image img = new Bitmap(ofd.FileName); // create the bitmap
string imgName = ofd.SafeFileName;
txtImageName.Text = imgName;
pictureBox1.Image = img.GetThumbnailImage(350, 350, null, new IntPtr());
ofd.RestoreDirectory = true;
img.Dispose(); // dispose the bitmap object
}
Please do not take the advice in the answer below - you should nearly never need to call GC.Collect and if you need to do it to make things work it should be a very strong signal that you are doing something else wrong.
Also, if you only want to delete the one file (the bitmap you have displayed) your deletion code is wrong and will delete every file in the directory as well (this is just repeating Adel's point). Further, rather than keep a global OpenFileDialog object alive simply to store the file name, I would suggest getting rid of that and saving just the file info :
FileInfo imageFileinfo; //add this
//OpenFileDialog ofd = null; Get rid of this
private void button1_Click(object sender, EventArgs e)
{
if (System.IO.Directory.Exists(path))
{
OpenFileDialog ofd = new OpenFileDialog(); //make ofd local
ofd.InitialDirectory = path;
DialogResult dr = new DialogResult();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Image img = new Bitmap(ofd.FileName);
imageFileinfo = new FileInfo(ofd.FileName); // save the file name
string imgName = ofd.SafeFileName;
txtImageName.Text = imgName;
pictureBox1.Image = img.GetThumbnailImage(350, 350, null, new IntPtr());
ofd.RestoreDirectory = true;
img.Dispose();
}
ofd.Dispose(); //don't forget to dispose it!
}
else
{
return;
}
}
Then in your second button handler you can just delete the one file you are interested in.
private void button2_Click(object sender, EventArgs e)
{
if (!IsFileLocked(imageFileinfo))
{
imageFileinfo.Delete();
}
}
I had the same problem : I loaded a file in a PictureBox and when trying to delete it I got the same exception.
This occurred only when the image was displayed.
I tried them all :
picSelectedPicture.Image.Dispose();
picSelectedPicture.Image = null;
picSelectedPicture.ImageLocation = null;
and still got the same exception.
Then I found this on CodeProject : [c#] delete image which is opened in picturebox.
Instead of using PictureBox.Load() it creates an Image from the file and sets it as PictureBox.Image:
...
// Create image from file and display it in the PictureBox
Image image = GetCopyImage(imagePath);
picSelectedPicture.Image = image;
...
private Image GetCopyImage(string path) {
using (Image image = Image.FromFile(path)) {
Bitmap bitmap = new Bitmap(image);
return bitmap;
}
}
No more exceptions when I delete the file.
IMHO, this is the most suitable solution.
EDIT
I forgot to mention that you can safely delete the file immediately after display :
...
// Create image from file and display it in the PictureBox
Image image = GetCopyImage(imagePath);
picSelectedPicture.Image = image;
System.IO.File.Delete(imagePath);
...
use this code
string imgName = ofd.SafeFileName;
if (Directory.Exists(path))
{
var directory = new DirectoryInfo(path);
foreach (FileInfo file in directory.GetFiles())
{
GC.Collect();
GC.WaitForPendingFinalizers();
file.Delete();
}
}
Your button2_Click event handler is cycling through all the files inside your directory & doing the deletes.
You need to change your code like the following:
public partial class RemoveAds : Form
{
OpenFileDialog ofd = null;
string path = #"C:\Users\Monika\Documents\Visual Studio 2010\Projects\OnlineExam\OnlineExam\Image\"; // this is the path that you are checking.
string fullFilePath;
public RemoveAds()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (System.IO.Directory.Exists(path))
{
ofd = new OpenFileDialog();
ofd.InitialDirectory = path;
DialogResult dr = new DialogResult();
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
Image img = new Bitmap(ofd.FileName);
string imgName = ofd.SafeFileName;
txtImageName.Text = imgName;
pictureBox1.Image = img.GetThumbnailImage(350, 350, null, new IntPtr());
fullFilePath = ofd.FilePath;
ofd.RestoreDirectory = true;
}
}
else
{
return;
}
}
private void button2_Click(object sender, EventArgs e)
{
FileInfo file = new FileInfo(fullFilePath);
if(!IsFileLocked(file))
file.Delete();
}
}
public static Boolean IsFileLocked(FileInfo path)
{
FileStream stream = null;
try
{ //Don't change FileAccess to ReadWrite,
//because if a file is in readOnly, it fails.
stream = path.Open ( FileMode.Open, FileAccess.Read, FileShare.None );
}
catch (IOException)
{ //the file is unavailable because it is:
//still being written to or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
}
By using GetThumnailImage you have to specify the width and height which is static.
Use the Load method instead.
eg: pictureBox1.Load(Path to the image); by using this u will have no problem in deleting the image or the folder before closing the app. no other methods need to be created.
hope this helps

Drag and Drop file transfer in WPF. How to get file name of the dropped content in a window

I'm creating a local file transfer app. I would like the user to drag-drop an item into the file transfer application to initiate the file transfer just like skype or other messengers.
While dropping an item. The drop event was triggered. But, I don't know where to get the details of the item such as Location, Size etc., eg., If I drop an Image. I want to read the details mentioned above.
Note:I have enabled the AllowDrop & Subsribed to Drop event.[If that helps]
Do you mean Size of file or Size of image in pixel?
Anyway, use this code:
private void Window_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, true))
{
string[] droppedFilePaths = e.Data.GetData(DataFormats.FileDrop, true) as string[];
foreach (var path in droppedFilePaths)
{
string location = null;
int pxWidth = 0, pxHeight = 0;
FileInfo fi = new FileInfo(path);
//fi.Length //File size
//fi.DirectoryName //Directory
using (var fs = fi.OpenRead())
{
try
{
var bmpFrame = BitmapFrame.Create(fs);
var m = bmpFrame.Metadata as BitmapMetadata;
if (m != null)
location = m.Location;
pxWidth = bmpFrame.PixelWidth;
pxHeight = bmpFrame.PixelHeight;
}
catch
{
//File isn't image
}
}
this.fileList.Items.Add(string.Format("({0}x{1}), location: {2}", pxWidth, pxHeight, location));
}
}
}

Categories