开源项目WebImageView加载图片

项目地址:https://github.com/ZaBlanc/WebImageView

作者对加载图片,以及图片的内存缓存和磁盘缓存做了封装。

代码量不多,但是能够满足一般的加载图片。

先看下项目结构:

我认为通常情况下自己去实现的话,这点需要仔细看下。

/**
	 *
	 * @param urlString 对应图片的网络地址
	 * @return
	 */
	private String hashURLString(String urlString) {
	    try {
	        // Create MD5 Hash
	        MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
	        digest.update(urlString.getBytes());
	        byte messageDigest[] = digest.digest();

	        // Create Hex String
	        StringBuffer hexString = new StringBuffer();
	        for (int i=0; i<messageDigest.length; i++)
	            hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
	        return hexString.toString();

	    } catch (NoSuchAlgorithmException e) {
	        e.printStackTrace();
	    }

	    //fall back to old method
	    return urlString.replaceAll("[^A-Za-z0-9]", "#");
	}

以及这里的文件流操作

@Override
	protected Bitmap doInBackground(Void... params) {
		// check mem cache first先从内存中查找
		Bitmap bitmap = mCache.getBitmapFromMemCache(mURLString);

		// check disk cache first然后从磁盘中查找
		if (bitmap == null) {
			bitmap = mCache.getBitmapFromDiskCache(mContext, mURLString,
					mDiskCacheTimeoutInSeconds);
			if (bitmap != null) {
				//获取到后加入内存中缓存起来
				mCache.addBitmapToMemCache(mURLString, bitmap);
			}
		}

		if (bitmap == null) {
			InputStream is = null;
			FlushedInputStream fis = null;

			try {
				URL url = new URL(mURLString);
				URLConnection conn = url.openConnection();

				is = conn.getInputStream();
				fis = new FlushedInputStream(is);

				bitmap = BitmapFactory.decodeStream(fis);

				// cache
				if (bitmap != null) {
					mCache.addBitmapToCache(mContext, mURLString, bitmap);
				}
			} catch (Exception ex) {
				Log.e(TAG, "Error loading image from URL " + mURLString + ": "
						+ ex.toString());
			} finally {
				try {
					is.close();
				} catch (Exception ex) {
				}
			}
		}

		return bitmap;
	}

	@Override
	protected void onPostExecute(Bitmap bitmap) {
		// complete!完成后回调回去
		if (null != mListener) {
			if (null == bitmap) {
				mListener.onWebImageError();
			} else {
				mListener.onWebImageLoad(mURLString, bitmap);
			}
		}
	}

	static class FlushedInputStream extends FilterInputStream {
		public FlushedInputStream(InputStream inputStream) {
			super(inputStream);
		}

		@Override
		public long skip(long n) throws IOException {
			long totalBytesSkipped = 0L;

			while (totalBytesSkipped < n) {
				long bytesSkipped = in.skip(n - totalBytesSkipped);

				if (bytesSkipped == 0L) {
					int b = read();

					if (b < 0) {
						break; // we reached EOF
					} else {
						bytesSkipped = 1; // we read one byte
					}
				}

				totalBytesSkipped += bytesSkipped;
			}

			return totalBytesSkipped;
		}
	}

附上源代码:

WebImageCache

/*
	Copyright (c) 2011 Rapture In Venice

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

package com.raptureinvenice.webimageview.cache;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

/**
 * 处理图片缓存的类
 *
 */
public class WebImageCache {
	private final static String TAG = WebImageCache.class.getSimpleName();

	// cache rules
	/**
	 * 是否允许内存缓存
	 */
	private static boolean mIsMemoryCachingEnabled = true;
	/**
	 * 是否允许磁盘缓存
	 */
	private static boolean mIsDiskCachingEnabled = true;
	/**
	 * 缺省的默认超时显示时间
	 */
	private static int mDefaultDiskCacheTimeoutInSeconds = 60 * 60 * 24; // one day default 

	/**
	 * 缓存 软饮用?说好的弱引用呢??
	 */
	private Map<String, SoftReference<Bitmap>> mMemCache;

