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

Picture Cache & DAL

starIconstarIconemptyStarIconemptyStarIconemptyStarIcon

2.00/5 (5投票s)

2007年11月26日

4分钟阅读

viewsIcon

25910

downloadIcon

269

Picture Cache & DAL(WebService & App cache)

Screenshot - pciturecache.jpg

引言

大家好:

这是一个演示项目……所以你可能会遇到异常……如果你想在实际生活中、实际应用中使用它,你需要做一些修改……

背景

我有一个包含大量图片的应用程序,这些图片存储在 SQL2005 中(出于安全和其他原因,而且我认为它并不比存储在文件系统中慢!)。

很多 BO(业务对象)都有图像(产品、客户、用户等),所以每个 BO 表都有一个指向 Image 数据表的 _img_id 外键。

我编写了自己的可绑定 PictureBox(见下文),我可以绑定 img_id 字段,Picture Box 就可以获取到图像。

从数据库中选择图片可能会对数据库造成很大的负载(用户打开产品详情,然后关闭,再打开,再关闭……)。所以我做了一个缓存。

这是一个非常简单的组件。我使用了单例模式,所以每个应用程序只有一个缓存(静态组件)。因此,当 PictureBox 获得一个 ID(int32)时,它会向自己的 PictureCache(应用程序中唯一的那个)请求图片,如果图片在内存中,就会被返回;如果不在,该组件会尝试从(SQL 或 WS)加载它,如果成功,就会返回 Image……

在示例应用程序中,它是一个缓存组件和 DBpicturebox 的简化版本。(你可以根据自己的需要进行开发……保存功能、释放内存“逻辑”等)

它运行完美……但是……一段时间后,我开发此应用程序的公司开设了第二个办公室,但无法将 SQL 服务器迁移到服务器场,所以它仍然留在第一个办公室,并通过 ADSL 连接,上传速度为 384-512kbps……

应用程序运行正常,但图片……太慢了……

我需要在“基础设施”中添加第二个缓存……最简单的方法是……将这个组件包装成一个 Web 服务

  • 远程客户端有一个本地的 Win2k3 服务器,上面运行着 Web 服务
  • Web 服务(带有静态 PictureCache 组件)有一个缓存
  • 当然,每个应用程序都有自己的应用程序缓存……

‘本地’客户端的获取方式
- 在内存中?是:使用它……否:查询 SQL

‘远程’客户端的获取方式
- 在内存中?是:使用它……否:查询 WS
- WS 在内存中?是:返回它,否:查询 SQL。

这样,冗余就消失了……

Using the Code

所以你需要……

  1. 一个包含一个 PictureCache 的测试应用程序
private localhost.Service1 ws = new PictureCache.localhost.Service1();

重要提示:在实际应用程序中使用它的静态实例……我在演示解决方案中使用了构造函数来演示它的工作原理!!!

初始化它

pc.MaxSizeInKbyte = 500; //max size of the app cache

pc.mode = PictureCacheDLL.PicCache.PCmode.WS_SQL; // the mode see below...
//set the connection u need
pc.SqlConn = new System.Data.SqlClient.SqlConnection("");

....

  1. 一个显示图片的 Picture Box
    public class DBPictureEdit:System.Windows.Forms.PictureBox
    {

        private int imgID = 0;

        public int ImgId
        {
            get 
            { 
                return imgID; 
            }
            set 
            {
                imgID = value;
                if (imgID == 0)
                {
                    this.Image = null;
                    return;
                }                
                if (pc != null)
                {
                    this.Image = pc.GetImage(value,true);
                }
                else
                {
                    throw new Exception();
                }
            }
        }

        public PicCache pc;
    }

初始化它

//set the PictureEdit PicCache
dbPictureEdit1.pc = this.pc;
  1. WebService

如果你设置了 WS|WS_SQL|SQL_WS 模式,缓存会在需要图片时,向 WebService 查询……所以你需要一个 IIS 和一个 Web 服务,里面包含一些函数。

    [WebService(Namespace = "http://tempuri.org/")]
    public class Service1 : System.Web.Services.WebService
    {

        public static PictureCacheDLL.PicCache pc;
 ....

初始化它
为 WS 项目添加一个 global.asax 文件

  
    public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {     
            //initialize the instance
            Service1.pc = PictureCacheDLL.PicCache.Instance;

            Service1.pc.MaxSizeInKbyte = 1500;

            //the web service local cache mode is SQL, ofc...
            Service1.pc.mode = PictureCacheDLL.PicCache.PCmode.SQL;

            //set the connection
            //Service1.pc.SqlConn = new System.Data.SqlClient.SqlConnection("");
        }
....

当然,它的模式是 SQL……(有可能远程客户端调用远程 WS,然后远程 WS 又调用另一个 WS,以此类推……但这可能会很有趣 :),不是一个真实的基础设施。)

主函数

 [WebMethod]
        public byte[] GetImageByte(int imgID, bool original)
        {
            return pc.GetImageByte(imgID, original);
        }
....

还有一些测试和演示函数

- 内存使用情况,清除图片,获取 ID……等等……

  1. 使用它……
 dbPictureEdit1.ImgId = int.Parse(textBox1.Text); 

缓存组件

这是一个简单的组件,带有一些功能。

主要功能和属性

