调整和存储图像文件 - 文件系统和数据库 第 1 部分






4.96/5 (14投票s)
本文将介绍并演示一种在本地文件系统上调整和存储图像文件,以及在 SQL Server 中存储图像元数据的方法,该方法基于 ASP.NET 4.51。
背景
我曾多次被要求创建允许用户上传和存储图像文件(.jpg、.png、.gif 等)以及相应文本描述的 Web 应用程序。第一次遇到这个问题是因为我从未做过。搜索“在数据库文件系统中存储图像文件”会发现许多关于在数据库或文件系统中存储图像数据的技术和观点。然而,这篇论文《 BLOB 还是不 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 图像进行上传。单击“提交照片”按钮开始处理。文件上传控件在生成的 Postback 中接收文件。按钮的单击事件处理程序会检查文件是否存在。如果不存在,或者 MIME 类型不是 JPEG,则不处理任何数据。否则,控制权将传递给 ResizeAndSaveImage 方法。
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();
}
首先需要做的是将上传的图像加载到 Bitmap 对象中并提取关键信息。
// 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 对象。此对象将处理图像数据。我们设置了几个渲染属性以生成高质量图像。
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。
using (Bitmap resizedBmp = new Bitmap(uploadedBmp, newWidth, newHeight))
{
Graphics 对象被命令将新图像绘制到新的、较小的 Bitmap 中。创建一个 MemoryStream 对象,然后新的 Bitmap 将文件以正确的 MIME 类型编码保存到内存中。内存文件被转换为 byte 数组,然后该数组被保存到磁盘。
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);
}
图像现在已经过调整大小并存储在磁盘上。下一步是创建另一个更小的相应缩略图,可以用作 GridView 控件中的预览。此步骤与第一个图像处理例程基本相同,只是完成图像的高度为 100 像素。此外,还省略了图像质量参数,并且为了迎合那些不愿使用 C# using 语句的用户,FileStream 和 Graphics 对象会通过编程方式关闭和释放。
// 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 的插入参数,并将记录插入表中。由于所有操作都发生在 Postback 期间,GridView 会自动使用新记录进行更新。
结论
现在我们已经上传了一个 JPG 图像文件,在保持纵横比的同时将其缩小并保存到磁盘,并附带一个相应的缩略图。这能有其他做法吗?更有效率吗?当然。这只是数十种可能技术中的一种。我已经在几个 Web 项目中成功使用了这项技术。欢迎在下方留下您的评论和建议。
历史
初始版本,2014 年 10 月