Resizing Images with ASP.NET and saving to Database - asp.net

I need to take an uploaded image, resize it, and save it to the database. Simple enough, except I don't have access to save any temp files to the server. I'm taking the image, resizing it as a Bitmap, and need to save it to a database field as the original image type (JPG for example). How can I get the FileBytes() like this, so I can save it to the database?
Before I was using ImageUpload.FileBytes() but now that I'm resizing I'm dealing with Images and Bitmaps instead of FileUploads and can't seem find anything that will give me the bytes.
Thanks!

It's actually not so simple... there are 28 non-obvious pitfalls you should watch out for when doing image resizing. It's best to use my free, open-source library to handle all the encoding issues and avoid the GDI bugs.
Here's how to get an encoded byte[] array for each uploaded file, after resizing, cropping, and converting to Jpeg format.
using ImageResizer;
using ImageResizer.Encoding;
//Loop through each uploaded file
foreach (string fileKey in HttpContext.Current.Request.Files.Keys) {
HttpPostedFile file = HttpContext.Current.Request.Files[fileKey];
//You can specify any of 30 commands.. See http://imageresizing.net
ResizeSettings resizeCropSettings =
new ResizeSettings("width=200&height=200&format=jpg&crop=auto");
using (MemoryStream ms = new MemoryStream()) {
//Resize the image
ImageBuilder.Current.Build(file, ms, resizeCropSettings);
//Upload the byte array to SQL: ms.ToArray();
}
}
It's also a bad idea to use MS SQL for storing images. See my podcast with Scott Hanselman for more info.

See Resizing an Image without losing any quality You can then write your image (Bitmap.SaveToStream) to a MemoryStream and call ToArray to get the bytes.

This is what I've done to resize images.
private byte[] toBytes(Image image)
{
Bitmap resized = new Bitmap(image, yourWidth, yourHeight);
System.IO.MemoryStream ms = new System.IO.MemoryStream();
resized.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
resized.Dispose();
return ms.ToArray();
}

Related

Fortify Cross-site scripting: Persistent issue in Response.Binarywrite

In an existing Asp.Net application, we are using Response.BinaryWrite to render image on an aspx page. This is the required functionality, and below is the C# code-
1. byte[] img = getImage();
2. Response.BinaryWrite(img);
The getImage function reads the image from a folder on server and returns byte array. Fortify scan shows cross-site vulnerability on 2nd line.
I did following validations, but fortify still reports it as cross-site issue -
Validated bytearray to check if the file is of correct format (jpeg or bmp), used this link - Determine file type of an image
Response.BinaryWrite(ValidateFileType(img));
Validated the domain in the file path to check if the file is originating from correct domain.
Is there any specific way to pass the fortify cross-site issue with byte array or can i consider it as false positive?
Had to use a workaround to resolve this, below is the old and new code -
Old Code -
1. byte[] byteImage = getImage();
2. Response.BinaryWrite(byteImage);
New Code (Replaced 2nd line in old code with below block) -
byte[] byteImage = getImage();
var msIn = new MemoryStream(byteImage);
System.Drawing.Image img = System.Drawing.Image.FromStream(msIn);
var msOut = new MemoryStream();
img.Save(msOut, img.RawFormat);
Response.BinaryWrite(msOut.ToArray());
msIn.Dispose();
msOut.Dispose();
Response.Flush();
So, basically converting the byteArray to an Image object, and then writing the image object back to the Response.BinaryWrite stream resolved this, and it passed through Fortify scan.
If anyone is looking for a solution, this might help.

Can I test the validity of an image file before uploading it in ASP.NET?

I have an ASP.NET web application that allows the user to upload a file from his PC to a SQL Server database (which is later used to generate an image for an tag). Is there an "easy" way to test the image within .NET to validate that it does not contain anything malicious before saving it?
Right now, I use this:
MemoryStream F = new MemoryStream();
Bitmap TestBitmap = new Bitmap(Filename);
TestBitmap.Save(F, System.Drawing.Imaging.ImageFormat.Png);
int PhotoSize = (int)F.Length;
Photo = new byte[PhotoSize];
F.Seek(0, SeekOrigin.Begin);
int BytesRead = F.Read(Photo, 0, PhotoSize);
F.Close();
Creating TestBitmap fails if it is not an image (e.g. if Filename is the name of a text file), but apparently this doesn't stop a file that is an image with malicious code appended to it from loading as an image, so saving it as a MemoryStream and then writing the stream to a byte array (which is later saved in the database) supposedly fixes this.
To avoid people pass programs and other information's using the ability to upload photos to your site you can do two main steps.
Read and save again the image with your code to remove anything elst.
Limit the size of each image to a logical number.
To avoid some one upload bad code and run it on your server you keep an isolate folder with out permission to run anything. More information's about that on:
I've been hacked. Evil aspx file uploaded called AspxSpy. They're still trying. Help me trap them‼
And a general topic on the same subject: Preparing an ASP.Net website for penetration testing