重要:它返回 JPEG 格式的图片!!!!而不是原始格式!!!!

///the PictureCache- web service....
    private PicCacheWS.Service1 WS = new PictureCacheDLL.PicCacheWS.Service1();


//singleton pattern...
     private static PicCache instance = null;

        public static PicCache Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new PicCache();
                }
                return instance;
            }
        }
 
//main get Image function
   public Image GetImage(int img_id, bool originalSize)
        {
            if (img_id < 0)
            {
                return null;
            }
            if (listID.IndexOf(img_id) < 0)
            {
                if (!LoadImage(img_id))
                {
                    return null;
                }
            }
            DataRow[] dr = dstImages1.Images.Select(
                "img_id = " + img_id.ToString());
            if (dr.Length > 0 && dr[0]["img_image"] != System.DBNull.Value)
            {
                MemoryStream memStream = new MemoryStream(
                   (byte[])dr[0]["img_image"]);
                Image ret = Image.FromStream(memStream);
                Image retSmall;

                memStream = new MemoryStream();
                ret.Save(memStream, System.Drawing.Imaging.ImageFormat.Jpeg);
                retSmall = new Bitmap(Image.FromStream(memStream),
                    new Size(64, 64));

                if ((originalSize))
                {
                    return ret;
                }
                else
                {
                    return retSmall;
                }
            }
            else
            {
                return null;
            }
        }

加载方法

  • 如果模式是 WS,它只从 Webservice 加载图片
  • 如果模式是 SQL,它只从 SQL 加载图片
  • 如果模式是 SQL_WS,它先从 SQL 加载,然后从 WS 加载(如果未成功)
  • 如果模式是 WS_SQL,它先从 WS 加载,然后从 SQL 加载(如果未成功)
   private bool LoadImage(int img_id)
        {

            if (mode == PCmode.WS || mode == PCmode.WS_SQL)
            {
               if (LoadFromWS(img_id))
               {
                    FreeMemory();
                    listID.Add(img_id);
                    return true;
               }
               if (mode == PCmode.WS_SQL)
               {
                   if (LoadFromSQL(img_id))
                   {
                       FreeMemory();
                       listID.Add(img_id);
                       return true;
                   }
                   return false;
               }
               else
               {
                    return false;
               }
            }

            if (mode == PCmode.SQL ||mode == PCmode.SQL_WS)
            {
                if (LoadFromSQL(img_id))
                {
                    FreeMemory();
                    listID.Add(img_id);
                    return true;
                }
                else
                {
                    if (mode == PCmode.SQL_WS)
                    {
                        if (LoadFromWS(img_id))
                        {
                            FreeMemory();
                            listID.Add(img_id);
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                    return false;
                }               
            }

            return false;

        }

从数据库加载

       private bool LoadFromSQL(int img_id)
        {
               AdapterParamNull(adtxImages);
                int count = dstImages1.Images.Rows.Count;
                try
                {
                    adtxImages.SelectCommand.Parameters["@action"].Value = 
                        "S";
                    adtxImages.SelectCommand.Parameters["@img_id"].Value =
                        img_id;
                    adtxImages.Fill(dstImages1.Images);
                    if (dstImages1.Images.Rows.Count == count)
                    {
                        return false;
                    }
                    else
                    {                     
                        return true;
                    }
                }
                catch (Exception ex)
                {
                    return false;
                }
                finally
                {

                }
        }

从 WS 加载

       private bool LoadFromWS(int img_id)
        {
             try
                {                    
                    byte[] img = WS.GetImageByte(img_id, true);
                    if (img != null)
                    {
                        dstImages1.Images.AddImagesRow(img_id, img, "",
                            "", "", "", 0);
                        return true;
                    }
                    return false;
                }
                catch (Exception)
                {

                    return false;
                }
        }

大小,释放内存

 
  public void ClearMemory()
        {            
            while (dstImages1.Images.Rows.Count > 0)
            {                
                dstImages1.Images.Rows.RemoveAt(0);             
                listID.RemoveAt(0);
            }
        }

 private void FreeMemory()
        {
            //free the memory until the usage < maxSize
            while (dstImages1.Images.Rows.Count > 0 && mMaxSizeInKbyte < 
                SizeInKbyte())
            {
                //toroljuk a legregebbit;
                dstImages1.Images.Rows.RemoveAt(0);
                //listID.Remove((int)dstImages1.Images.Rows[0]["img_id"]);
                listID.RemoveAt(0);
            }
        } 

  public int SizeInKbyte()
        {
            int size = 0;
            foreach (DataRow act in dstImages1.Images.Rows)
            {
                size += ((byte[])act["img_image"]).Length;
            }
            size = size / 1024;
            return size;
        }

最后

所以这是我的 CacheComponent 的一个简单部分,你可以使用它,以你自己的方式进行开发……

在慢速网络上,它可能比直接访问 SQL(没有缓存)更快。

可能的升级/功能

  • 保存功能
  • 在远程 WS 中增加本地缓存(存储在本地文件系统中,并在启动时加载到内存……),如果数据库中的图片是只读的……
  • 更智能的清理逻辑:计算图片的访问次数,按次数排序,然后按此顺序清理……
  • 获取小尺寸/缩略图
  • 增强 Picture Box……
© . All rights reserved.