	public WebImageCache() {
		mMemCache = new HashMap<String, SoftReference<Bitmap>>();
	}

//	----------setter and getter
	public static void setMemoryCachingEnabled(boolean enabled) {
		mIsMemoryCachingEnabled = enabled;
		Log.v(TAG, "Memory cache " + (enabled ? "enabled" : "disabled") + ".");
	}

	public static void setDiskCachingEnabled(boolean enabled) {
		mIsDiskCachingEnabled = enabled;
		Log.v(TAG, "Disk cache " + (enabled ? "enabled" : "disabled") + ".");
	}

	public static void setDiskCachingDefaultCacheTimeout(int seconds) {
		mDefaultDiskCacheTimeoutInSeconds = seconds;
		Log.v(TAG, "Disk cache timeout set to " + seconds + " seconds.");
	}

	/**
	 * 从缓存中取出图片
	 * @param urlString  对应图片的URL,在cache中是键
	 * @return
	 */
	public Bitmap getBitmapFromMemCache(String urlString) {
		if (mIsMemoryCachingEnabled) {
			//同步缓存
			synchronized (mMemCache) {
				SoftReference<Bitmap> bitmapRef = mMemCache.get(urlString);

				if (bitmapRef != null) {
					Bitmap bitmap = bitmapRef.get();

					if (bitmap == null) {
						//键对应的Bitmap为空,则T掉。去掉无效值
						mMemCache.remove(urlString);
				        Log.v(TAG, "Expiring memory cache for URL " + urlString + ".");
					} else {
				        Log.v(TAG, "Retrieved " + urlString + " from memory cache.");
						return bitmap;
					}
				}
			}
		}

		return null;
	}

	/**
	 * 从磁盘中获取缓存图片
	 * @param context
	 * @param urlString
	 * @param diskCacheTimeoutInSeconds
	 * @return
	 */
	public Bitmap getBitmapFromDiskCache(Context context, String urlString, int diskCacheTimeoutInSeconds) {
		if (mIsDiskCachingEnabled) {
			Bitmap bitmap = null;
			File path = context.getCacheDir();
	        InputStream is = null;
	        String hashedURLString = hashURLString(urlString);

	        // correct timeout保证正确的超时设置
	        if (diskCacheTimeoutInSeconds < 0) {
	        	diskCacheTimeoutInSeconds = mDefaultDiskCacheTimeoutInSeconds;
	        }

	        File file = new File(path, hashedURLString);

	        if (file.exists() && file.canRead()) {
	        	// check for timeout
	        	if ((file.lastModified() + (diskCacheTimeoutInSeconds * 1000L)) < new Date().getTime()) {
	        		Log.v(TAG, "Expiring disk cache (TO: " + diskCacheTimeoutInSeconds + "s) for URL " + urlString);

	        		// expire
	        		file.delete();
	        	} else {
			        try {
			        	is = new FileInputStream(file);

			        	bitmap = BitmapFactory.decodeStream(is);
				        Log.v(TAG, "Retrieved " + urlString + " from disk cache (TO: " + diskCacheTimeoutInSeconds + "s).");
			        } catch (Exception ex) {
			        	Log.e(TAG, "Could not retrieve " + urlString + " from disk cache: " + ex.toString());
			        } finally {
			        	try {
			        		is.close();
			        	} catch (Exception ex) {}
			        }
	        	}
	        }			

			return bitmap;
		}

		return null;
	}

	/**
	 * 将图片加入缓存中
	 * @param urlString 对应的http网络地址
	 * @param bitmap 对应的Bitmap对象
	 */
	public void addBitmapToMemCache(String urlString, Bitmap bitmap) {
		if (mIsMemoryCachingEnabled) {
			synchronized (mMemCache) {
				mMemCache.put(urlString, new SoftReference<Bitmap>(bitmap));
			}
		}
	}

	/**
	 * 将图片加入磁盘缓存中
	 * @param context
	 * @param urlString
	 * @param bitmap
	 */
	public void addBitmapToCache(Context context, String urlString, Bitmap bitmap) {
		// mem cache
		addBitmapToMemCache(urlString, bitmap);

		// disk cache
		// TODO: manual cache cleanup
		if (mIsDiskCachingEnabled) {
			File path =  context.getCacheDir();
	        OutputStream os = null;
	        String hashedURLString = hashURLString(urlString);

	        try {
		        // NOWORKY File tmpFile = File.createTempFile("wic.", null);
		        File file = new File(path, hashedURLString);
		        os = new FileOutputStream(file.getAbsolutePath());

		        //对图片进行压缩处理
		        bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
		        os.flush();
		        os.close();

		        // NOWORKY tmpFile.renameTo(file);
	        } catch (Exception ex) {
	        	Log.e(TAG, "Could not store " + urlString + " to disk cache: " + ex.toString());
	        } finally {
	        	try {
	        		os.close();
	        	} catch (Exception ex) {}
	        }
		}
	}

	/**
	 *
	 * @param urlString 对应图片的网络地址
	 * @return
	 */
	private String hashURLString(String urlString) {
	    try {
	        // Create MD5 Hash
	        MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
	        digest.update(urlString.getBytes());
	        byte messageDigest[] = digest.digest();

	        // Create Hex String
	        StringBuffer hexString = new StringBuffer();
	        for (int i=0; i<messageDigest.length; i++)
	            hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
	        return hexString.toString();

	    } catch (NoSuchAlgorithmException e) {
	        e.printStackTrace();
	    }

	    //fall back to old method
	    return urlString.replaceAll("[^A-Za-z0-9]", "#");
	}
}

WebImageManagerRetriever

/*
	Copyright (c) 2011 Rapture In Venice

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
 */

package com.raptureinvenice.webimageview.download;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;

import com.raptureinvenice.webimageview.cache.WebImageCache;

/**
 *
 * 异步获取图片
 */
public class WebImageManagerRetriever extends AsyncTask<Void, Void, Bitmap> {
	private final static String TAG = WebImageManagerRetriever.class
			.getSimpleName();

	// cache
	private static WebImageCache mCache;

	// what we're looking for
	private Context mContext;
	private String mURLString;
	private int mDiskCacheTimeoutInSeconds;
	/**
	 * 回调
	 */
	private OnWebImageLoadListener mListener;

	static {
		mCache = new WebImageCache();
	}

	public WebImageManagerRetriever(Context context, String urlString,
			int diskCacheTimeoutInSeconds, OnWebImageLoadListener listener) {
		mContext = context;
		mURLString = urlString;
		mDiskCacheTimeoutInSeconds = diskCacheTimeoutInSeconds;
		mListener = listener;
	}

	@Override
	protected Bitmap doInBackground(Void... params) {
		// check mem cache first先从内存中查找
		Bitmap bitmap = mCache.getBitmapFromMemCache(mURLString);

		// check disk cache first然后从磁盘中查找
		if (bitmap == null) {
			bitmap = mCache.getBitmapFromDiskCache(mContext, mURLString,
					mDiskCacheTimeoutInSeconds);
			if (bitmap != null) {
				//获取到后加入内存中缓存起来
				mCache.addBitmapToMemCache(mURLString, bitmap);
			}
		}

		if (bitmap == null) {
			InputStream is = null;
			FlushedInputStream fis = null;

			try {
				URL url = new URL(mURLString);
				URLConnection conn = url.openConnection();

				is = conn.getInputStream();
				fis = new FlushedInputStream(is);

				bitmap = BitmapFactory.decodeStream(fis);

				// cache
				if (bitmap != null) {
					mCache.addBitmapToCache(mContext, mURLString, bitmap);
				}
			} catch (Exception ex) {
				Log.e(TAG, "Error loading image from URL " + mURLString + ": "
						+ ex.toString());
			} finally {
				try {
					is.close();
				} catch (Exception ex) {
				}
			}
		}

		return bitmap;
	}

	@Override
	protected void onPostExecute(Bitmap bitmap) {
		// complete!完成后回调回去
		if (null != mListener) {
			if (null == bitmap) {
				mListener.onWebImageError();
			} else {
				mListener.onWebImageLoad(mURLString, bitmap);
			}
		}
	}

	static class FlushedInputStream extends FilterInputStream {
		public FlushedInputStream(InputStream inputStream) {
			super(inputStream);
		}

		@Override
		public long skip(long n) throws IOException {
			long totalBytesSkipped = 0L;

			while (totalBytesSkipped < n) {
				long bytesSkipped = in.skip(n - totalBytesSkipped);

				if (bytesSkipped == 0L) {
					int b = read();

					if (b < 0) {
						break; // we reached EOF
					} else {
						bytesSkipped = 1; // we read one byte
					}
				}

				totalBytesSkipped += bytesSkipped;
			}

			return totalBytesSkipped;
		}
	}

	public interface OnWebImageLoadListener {
		public void onWebImageLoad(String url, Bitmap bitmap);

		public void onWebImageError();
	}
}

WebImageManager

/*
	Copyright (c) 2011 Rapture In Venice

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

package com.raptureinvenice.webimageview.download;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import android.content.Context;
import android.graphics.Bitmap;

import com.raptureinvenice.webimageview.download.WebImageManagerRetriever.OnWebImageLoadListener;
import com.raptureinvenice.webimageview.image.WebImageView;

public class WebImageManager implements OnWebImageLoadListener {
	private static WebImageManager mInstance = null;

	// TODO: pool retrievers

	// views waiting for an image to load in
	private Map<String, WebImageManagerRetriever> mRetrievers;
	private Map<WebImageManagerRetriever, Set<WebImageView>> mRetrieverWaiters;
	private Set<WebImageView> mWaiters;

	public static WebImageManager getInstance() {
		if (mInstance == null) {
			mInstance = new WebImageManager();
		}

		return mInstance;
	}

	private WebImageManager() {
		mRetrievers = new HashMap<String, WebImageManagerRetriever>();
		mRetrieverWaiters = new HashMap<WebImageManagerRetriever, Set<WebImageView>>();
		mWaiters = new HashSet<WebImageView>();
	}

	/**
	 * 处理多个同时加载图片
	 * @param context
	 * @param urlString
	 * @param view
	 * @param diskCacheTimeoutInSeconds
	 */
	public void downloadURL(Context context, String urlString, final WebImageView view, int diskCacheTimeoutInSeconds) {
		WebImageManagerRetriever retriever = mRetrievers.get(urlString);

		if (mRetrievers.get(urlString) == null) {
			retriever = new WebImageManagerRetriever(context, urlString, diskCacheTimeoutInSeconds, this);
			mRetrievers.put(urlString, retriever);
			mWaiters.add(view);

			Set<WebImageView> views = new HashSet<WebImageView>();
			views.add(view);
			mRetrieverWaiters.put(retriever, views);

			// start!
			retriever.execute();
		} else {
			mRetrieverWaiters.get(retriever).add(view);
			mWaiters.add(view);
		}
	}

    public void reportImageLoad(String urlString, Bitmap bitmap) {
        WebImageManagerRetriever retriever = mRetrievers.get(urlString);

        for (WebImageView iWebImageView : mRetrieverWaiters.get(retriever)) {
            if (mWaiters.contains(iWebImageView)) {
                iWebImageView.setImageBitmap(bitmap);
                mWaiters.remove(iWebImageView);
            }
        }

        mRetrievers.remove(urlString);
        mRetrieverWaiters.remove(retriever);
    }

	public void cancelForWebImageView(WebImageView view) {
		// TODO: cancel connection in progress, too
		mWaiters.remove(view);
	}

    @Override
    public void onWebImageLoad(String url, Bitmap bitmap) {
        reportImageLoad(url, bitmap);
    }

    @Override
    public void onWebImageError() {
    }
}

开源项目WebImageView加载图片

时间: 2024-10-27 10:07:15

开源项目WebImageView加载图片的相关文章

Android开源框架ImageLoader:加载图片的三级缓存机制

前言:可从  https://github.com/nostra13/Android-Universal-Image-Loader 下载三级缓存机制的开源框架.下文简单介绍该框架中主要的常用方法,掌握这些方法,基本就可应对多数图片下载的需求. 注意:以下代码为示意代码片断,仔细读一下应能知道怎么用.蓝色表示为开源框架中的类. 1.初始化ImageLoader类对象: ImageLoader imageLoader = ImageLoader.getInstance(); imageLoader.

django之创建第12个项目-加载图片