Most Space-Efficient Way to Store a Byte Array in a Database Table - ASP.NET

Right now we have a database table (SQL Server 2008 R2) that stores an uploaded file (PDF, DOC, TXT, etc.) in an image type column. A user uploads this file from an ASP.NET application. My project is to get a handle on the size at which this table is growing, and I've come up with a couple of questions along the way.
On the database side, I've discovered the image column type is supposedly somewhat depreciated? Will I gain any benefits to switching over to varbinary(max), or should I say varbinary(5767168) because that is my file size cap, or might as well I just let it stay as an image type as far as space-efficiency is concerned?
On the application side, I want to compress the byte array. Microsoft's built in GZip sometimes made the file bigger instead of smaller. I switched over to SharpZipLib, which is much better, but I still occasionally run into the same problem. Is there a way to find out the average file compression savings before I implement it on a wide scale? I'm having a hard time finding out what the underlying algorithm is that they use.
Would it be worth writing a Huffman code algorithm of my own, or will that present the same problem where there is occasionally a larger compressed file than original file?
For reference, in case it matters, here's the code in my app:
using ICSharpCode.SharpZipLib.GZip;
private static byte[] Compress(byte[] data)
{
MemoryStream output = new MemoryStream();
using (GZipOutputStream gzip = new GZipOutputStream(output))
{
gzip.IsStreamOwner = false;
gzip.Write(data, 0, data.Length);
gzip.Close();
}
return output.ToArray();
}
private static byte[] Decompress(byte[] data)
{
MemoryStream output = new MemoryStream();
MemoryStream input = new MemoryStream();
input.Write(data, 0, data.Length);
input.Position = 0;
using (GZipInputStream gzip = new GZipInputStream(input))
{
byte[] buff = new byte[64];
int read = gzip.Read(buff, 0, buff.Length);
while (read > 0)
{
output.Write(buff, 0, read);
read = gzip.Read(buff, 0, buff.Length);
}
gzip.Close();
}
return output.ToArray();
}
Thanks in advance for any help. :)
that's not a byte array, that's a BLOB. 10 years ago, you would have used the IMAGE datatype.
these days, it's more efficient to use VARBINARY(MAX)
I really reccomend that people use FILESTREAM for VarBinary(Max) as it makes backing up the database (without the blobs) quite easy.
Keep in mind that using the native formats (without compression) will allow full text searches.. Which is pretty incredible if you think about it. You have to install some iFilter from Adobe for searching inside PDF.. but it's a killer feature, I can't live without it.
I hate to be a jerk and answer my own question, but I thought I'd summarize my findings into a complete answer for anyone else looking to space-efficiently store file/image data within a database:
* Using varbinary(MAX) versus Image?
Many reasons for using varbinary(MAX), but top among them is that Image is deprecated and in a future version of SQL it will be removed altogether. Not starting any new projects with it is just nipping a future problem in the bud.
According to the info in this question: SQL Server table structure for storing a large number of images, varbinary(MAX) has more operations available to be used on it.
Varbinary(MAX) is easy to stream from a .NET application by using an SQL Parameter. Negative one is for 'MAX' length. Like so:
SQLCommand1.Parameters.Add("#binaryValue", SqlDbType.VarBinary, -1).Value = compressedBytes;
* What compression algorithm to use?
I'm really not much closer to a decent answer on this one. I used ICSharpCode.SharpZipLib.Gzip and found it had better performance than the built in zipping functions simply by running it on a bunch of stuff and comparing it.
My results:
I reduced my total file size by about 20%. Unfortunately, a lot of the files I had were PDFs which don't compress that well, but there was still some benefit. Not much luck (obviously) with file types that were already compressed.

How can I create an image object from an FileUpload control in asp.net?

