在本文中,将探讨如何构建一个Web应用程序,允许用户上传和存储图像文件(如.jpg、.png、.gif等)以及相应的文本描述。这是一项之前从未尝试过的任务,因此通过Google搜索“在数据库或文件系统中存储图像文件”来寻找解决方案。搜索结果显示了多种技术和观点,支持和反对将图像数据存储在数据库或文件系统中。然而,本文《在数据库还是文件系统中存储大对象:BLOB》总结了一个观点:“小于256K的对象最好存储在数据库中,而大于1M的对象最好存储在文件系统中。” 同样,关于即时调整图像大小的搜索也返回了各种编程技术。这里展示的代码是这些研究的结果。
本文不会讨论图像文件存储和调整大小的优缺点。将从将图像存储在文件系统中开始,同时将与图像相关的元数据存储在SQL Server数据库中。稍后,将撰写另一篇文章,描述将图像存储在数据库中的方法。
使用代码:
该Web项目使用了.NET 4.51框架,因此请确保系统上已安装。下载并解压缩Visual Studio解决方案到选择的文件夹,并使用Visual Studio 2012或更高版本打开它。
默认的aspx页面使用了标准的FileUpload、TextBox、Button和GridView控件。GridView列已转换为模板,GridView绑定到SqlDataSource控件。SqlDataSource控件又绑定到App_Data文件夹中找到的localDB。它包含一个表,用于存储文件名、文件路径和图像的文本描述。为简单起见,此应用程序仅接受MIME类型为"image/jpeg"的图形文件。其他类型,如PNG和GIF,可以通过在代码中进行少量更改来处理。该项目是使用Internet Explorer 11开发和测试的。
在Internet Explorer中运行代码并选择一个JPG图像上传。点击“提交照片”按钮开始处理。FileUpload控件在随后的回传中接收文件。按钮的点击事件处理程序检查文件的存在。如果不存在,或者不是JPEG MIME类型,则不处理数据。否则,控制权传递给ResizeAndSaveImage方法。
C#
using
System;
using
System.Drawing;
using
System.Drawing.Imaging;
using
System.Drawing.Drawing2D;
using
System.IO;
namespace
StoringImages
{
public
partial
class
Default : System.Web.UI.Page
{
protected
void
Button1_Click(
object
sender, EventArgs e)
{
if
(FileUpload1.HasFile ==
false
|| FileUpload1.PostedFile.ContentType.ToLower() !=
"
image/jpeg"
)
{
return
;
}
ResizeAndSaveImage();
SqlDataSource1.InsertParameters[
"
FilePath"
].DefaultValue = Server.MapPath(
"
SavedImages"
);
SqlDataSource1.InsertParameters[
"
FileName"
].DefaultValue = System.IO.Path.GetFileName(FileUpload1.PostedFile.FileName);
SqlDataSource1.InsertParameters[
"
Description"
].DefaultValue = TextBox1.Text;
SqlDataSource1.Insert();
}
The first thing that must be done is to get the uploaded image into a
Bitmap
object and extract vital information:
C#
//
Assumes JPG/JPEG images only
protected
void
ResizeAndSaveImage()
{
using
(Bitmap uploadedBmp =
new
Bitmap(FileUpload1.FileContent))
{
decimal
origHeight = uploadedBmp.Height;
decimal
origWidth = uploadedBmp.Width;
int
newHeight =
500
;
int
newWidth = Convert.ToInt32(newHeight / (origHeight / origWidth));
在这里,原始图像的高度和宽度被保存,新调整大小的图像的高度被确定。在这种情况下,新的高度将是500像素。它可以是任何值,甚至比原始的还要大。新宽度的计算方式是,调整大小后的图像不会出现挤压或拉伸。接下来,创建一个Graphics对象。这个对象将处理图像数据。设置了几个渲染属性,以产生高质量的图像:
C#
using
(Graphics resizedGr = Graphics.FromImage(uploadedBmp))
{
//
Optional. These properties are set for the best possible quality
resizedGr.CompositingMode = CompositingMode.SourceCopy;
resizedGr.CompositingQuality = CompositingQuality.HighQuality;
resizedGr.InterpolationMode = InterpolationMode.HighQualityBicubic;
resizedGr.SmoothingMode = SmoothingMode.HighQuality;
resizedGr.PixelOffsetMode = PixelOffsetMode.HighQuality;
此时,使用新的、更小的高度和宽度属性实例化一个新的Bitmap:
C#
using
(Bitmap resizedBmp =
new
Bitmap(uploadedBmp, newWidth, newHeight))
{
The Graphics object is commanded to draw the new image into the new smaller Bitmap. A
MemoryStream
object is created and the new Bitmap then saves the file to memory with the proper MIME type encoding. The memory file is converted to a
byte
array that is in turn saved to disk:
C#
resizedGr.DrawImage(resizedBmp,
0
,
0
);
using
(MemoryStream resizedMs =
new
MemoryStream())
{
System.Drawing.Imaging.EncoderParameters encParms =
new
System.Drawing.Imaging.EncoderParameters(
1
);
//
This allows jpeg compression to be set to 90
encParms.Param[0] =
new
System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (
long
)
90);
resizedBmp.Save(resizedMs, GetImgCodecInf(
"
image/jpeg"
), encParms);
long
msLen = resizedMs.Length;
byte[] resizedData =
new
byte[msLen];
resizedData = resizedMs.ToArray();
using
(System.IO.FileStream fStream =
new
System.IO.FileStream(Server.MapPath(
"
SavedImages"
) +
@"
\"
+ Path.GetFileName(FileUpload1.PostedFile.FileName), System.IO.FileMode.Create))
{
fStream.Write(resizedData,
0
, resizedData.Length);
}
The image now has been resized and stored on disk. The next step is to create yet another smaller corresponding thumbnail image that can be used as a preview in the GridView control. This step is essentially the same as the first image processing routine accept the height of the finished image is 100 pixels. Also the image quality parameters have been left out and to appease those who prefer not to use the C#
using
statement, the FileStream and Graphics object are closed and disposed of programmatically:
C#
//
Repeat process to create a thumbnail image, reusing resizedBmp
//
This approach does not use the 'using' statement or the
//
high quality graphics properties
origHeight = resizedBmp.Height;
origWidth = resizedBmp.Width;
int
thumbHeight =
100
;
int
thumbWidth = Convert.ToInt32(thumbHeight / (origHeight / origWidth));
Bitmap thumbBmp =
new
Bitmap(resizedBmp, thumbWidth, thumbHeight);
Graphics thumbGr = Graphics.FromImage(thumbBmp);
thumbGr.DrawImage(thumbBmp,
0
,
0
);
MemoryStream thumbMs =
new
MemoryStream();
thumbBmp.Save(thumbMs, System.Drawing.Imaging.ImageFormat.Jpeg);
long
thumbmsLen = thumbMs.Length;
byte[] thumbData =
new
byte[thumbmsLen];
thumbData = thumbMs.ToArray();
System.IO.FileStream tStream =
new
System.IO.FileStream(Server.MapPath(
"
SavedImages"
) +
@"
\thmb_"
+ Path.GetFileNameWithoutExtension(FileUpload1.PostedFile.FileName) + Path.GetExtension(FileUpload1.PostedFile.FileName), System.IO.FileMode.Create);
tStream.Write(thumbData,
0
, thumbData.Length);
tStream.Close();
thumbGr.Dispose();
thumbBmp.Dispose();
thumbMs.Dispose();
}
}
}
}
//
Returns an object with all codec data
protected
ImageCodecInfo GetImgCodecInf(
string
mimeType)
{
ImageCodecInfo[] imgCodecInfo = ImageCodecInfo.GetImageEncoders();
foreach
(ImageCodecInfo infoItem
in
imgCodecInfo)
{
if
(infoItem.MimeType.ToString().ToLower() == mimeType.ToLower())
{
return
infoItem;
}
}
return
null
;
}
}
最后,控制权返回到按钮点击事件处理程序,SqlDataSource插入参数被更新,表中插入了新记录。由于所有操作都发生在回传期间,GridView会自动用新记录更新。
结论: