65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (14投票s)

2014年10月21日

CPOL

4分钟阅读

viewsIcon

31286

downloadIcon

796

本文将介绍并演示一种在本地文件系统上调整和存储图像文件,以及在 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 页面使用了标准的 FileUploadTextBoxButtonGridView 控件。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 月

调整和存储图像文件 - 文件系统和数据库第一部分 - CodeProject - 代码之家
© . All rights reserved.