I have a FileUpload control. I am trying to save the file that is uploaded (an image) and also save several thumbnail copies of the file.
When I try something like this:
System.Drawing.Image imgOriginal = System.Drawing.Image.FromStream(PhotoUpload.PostedFile.InputStream);
I get an "System.ArgumentException: Parameter is not valid."
I also tried using the PhotoUpload.FileBytes to create the image from the file bytes instead of the InputStream, but the same error occurs.
The uploaded file is a jpg. I know it's a valid jpg since it saves the original ok.
Edit: This code actually does work. The Parameter is not valid was due to the PhotoUpload.PostedFile.InputStream being empty... which seems to be an entirely different issue. It looks like after I save the original the fileupload stream goes away.
Edit: Found out that the InputStream of a FileUpload can only be read/consumed one time and then it is gone.
To get around that I saved the fileupload filebytes into a byte array and used the byte array to create copies of the image.
Code:
// Copy the FileBytes into a byte array
byte[] imageData = PhotoUpload.FileBytes;
// Create a stream from the byte array if you want to save it somewhere:
System.IO.Stream myStream = new System.IO.MemoryStream(imageData);
// Or create an image from the stream as many times as needed:
System.Drawing.Image imgOriginal = System.Drawing.Image.FromStream(myStream);
Have a look at this link
ASP Net - How to pass a postedfile to a system.drawing.image
Here's my function call:
uploadAndSizeImage(System.Drawing.Image.FromStream
(uploadedFileMD.PostedFile.InputStream))
I'm getting this error:
Exception Details:
System.ArgumentException: Invalid
parameter used.
Google isn't turning up much though I
did find a reference to it possibly
being caused by the stream reader
being at the end of the stream and me
needing to reset it to position one.
But that was kind of vague and not
really sure if it applies here.
Does this help?
EDIT:
Also, have you tried manually reading the file using something like
System.IO.FileStream fs = System.IO.File.OpenRead(#"Image.JPG");
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
System.IO.MemoryStream ms = new System.IO.MemoryStream(data);
System.Drawing.Image image = Image.FromStream(ms);
Or saving a temp copy from the FileUpload and loading the image from file?

Getting image height and width when image is saved in database

I save my images into my SQL Server Database with ASP.NET(2.0).
(imageData -> image) (imageType -> varchar) (imageLength -> bigint)
Thus the imageData will be "Binary data" and the imageType will be like "image/gif" and the imageLength will be like "6458".......
Is it possible to get the image HEIGHT and WIDTH from my VB.NET code inside my ASP.NET?
I want to make my picture box on my web form the size of the actual image that is saved in my database.
Regards
Etienne
Assuming you have the data in a stream:
System.Drawing.Image.FromStream(yourStream).Height
You are probally better doing this when you save the image to the DB, as I'm sure that loading the image object isn't going to be cheap.
Edit
If we take this to email then the next guy with this issue won't have a record of our solution. Let's keep it in the forum for now.
Just so we know, I am a C# developer so I'm not going to try and remember vb.net syntax if this is an issue and you need help converting let me know.
You have an IDataReader I'm assuming which is pulling an Image or binary varbinary etc field from your DB. You need to load it into an object which derives from System.IO.Stream. For our purposes a MemoryStream is the perfect choice as it doesn't require a backing store such as a disk.
System.IO.MemoryStream yourStream = new System.IO.MemoryStream(dr["imgLength"] as byte[]);
System.Drawing.Image yourImage=System.Drawing.Image.FromStream(yourStream);
yourImage.Height;
yourImage.width
I would save the height and width of the image in separate columns when you save the image to the database intially. Then when you do your select statement to read the image out of the database, you can also access the height and width fields from the database.
Alternatively you can access the height and width information when you load the image out of the database and then set the height and width properties before assigning the image to the picture box.
You can get the height and width but you are going to have to load the whole image into memory using the system.drawing.image library, each time you need that info.
Better to save it as separate fields the first time you save it to the database, that is generally what I do.
Sounds like you want 2 more fields in your database, height and width.
I strongly believe that after a few hundred gigabytes of images you'll find yourself thinking that the file system and static file http servers are better suited than the databas for storing images. It also allows you to use thousands of existing free tools to work with, move, host, etc the images. Your database might get busy, and it's not easy to cluster.
Dim mobj_wc As New System.Net.WebClient
Dim obj_BookOriginalImage As System.Drawing.Bitmap
Dim ImageInBytes() As Byte = mobj_wc.DownloadData(mstr_BookURL & mds_BookDetails.Tables(0).Rows(0).Item("BookImage"))
'CREATE A MEMORY STREAM USING THE BYTES
Dim ImageStream As New IO.MemoryStream(ImageInBytes)
obj_BookOriginalImage = New System.Drawing.Bitmap(ImageStream)
mint_ImageWidth = obj_BookOriginalImage.Width

Resources