百度云盘:django之创建第12个项目-加载图片 1.setting配置 #静态文件相关配置 # URL prefix for static files. # Example: "http://example.com/static/", "http://static.example.com/" STATIC_URL = '/static/' import os BASE_DIR = os.path.dirname(os.path.dirname(__file__)

UI小项目之拳皇动画的实现(抽取加载图片和播放音乐的方法)

实现思路 1.加载图片 2.播放音乐 实现思想 1.封装思想 抽取相同代码生成一个新的方法,通过传递参数调用该方法: 2.内存管理思想 不需要每次调用方法时都重新加载图片,for循环加载图片写在ViewdidLoad中 下列代码没有对运行过程中内存管理进行优化 其中加载图片有两种方法: 通过imageNmae加载有缓存 通过imageWithContentsOfFile加载无缓存 有无缓存的区别: 有缓存,使用时不需要重新加载 无缓存,使用时才加载 #import "ViewController

重构项目之二:使用瀑布流效果加载图片

虽然最后这个功能使用了另外的插件,但是还是一下大概的原理吧,还是先上图: 功能描述: 根据不同的菜单的属性分别加载不同的数据 下拉滚动条到一定位置预加载图片,滚动条拉到最底下的时候渲染html: 鼠标移到菜单,切换各个列表图片: 鼠标移到图片上,显示详细信息: 技术实现方案:

Silverlight项目笔记7:xml/json数据解析、MVVM下实现多级树形结构TreeView、忽视引用类型导致数据绑定错误、通过流或动态空间加载图片、虚拟目录设置、silverlight安全机制引发的问题、WebClient缓存问题

涉及的内容主要有: 1.xml/json数据解析 2.多级树形结构TreeView 3.忽视引用类型导致数据绑定错误 4.通过流或动态空间加载图片 5.虚拟目录设置 6.silverlight安全机制引发的问题 7.webclient缓存问题 1.xml/json数据解析 (1)xml数据解析 使用WebClient获取数据,获取到的数据实例化为一个XDocument,使用XDocument的Descendants(XName)方法获得对应节点的数据集合,再通过Element这个方法对数据集合进

Android:ViewPager扩展详解——带有导航的ViewPagerIndicator(附带图片缓存,异步加载图片)

大家都用过viewpager了, github上有对viewpager进行扩展,导航风格更加丰富,这个开源项目是ViewPagerIndicator,很好用,但是例子比较简单,实际用起来要进行很多扩展,比如在fragment里进行图片缓存和图片异步加载. 下面是ViewPagerIndicator源码运行后的效果,大家也都看过了,我多此一举截几张图: 下载源码请点击这里 ===========================================华丽的分割线==============

RecyclerView使用 及 滑动时加载图片优化方案

RecyclerView使用 及 滑动时加载图片优化方案 简述 本篇博文主要给大家分享关于RecyclerView控件的使用及通过继承RecyclerView来实现滑动时加载图片的优化方案,也同样能解决防止图片乱序的问题,之前有在网上有看到大神对Android中ListView异步加载图片乱序问题进行过分析,并深入剖析原理后分别给出了3种对应的解决方案:一 .使用findViewWithTag.二.使用弱引用关联.三.使用Volley框架提供的NetworkImageView. 看了之后思索了很

android 网络加载图片,对图片资源进行优化,并且实现内存双缓存 + 磁盘缓存

经常会用到 网络文件 比如查看大图片数据 资源优化的问题,当然用开源的项目  Android-Universal-Image-Loader  或者 ignition 都是个很好的选择. 在这里把原来 写过的优化的代码直接拿出来,经过测试千张图片效果还是不错的. 免费培训课:http://www.jinhusns.com/Products/Curriculum/?type=xcj 工程目录 至于 Activity 就是加载了 1个网格布局 01./** 02.*   实现 异步加载 和   2级缓

C# C1TrueDBGrid控件如何加载图片列

表格中加载图片是很常见的功能,尤其是网页中,图片的展示更是随处可见.这个功能在bs中很容易就实现了: 前台代码: <asp:GridView ID="GridView1" runat="server"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:Image ID="img1" ImageUrl='<%#Eval("img