使用 ASP.NET 2.0 构建简单图片库






4.60/5 (17投票s)
2006年5月30日
7分钟阅读

321589

12965
一篇关于创建简单、易于维护且美观的照片库的文章。

引言
前一阵子,我花了相当多的时间在网上寻找我心目中完美的图片库。我尝试了网上已有的各种解决方案,但由于这样或那样的原因,我无法让它们满足我的需求。
- 我需要一个“开箱即用”的图片库。你所需要做的就是将几张图片放到一个文件夹中,仅此而已。
- 这个图片库还需要美观。
- 这个图片库应该易于使用。
我决定自己尝试一下。我相信,在一个周末内,我创建了一个满足我所有需求的东西。希望它也能满足你的需求。
步骤 1
第一步是在 Visual Studio .NET 2005 中创建一个“新建网站...”项目。创建完成后,你将拥有一个 Default.aspx 文件和一个 web.config 文件。在根目录中创建一个 PicStore 文件夹。这个文件夹将用于存放你的图片库。
在 PicStore 文件夹中,创建一个 Default 子文件夹。我们创建此文件夹是为了表示默认的图片库集合。这是为了将来,当我计划扩展应用程序以包含我朋友的图片库时。他们的图片库将位于其他文件夹中,但当用户首次浏览应用程序时,将加载 Default 图片库集合。
在 Default 文件夹中,创建你的图片库。为了本文的缘故,我建议创建 Photogallery 1、Photogallery 2 等。在每个图片库中,创建一个 pics 文件夹。此文件夹将用于存放你的图片。
注意
- 只接受 JPG 文件格式。
- 照片**必须**以 001.JPG、002.JPG、003.JPG 等模式命名。
- 如果你想为任何照片添加标题,请将其重命名为 00X~Title_With_Underscores_For_Spaces.JPG 的模式,其中 X 是照片的编号。
这个图片库设计用于清晰地显示最大宽度为 750 像素的图片。当然,你可以放置更大或更小的图片,但你需要相应地调整背景。我建议所有图片保持相同的最大宽度。你可以使用免费工具 Irfanview 批量将所有图像格式化为特定大小。在“批量转换 > 高级选项”中有一个选项可以将所有图像的长边设置为特定大小。这完美地符合了我的要求。
或者,如果你希望用户能够看到图片的缩略图而不是导航中的图像名称,你还可以在 pics 文件夹旁边创建一个兄弟文件夹 thumbs。我建议你的缩略图长边应限制为 100 像素。
我的示例代码中提供了一些图像,如 arrow2.gif、bg.gif 等。你可以下载这些并将它们放到项目根目录中相应的文件夹中,或者,从代码中删除对它们的引用。
我们最后要做的是将键 `PicRootPath` 和 `PicRootDefaultPath` 添加到 web.config 中,如下所示
<appSettings>
<add key="PicRootPath" value="~/PICStore/"/>
<add key="PicRootDefaultPath" value="~/PicStore/Default/" />
</appSettings>
以上是项目的准备工作。现在,我们将添加图片库的代码。
第二步
现在,如果项目中不存在 App_Code 文件夹,我们就创建一个。在其中,我们将创建一个名为 `ContentInfoLoader` 的类。这个类将负责为我们的应用程序加载图片库和照片的信息。我们类的构造函数将加载图片库的根路径和默认的图片库集合。以下是其代码
Configuration rootWebConfig =
WebConfigurationManager.OpenWebConfiguration("~/");
if(0<rootWebConfig.AppSettings.Settings.Count)
{
KeyValueConfigurationElement picRootElement =
rootWebConfig.AppSettings.Settings["PicRootPath"];
if(null!=picRootElement)
{
_picRootPath=picRootElement.Value;
}
picRootElement = rootWebConfig.AppSettings.Settings["PicRootDefaultPath"];
if (null != picRootElement)
{
_picRootDefaultPath = picRootElement.Value;
}
}
路径存储在私有变量中,可以通过只读属性访问。现在我们创建一个简单的方法来获取我们集合中可用的图片库列表。以下是其代码
public string[] GetGalleryPaths(string picRootRealPath)
{
if (Directory.Exists(picRootRealPath))
{
return Directory.GetDirectories(picRootRealPath);
}
else
{
return null;
}
}
如你所见,此方法接受图片根目录的路径(可以是默认路径或任何其他路径),并返回一个包含图片库路径的 string
数组。请注意,这些是物理路径。我们需要创建另一个方法来获取当前选定图片库中每张照片的列表。以下是执行此操作的代码
public string[] GetPhotoList(string galleryName, string picRootRealPath)
{
string galleryPath=picRootRealPath + "\\" + galleryName + "\\pics";
if (Directory.Exists(galleryPath))
{
return Directory.GetFiles(galleryPath,"*.JPG");
}
else
{
return null;
}
}
请注意,此方法接受图片库的名称和根路径。它返回包含其物理路径的照片列表。
步骤 3
现在我们创建一个母版页来配合我们的图片库应用程序页面。
首先,我建议删除项目自动创建的 Default.aspx 文件。接下来,在解决方案资源管理器中右键单击项目,然后单击“添加新项...”。选择“母版页”并将其命名为 SuperMasterPage.master。在母版页中,紧随 body
标签之后添加以下 JavaScript 代码
<script type="text/javascript">
var offsetfromcursorX=12 //Customize x offset of tooltip
var offsetfromcursorY=10 //Customize y offset of tooltip
//Customize x offset of tooltip DIV relative to pointer image
var offsetdivfrompointerX=10
//Customize y offset of tooltip DIV relative
//to pointer image. Tip: Set it to
//(height_of_pointer_image-1).
var offsetdivfrompointerY=7
//write out tooltip DIV
document.write('<div id="dhtmltooltip"></div>')
//write out pointer image
document.write('<img id="dhtmlpointer" ' +
'src="https://codeproject.org.cn/SourceCode/images/arrow2.gif">')
var ie=document.all
var ns6=document.getElementById && !document.all
var enabletip=false
if (ie||ns6)
var tipobj=document.all? document.all["dhtmltooltip"] :
document.getElementById?
document.getElementById("dhtmltooltip") : ""
var pointerobj=document.all? document.all["dhtmlpointer"] :
document.getElementById?
document.getElementById("dhtmlpointer") : ""
function ietruebody()
{
return (document.compatMode && document.compatMode!="BackCompat")?
document.documentElement : document.body
}
function ddrivetip(thetext, thewidth, thecolor)
{
if (ns6||ie)
{
if (typeof thewidth!="undefined")
tipobj.style.width=thewidth+"px"
if (typeof thecolor!="undefined" && thecolor!="")
tipobj.style.backgroundColor=thecolor
tipobj.innerHTML=thetext
enabletip=true
return false
}
}
function positiontip(e)
{
if (enabletip)
{
var nondefaultpos=false
var curX=(ns6)?e.pageX : event.clientX+ietruebody().scrollLeft;
var curY=(ns6)?e.pageY : event.clientY+ietruebody().scrollTop;
//Find out how close the mouse is to the corner of the window
var winwidth=ie&&!window.opera? ietruebody().clientWidth :
window.innerWidth-20
var winheight=ie&&!window.opera?
ietruebody().clientHeight : window.innerHeight-20
var rightedge=ie&&!window.opera?
winwidth-event.clientX-offsetfromcursorX :
winwidth-e.clientX-offsetfromcursorX
var bottomedge=ie&&!window.opera?
winheight-event.clientY-offsetfromcursorY :
winheight-e.clientY-offsetfromcursorY
var leftedge=(offsetfromcursorX<0)?
offsetfromcursorX*(-1) : -1000
//if the horizontal distance isn't enough to
//accomodate the width of the context menu
if (rightedge<tipobj.offsetWidth)
{
//move the horizontal position of
//the menu to the left by it's width
tipobj.style.left=curX-tipobj.offsetWidth+"px"
nondefaultpos=true
}
else if (curX<leftedge)
tipobj.style.left="5px"
else
{
//position the horizontal position of
//the menu where the mouse is positioned
tipobj.style.left=curX+offsetfromcursorX-
offsetdivfrompointerX+"px"
pointerobj.style.left=curX+offsetfromcursorX+"px"
}
//same concept with the vertical position
if (bottomedge<tipobj.offsetHeight)
{
tipobj.style.top=curY-tipobj.offsetHeight-
offsetfromcursorY+"px"
nondefaultpos=true
}
else
{
tipobj.style.top=curY+offsetfromcursorY+
offsetdivfrompointerY+"px"
pointerobj.style.top=curY+offsetfromcursorY+"px"
}
tipobj.style.visibility="visible"
if (!nondefaultpos)
pointerobj.style.visibility="visible"
else
pointerobj.style.visibility="hidden"
}
}
function hideddrivetip()
{
if (ns6||ie)
{
enabletip=false
tipobj.style.visibility="hidden"
pointerobj.style.visibility="hidden"
tipobj.style.left="-1000px"
tipobj.style.backgroundColor=''
tipobj.style.width=''
}
}
document.onmousemove=positiontip
</script>
此 JavaScript 代码将用于显示工具提示,以显示图片库名称和缩略图。
**注意:** 无论你在代码中看到“/SourceCode/”的地方,请将其替换为你的应用程序所在的“虚拟目录”。如果你将其创建在机器网站的根目录中,则在必要时删除 /SourceCode/。
步骤 4
现在我们需要创建实际的图片库显示页面,以便它可以使用我们上面创建的类。右键单击你的项目,然后单击“添加新项...”选择“Web 窗体”,并确保选中“将代码放在单独的文件中”和“选择母版页”复选框。打开 PicViewer.aspx 源文件。你将看到一个 asp:content
标签。在此标签内,放置以下代码
<table width="760px" cellpadding="1" cellspacing="0">
<tr>
<td colspan="5"><asp:PlaceHolder ID="galleryPlaceholder"
runat="server"></asp:PlaceHolder>
</td>
</tr>
<tr>
<td bgcolor="#494949" width="700px">
<asp:Label ID="lblGalleryName" runat="server"
CssClass="SubHeadingWhite">▼ How to use</asp:Label>
</td>
<td bgcolor="#494949"></td>
<td bgcolor="#494949"><asp:Label ID="lblPhotoName"
runat="server" CssClass="SubTitleWhite"></asp:Label></td>
</tr>
<tr>
<td align="center" colspan="5"><asp:Image ID="galleryImage"
runat="server" BorderStyle="Solid"
BorderWidth="5px" BorderColor="Black" />
</td>
</tr>
</table>
上述代码放置了一个名为 `galleryPlaceholder` 的占位符,我们稍后将向其中添加图片库和照片导航栏。除此之外,你还可以看到标签,它将显示图片的标题(如果有),否则显示图片的编号。
当 PicViewer 页面加载时,我们加载图片库的导航。如果这是第一次(没有显示图片库),我们显示 Howtouse.jpg 说明照片。如果这不是第一次,即用户单击了某个图片库,我们显示第一张图片。如果用户单击了某张图片,我们显示图片和标题。以下是 `Page_Load` 子例程的代码
protected void Page_Load(object sender, EventArgs e)
{
ContentInfoLoader cil = new ContentInfoLoader();
string galleriesPath = "";
if (Session["PicRootPath"] != null &&
Session["PicRootPath"].ToString() != "")
{
galleriesPath = Session["PicRootPath"].ToString();
}
else
{
galleriesPath = cil.PicRootDefaultPath;
Session["PicRootPath"] = galleriesPath;
}
LoadGalleriesNav(galleriesPath,cil);
if (Request["gallery"] != "" && Request["gallery"] != null)
{
lblGalleryName.Text = Request["gallery"];
Int32 photoCount =
LoadPhotosNav(Request["gallery"], galleriesPath, cil);
string photoName;
if (Request["photo"] == "" || Request["photo"] == null)
{
//If the user has just clicked on
//the gallery, load the first photo
photoName = cil.GetPhotoList(Request["gallery"],
Server.MapPath(galleriesPath))[0];
photoName = photoName.Substring(photoName.LastIndexOf("\\"));
}
else
{
//Otherwise load the photo as in the querystring
photoName = Request["photo"];
}
char[] charSeparators = new char[] { '~', '.' };
if (photoName.Contains("~"))
{
string photoTitle = photoName.Split(charSeparators)[1];
lblPhotoName.Text = photoTitle.Replace("_", " ");
}
else
{
lblPhotoName.Text = photoName.Split(charSeparators)[0];
}
galleryImage.ImageUrl = galleriesPath +
Request["gallery"] + "/pics/" + photoName;
}
else
{
galleryImage.ImageUrl = galleriesPath + "Howtouse.jpg";
}
}
图片库和照片的导航基本上是使用 CSS 格式化的无序列表。图片库列表和照片列表之间存在一些主要差异,除了明显的尺寸、形状和颜色之外
- 图片库列表,当我们鼠标悬停时,我们会看到图片库的名称。
- 照片列表,当我们鼠标悬停时,如果 `thumbs` 目录不存在,我们看到标题;否则,我们看到缩略图和标题。
在两种导航中,如果选择了某个图片库或选择了某个图片库和一张照片,这些项目将变灰且无法选择。除此之外,大部分代码都非常简单且不言自明。如果需要进一步解释,请告诉我。以下是代码
/// <summary>
/// This method is supposed to load the navigation for the galleries
/// </summary>
public void LoadGalleriesNav(string galleriesPath, ContentInfoLoader cil)
{
string[] galleryPathList = new string[1];
// Load the path info for all the galleries
if (galleriesPath != "")
{
galleryPathList =
cil.GetGalleryPaths(Server.MapPath(galleriesPath));
}
// Generate the unordered list
HtmlGenericControl blstGalleries = new HtmlGenericControl("ul");
blstGalleries.Attributes.Add("id", "navlist_a");
// Generate the navigation for the galleries
foreach (string galleryPath in galleryPathList)
{
string galleryName =
galleryPath.Substring(galleryPath.LastIndexOf("\\") + 1);
HtmlGenericControl galleryListItem = new HtmlGenericControl("li");
HtmlAnchor galleryAnchor = new HtmlAnchor();
if (Request["gallery"] != "" && Request["gallery"] != null)
{
// If the gallery has not been chosen, generate a link
if (Request["gallery"].ToUpper() != galleryName.ToUpper())
{
galleryAnchor.Attributes.Add("onMouseOver", "ddrivetip('" +
galleryName.ToUpper() + "', " +
galleryName.Length * 7 + ")");
galleryAnchor.Attributes.Add("onMouseOut", "hideddrivetip()");
galleryAnchor.HRef = "PicViewer.aspx?gallery=" + galleryName;
galleryAnchor.InnerHtml = "<em></em>";
}
else
{
// if a gallery has been chosen
// already then gray out the selection
galleryAnchor.Disabled = true;
galleryAnchor.InnerHtml = "<em style='border-top" +
":1em solid #696969'></em>";
}
}
else
{// If the gallery has not been chosen, generate a link
galleryAnchor.Attributes.Add("onMouseOver", "ddrivetip('" +
galleryName.ToUpper() + "', " +
galleryName.Length * 7 + ")");
galleryAnchor.Attributes.Add("onMouseOut", "hideddrivetip()");
galleryAnchor.HRef = "PicViewer.aspx?gallery=" + galleryName;
galleryAnchor.InnerHtml = "<em></em>";
}
// add the anchor to the list
galleryListItem.Controls.Add(galleryAnchor);
blstGalleries.Controls.Add(galleryListItem);
}
galleryPlaceholder.Controls.Add(blstGalleries);
}
/// <summary>
/// Load the navigation for the photos
/// </summary>
public Int32 LoadPhotosNav(string galleryName,
string galleriesPath, ContentInfoLoader cil)
{
string[] photoList = new string[1];
if (galleriesPath!= "")
{
photoList = cil.GetPhotoList(galleryName,
Server.MapPath(galleriesPath));
}
if (photoList != null && photoList.Length != 0)
{
HtmlGenericControl blstPhotos = new HtmlGenericControl("ul");
blstPhotos.Attributes.Add("id", "navlist_b");
foreach (string iPhotoName in photoList)
{
string photoName;
photoName =
iPhotoName.Substring(iPhotoName.LastIndexOf("\\") + 1);
HtmlGenericControl photoListItem = new HtmlGenericControl("li");
HtmlAnchor photoAnchor = new HtmlAnchor();
string originalPhotoName = photoName;
char[] charSeparators = new char[] { '~','.' };
string photoTitle = "";
if (photoName.Contains("~"))
{
photoTitle = photoName.Split(charSeparators)[1];
photoName = photoName.Split(charSeparators)[0] + ".JPG";
}
if (Request["photo"] != "" && Request["photo"] != null)
{
// if a photo has been selected then we need
// to show that square as gray otherwise
// clickable and mouseover effects.
if (Request["photo"].ToUpper() != photoName.ToUpper())
{
if (Directory.Exists(Server.MapPath(galleriesPath +
galleryName + "/thumbs/")))
{
photoAnchor.Attributes.Add("onMouseOver",
"ddrivetip('<img src=\\'" +
ResolveUrl(galleriesPath) + galleryName +
"/thumbs/" + photoName +
"\\' /><br>" +
photoTitle.Replace("_", " ") + "', " +
photoTitle.Length + ")");
}
else
{
if (photoTitle != "")
{
photoAnchor.Attributes.Add("onMouseOver",
"ddrivetip('" + photoTitle.Replace("_",
" ") + "', " + photoTitle.Length + ")");
}
else
{
photoAnchor.Attributes.Add("onMouseOver",
"ddrivetip('" + photoName + "', " +
photoName.Length + ")");
}
}
photoAnchor.Attributes.Add("onMouseOut",
"hideddrivetip()");
photoAnchor.HRef = "PicViewer.aspx?gallery=" +
galleryName + "&photo=" + originalPhotoName;
photoAnchor.InnerHtml = "<em></em>";
}
else
{
photoAnchor.Disabled = true;
photoAnchor.InnerHtml = "<em style='border-top" +
":0.5em solid #696969'></em>";
}
}
else
{
if (Directory.Exists(Server.MapPath(galleriesPath +
galleryName + "/thumbs/")))
{
photoAnchor.Attributes.Add("onMouseOver",
"ddrivetip('<img src=\\'" +
ResolveUrl(galleriesPath) +
galleryName + "/thumbs/" + photoName +
"\\' /><br>" + photoTitle.Replace("_",
" ") + "', " + photoTitle.Length + ")");
}
else
{
if (photoTitle != "")
{
photoAnchor.Attributes.Add("onMouseOver",
"ddrivetip('" +
photoTitle.Replace("_", " ") + "', " +
photoTitle.Length + ")");
}
else
{
photoAnchor.Attributes.Add(
"onMouseOver", "ddrivetip('" +
photoName + "', " + photoName.Length + ")");
}
}
photoAnchor.Attributes.Add("onMouseOut", "hideddrivetip()");
photoAnchor.HRef = "PicViewer.aspx?gallery=" + galleryName +
"&photo=" + originalPhotoName;
photoAnchor.InnerHtml = "<em></em>";
}
photoListItem.Controls.Add(photoAnchor);
blstPhotos.Controls.Add(photoListItem);
}
galleryPlaceholder.Controls.Add(blstPhotos);
return photoList.Length;
}
else
{
return 0;
}
}
应用程序现在已准备好进行测试。
如何安装演示/源代码
请注意,你必须安装 .NET Framework 2.0,并且你的网站应设置为使用 ASP.NET 2.0 (IIS 网站属性)。
- 在你的 IIS 上创建一个名为 SourceCode 的虚拟目录。
- 将 demo/sourcecode 的内容解压到相应的物理文件夹中。
已知问题
已知问题如下
- 一个图片库中不能添加超过 999 张照片。
- 图片库加载时,第一张照片的导航方块未变灰。
- 你无法提供照片的描述,只能提供标题。
- 没有用户友好的照片上传工具。
我确信还有其他需要修改的地方或需要添加的其他功能。请告诉我。
最后...
随着应用程序的升级,我也会升级本文。所以,请关注此空间。欢迎向我发送你对此文章的评论。请告诉我这篇文章是否对你有帮助,或者是否需要改进。