5

I'm using an AsyncFileUpload (AJAX Toolkit) to upload images. I have a Button which handle the image resizing. This have worked fine for some time, but not anymore...

protected void BtnUploadImage_Click(object sender, EventArgs e)
{
    var imageFileNameRegEx = new Regex(@"(.*?)\.(jpg|jpeg|png|gif)$", 
        RegexOptions.IgnoreCase);
    if (!AsyncFileUpload1.HasFile || 
        !imageFileNameRegEx.IsMatch(AsyncFileUpload1.FileName))
    {
        AsyncFileUpload1.FailedValidation = true;
        ErrorLabel.Visible = true;
        return;
    }
    ErrorLabel.Visible = false;

    var file = AsyncFileUpload1.PostedFile.InputStream;

    var img = Image.FromStream(file, false, false);

...
}

Another thing which I find weird: If I try a image which is smaller than 80kb it works..!

We have tried to restart the server, but no change. Same code runs fine on my machine. (heard that before ?? :) )

I also tried to save the file on the server, then to get the file trough Image.FromFile(), but then I get "Cannot access a closed file."

How to resolve this ?

Thomas Sandberg
  • 943
  • 2
  • 9
  • 19
  • There's missing code in your snippet. – Darin Dimitrov Oct 23 '09 at 17:15
  • @Joseph, I think that just adding the ending brace at the function is wrong, it can't just stop there, the `img` variable is not used. – Darin Dimitrov Oct 23 '09 at 17:16
  • Didn't think the rest was relevant, what happens after Image.FromStream() doesn't executes anyway. – Thomas Sandberg Oct 23 '09 at 17:21
  • Based on your notion that < 80kB works complies with my own findings when working with upload-streams and `Graphics`/`Bitmap` etc. The same problem occurs in the reverse, when you try to write a Graphic to a Response.Out stream. – Abel Oct 23 '09 at 17:30
  • The `Cannot access a closed file` error is odd, but if you look through your real code, you may see places where you do not work correctly with `IDisposable` and `using` blocks. This is usually the cause of resources that are still in use or that cannot be read or written to. – Abel Oct 23 '09 at 17:44

2 Answers2

8

I would make sure the stream is positioned at the start:

var file = AsyncFileUpload1.FileContent;
file.Seek(0, SeekOrigin.Begin);

var img = Image.FromFile(file);

Second thing to check: the requestLengthDiskThreshold setting. Unless specified this setting has a default of ... yes, 80 KB.

Note: imo there should be no overall difference whether you use Image to read the file stream directly or if you use an intermediate MemoryStream (other than the fact that in the latter case you actually loads the entire file into memory twice). Either way the original file stream will be read from, thus stream position, CAS rights, file permissions, etc still applies.

Note2: and yes, by all means make sure those resources are disposed properly :)

Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
  • 1
    Thanks, that worked like a charm. I had looked at the HttRuntime settings, but somehow totally overlooked the requestLengthDiskThreshold setting. :( Yet again you save the day :D – Thomas Sandberg Oct 24 '09 at 11:58
  • @PeterLillevold how to achieve this without using ajax toolkit – Heemanshu Bhalla Aug 03 '17 at 15:41
  • @HeemanshuBhalla achieve what? you have to be more specific here, my answer doesn't only apply to ATK. Are you asking about image upload in general? If so, look for existing QAs here on SO, or ask a new question. – Peter Lillevold Aug 06 '17 at 16:17
1

This is correct, it will not work. The problem is that you are crossing a managed/unmanaged boundary, I recently encountered the same. Other problems are that the stream is not directly there and the Image.FromStream has no idea how to deal with it.

The solution is quite straightforward: read everything from PostedFile into a MemoryStream (just use new MemoryStream()) and use the MemoryStream with the Image.FromStream. This will solve your problem.

Make sure to make proper use of using when you work with Image, Graphics and Streams. All of them implement the IDisposable and in an ASP.NET environment, not using using blocks properly, can and will lead to increased memory usage and other nasty side effect on the long run (and ASP.NET apps do run very long!).

The solution should look something like this:

using(Stream memstr = new MemoryStream())
{
    // copy to a memory stream
    Stream uploadStream = AsyncFileUpload1.PostedFile.InputStream;
    byte[] all = new byte[uploadStream.Length];
    uploadStream.Read(all, 0, uploadStream.Length);
    memstr.Write(all, 0, uploadStream.Length);
    memstr.Seek(0, SeekOrigin.Begin);
    using(Graphics g = Graphics.FromStream(memstr))
    {
         // do your img manipulation, or Save it.
    }
}

Update: the crossing managed boundary issue only occurs in the reverse (using Response stream), it seems, not with Upload streams, but I'm not entirely sure.

Abel
  • 56,041
  • 24
  • 146
  • 247
  • Could you please provide a example on how to read all the bytes into the memory stream? – Thomas Sandberg Oct 23 '09 at 17:28
  • Note: it is not guaranteed that the `Read` works. If not, the return value contains the amount of bytes that were actually read. You can make your code more robust by always checking the return value of `Read()`, or loop until all bytes are read. – Abel Oct 23 '09 at 17:42
  • Tried this: string imageID; using (var mStream = new MemoryStream()) { var uploadStream = AsyncFileUpload1.PostedFile.InputStream; var all = new byte[uploadStream.Length]; uploadStream.Read(all, 0, (int) uploadStream.Length); mStream.Write(all, 0, (int) uploadStream.Length); mStream.Seek(0, SeekOrigin.Begin); using (var img = Image.FromStream(mStream)) { imageID = ImageCreation.SaveImage(img, true); } } But I get {"Parameter is not valid."}, still on the Image.FromStream. (Now even on local machine) – Thomas Sandberg Oct 23 '09 at 17:45
  • then I'm afraid you'll have to do some debugging. Place a breakpoint and go through the code line by line. I suspect that the stream is not complete for some reason, but I don't seem to be able to create the same situation... MemoryStream as intermediate stream always worked for me. – Abel Oct 23 '09 at 17:57
  • Ok, I'll try that. Btw: The individual bytes in "all" were "0". Does this give any clues? – Thomas Sandberg Oct 23 '09 at 18:01
  • Apologies, I wrote `Graphics.FromStream`, this should of course be `Image.FromStream`. The bytes must be the same size as the uploaded file size, otherwise, there's your error. Also, consider this link, it explains why the error occurs, invalid image data: http://stackoverflow.com/questions/629955/parameter-not-valid-exception-loading-system-drawing-image and this: http://www.sebastianpereira.com/blog/2009/08/20/parameter-is-not-valid-gdi-error/ – Abel Oct 23 '09 at 18:13