标签:
图片下载系统
需求:用户表增加一个级别字段。只有登录用户才能下载Images下的图片文件(Session中标识是否登录),如果用户没有登录则首先重定向到登录界面让用户登录,用户登录成功则跳转到下载列表页面,下载链接固定写好即可。如果登录用户是普通用户则在图片左上角加上“免费用户试用”的字样。
新建ImageDownload.aspx页面:
<form id="form1" runat="server"> <div> <asp:Label ID="Label1" runat="server" Text="用户名:"></asp:Label> <asp:TextBox ID="txtUserName" runat="server"></asp:TextBox> <br /> <asp:Label ID="Label2" runat="server" Text="密码:"></asp:Label> <asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox> <br /> <asp:Label ID="LabelErrorMsg" runat="server" BackColor="Red" Visible="False"></asp:Label> <br /> <asp:Button ID="btnLogin" runat="server" Text="登录" onclick="btnLogin_Click" /> </div> </form>
新建数据库PicUserDB.mdf,添加新表T_Users,字段:ID,类型int,自动增长;UserName,Password,类型为nvarchar(50);Level,类型为int,0表示免费会员,1表是收费会员。录入两条数据,张三,123,1;李四,123,0。用强类型DataSet建立数据访问:在项目中新建文件夹DAL,在DAL文件夹上右击选择“添加”“新建项”选择“数据”选择“数据集”,输入名称:DataSetPic,在新生成的DataSetPic.xsd中把T_Users表拖到上面。右击T_UsersTableAdapter,选择“添加查询”“使用SQL语句”“下一步”“SELECT(返回行)(S)”“下一步”,输入“SELECT ID, UserName, Password, [Level] FROM dbo.T_Users where ID=@ID”将“填充DataTable(I)”前面的选择取消,在方法名称中输入“GetDataByID”,点“完成”。完成了一个根据ID新建的查询。接着新建一个根据UserName生成的查询,语句“SELECT ID, UserName, Password, [Level] FROM dbo.T_Users
where UserName=@UserName”。
ImageDownload.aspx页面的后台代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using ImageDownload.DAL.DataSetPicTableAdapters; namespace ImageDownload { public partial class ImageDownload : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void btnLogin_Click(object sender, EventArgs e) { T_UsersTableAdapter adapter = new T_UsersTableAdapter(); var data = adapter.GetDataByUserName(txtUserName.Text); if (data.Count <= 0) { LabelErrorMsg.Text = "用户名不存在!"; LabelErrorMsg.Visible = true; Response.Redirect("DownloadList.aspx");//将用户重定向到Download.aspx } else { var user = data.Single();//Single方法:返回唯一一条数据,如果数据为0或者多条,,则抛出异常。把程序的错误扼杀在摇篮中。 if (user.Password == txtPassword.Text) { Session["是否登录"] = true; Session["登录用户Id"] = user.ID; } else { LabelErrorMsg.Text = "密码错误!";//todo:增加防暴力破解,重复错误5次就锁定帐户半个小时。 LabelErrorMsg.Visible = true; } } } } }
浏览ImageDownload.aspx页面,输入用户名,密码,页面运行正常。接着在项目中新建文件夹Images,考入几张图片,命名为1~7.jpg,新建DownloadList.aspx页面,
<form id="form1" runat="server"> <div> <a href="DownloadPic.ashx?FileName=1.jpg">美女1</a><br/> <a href="DownloadPic.ashx?FileName=2.jpg">美女2</a><br/> <a href="DownloadPic.ashx?FileName=3.jpg">美女3</a><br/> <a href="DownloadPic.ashx?FileName=4.jpg">美女4</a><br/> <a href="DownloadPic.ashx?FileName=5.jpg">美女5</a><br/> <a href="DownloadPic.ashx?FileName=6.jpg">美女6</a><br/> <a href="DownloadPic.ashx?FileName=7.jpg">美女7</a><br/> </div> </form>
浏览ImageDownload.aspx页面,登陆成功后就能正确导向DownloadList.aspx页面。
新建一般处理程序DownloadPic.ashx:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using ImageDownload.DAL.DataSetPicTableAdapters; using System.Drawing; using System.Web.SessionState; using System.Drawing.Imaging; namespace ImageDownload { /// <summary> /// DownloadPic 的摘要说明 /// </summary> public class DownloadPic : IHttpHandler,IRequiresSessionState { public void ProcessRequest(HttpContext context) { if (context.Session["是否登录"] == null) { context.Response.Redirect("ImageDownload.aspx");//ToDo:转到跳转页(3秒以后自动转向登录页面,也有一个链接点击直接进入登录页面) } else { string filename = context.Request["FileName"]; context.Response.ContentType = "image/JPEG"; string encodeFileName = HttpUtility.UrlEncode(filename); context.Response.AddHeader("Content-Disposition", string.Format("attachment;filename=\"{0}\"", encodeFileName)); int userId = (int)context.Session["登录用户Id"]; T_UsersTableAdapter adapter = new T_UsersTableAdapter(); var data = adapter.GetDataByID(userId); var user = data.Single(); if (user.Level == 0)//普通用户 { using (Bitmap bitmap = new Bitmap(context.Server.MapPath("images/" + filename))) { using (Graphics g = Graphics.FromImage(bitmap)) { g.DrawString("免费用户试用--" + user.UserName, new Font("宋体", 20), Brushes.Red, 0, 0); } bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);//为了演示,效率不高。 } } else//收费用户 { context.Response.WriteFile("images/" + filename);//有攻击漏洞,如果输入“下载图片.ashx?FileName=../下载图片.ashx.cs”还能将ashx源代码下载下来。 } } } public bool IsReusable { get { return false; } } } }
IRequiresSessionState这是一个很重要的接口,如果不加这个接口,该页面的session就不能用。
浏览DownloadList.aspx,点击超连接,导向到ImageDownload.axps页面,输入用户名:张三,密码:123,正确登录后,能正常下载图片;输入用户名:李四,密码:123,登录后下载图片,图片上会有“免费用户试用”的字样。
这样看上去没有什么问题,如果用户正常在页面点击,当然没有什么问题,但如果用户在网站输入http://localhost:1207/DownloadPic.ashx?FileName=../DownloadPic.ashx.cs,就会把网站DownloadPic.ashx.cs源代码下载下来,网站就不安全了。
在数据库中T_User增加两个字段:ErrorTimes(类型为int)、LastErrorTime,类型为datetime。由于数据库中的表修改了,所以要重新配置DataSetPic.xsd,右击T_Uwers表,选择“配置”,点击“查询生成器”,购先刚才添加的两个字段。点击“完成”。完成配置。在DataSetPic.xsd中添加更新方法,右击T_Uwers,选择“添加查询”“下一步”“UDPDATE(U)”“下一步”,输入:UPDATE T_Users SET ErrorTimes = ErrorTimes+1, LastErrorTime = getdate() WHERE ID=@ID,点击“下一步”,输入名称:IncErrorTimesByID,点击“完成”。再添加一个“Update”方法,重置错误次数,语句:UPDATE [T_Users] SET ErrorTimes=0 where ID=@ID,名称:ResetErrorTimesByID。
修改ImageDownload.aspx.cs中的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using ImageDownload.DAL.DataSetPicTableAdapters; namespace ImageDownload { public partial class ImageDownload : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void btnLogin_Click(object sender, EventArgs e) { T_UsersTableAdapter adapter = new T_UsersTableAdapter(); var data = adapter.GetDataByUserName(txtUserName.Text); if (data.Count <= 0) { LabelErrorMsg.Text = "用户名不存在!"; LabelErrorMsg.Visible = true; } else { var user = data.Single();//Single方法:返回唯一一条数据,如果数据为0或者多条,,则抛出异常。把程序的错误扼杀在摇篮中。 if (!user.IsLastErrorTimeNull() && !user.IsErrorTimesNull()) { double span = (DateTime.Now - user.LastErrorTime).TotalMinutes;//计算当前时间与上次错误时间之间的差的分钟数 if (user.ErrorTimes > 5 && span <= 30) { LabelErrorMsg.Text = "错误次数过多,30分钟后再重试!";//一旦被锁定就不告诉访问者的密码是对还是错 LabelErrorMsg.Visible = true; return; } } if (user.Password == txtPassword.Text) { Session["是否登录"] = true; Session["登录用户Id"] = user.ID; adapter.ResetErrorTimesByID(user.ID); Response.Redirect("DownloadList.aspx");//将用户重定向到Download.aspx } else { adapter.IncErrorTimesByID(user.ID);//注意数据库为Null的问题,IsNull函数。 LabelErrorMsg.Text = "密码错误!";//todo:增加防暴力破解,重复错误5次就锁定帐户半个小时。 LabelErrorMsg.Visible = true; } } } } }
浏览ImageDownload.aspx页面,输入用户名:张三,输入错的密码,多次确定,程序没有达到预期效果,这是因为数据库中的null+1=null的原因。数据库中有个ISNULL的方法,可以改变数据库中的值如select isnull(ErrorTimes,100) as expr1 form T_Users。修改DataSetPic.xsd中的IncErrorTimesByID方法,修改语句成:UPDATE T_Users SET ErrorTimes =IsNULL(ErrorTimes,0)+1, LastErrorTime = getdate() WHERE ID=@ID。
接下来新建RedirectLogin.htm页面:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script type="text/javascript"> var leftSeconds = 5; setInterval(function () { if (leftSeconds <= 0) { window.location.href = ‘ImageDownload.aspx‘; } document.getElementById("leftDiv").innerText = leftSeconds; leftSeconds--; }, 1000); </script> </head> <body>
请先登录,页面将在5秒后转向登录页面!如果你想立即进入登录页面,请<a href="ImageDownload.aspx">点击这里</a><br/>还剩<div id="leftDiv">0</div>秒
</body>
</html>
再将上面的DownloadPic.ashx文件中的context.Response.Redirect("ImageDownload.aspx");这名改成:context.Response.Redirect("RedirectLogin.htm");
浏览ImageDownload.aspx页面,达到要求。
补充:
1、WebApplication每次修改以后点击【生成解决方案】也能立即看到修改效果,不需要重启浏览器。原理:生成以后才将变化的部分生成到DLL,而WebSite则每次访问页面的时候会检查cs是否变了,变了则自动重新编译,所以每次修改以后立即有效果。
2、方便开发不用每次调试都设定起始页,在项目的选项中设定【Web】->启动操作->当前页面,这样当前激活的页就是起始页。
3、VS2008一个Bug的发现:路径中有#的问题,发现问题的过程重现。
WebForm1:
1、如果每次输出网页都直接用HttpHandler的话太痛苦了(开始我们建网页是这样做的,先建一个htm页面,再建一个ashx文件,把htm模板中的内容读取到ashx文件中,再发给用户。),所以一般生成html的时候都直接创建aspx(Web窗体,WebForm)。
2、WebForm分为两个文件aspx和aspx.cs,aspx是页面模板,是页面描述文件,就是html的内容,和aspx结合的更好,不用像一开始哪样程序员自己去填充模板,控件都是定义在aspx中,内联的JavaScript、Css也是写在aspx中的,服务端的C#代码是定义在aspx.cs中。aspx控制页面长相,cs控制程序逻辑,这种“前aspx后cs”的方式就被称为CodeBehind。aspx就是模板引擎,不需要再去寻找第三方的模板引擎。
WebForm2:
1、cs可以调用aspx中的控件,aspx中也可以访问cs中定义的字段、函数,还可以编写复杂的C#代码,for等所有C#代码都可以写在aspx中(不推荐)。
2、前面<%=UserName %> <%SayHello(); %> <%if(UserName=="aaa"){UserName="bbb";} %> 后面在当前位置输出表达式的值的时候使用<%=UserName %>,不要丢了=,相当于在当前位置调用Response.Write(UserName)。
Aspx页面:
<form id="form1" runat="server"> <div> <%for(int i=0;i<10;i++){%> 你好! <%} %> <br/> <%=MyName %> <br/> <%Response.Write("你好啊!"); %><br/> <% for (int i = 0; i < 10; i++) { Response.Write("哈哈"); } %><br/> <%SayHello(); %><br/> <%=QuotodStr("hello") %> </div> </form>
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class _15WebForm入门1_Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } public string MyName { get { return "Tom"; } } public void SayHello() { Response.Write("哈楼!"); } public string QuotodStr(string s) { return "\""+s+"\""; } }
3、使用的函数、代码相当于在这个位置调用函数、执行代码。注意aspx中调用cs的成员级别必须是Protected或者Public,不能是Private的。
aspx、cs、dll之间的关系(*)
1、在WebForm的页面中执行下面的代码:
Response.Write(this.GetType() + "<br/>");
Response.Write(this.GetType().Assembly.Location + "<br/>");
Response.Write(this.GetType().BaseType + "<br/>");
Response.Write(this.GetType().BaseType.Assembly.Location + "<br/>");
2、发现当前执行页面的类名是ASP.webform1_aspx这样的类名,父类才是ASPNETTest1.WebForm1。
3、使用Reflector打开这个临时dll,反编译这两个类,发现ASPNETTest1.WebForm1是在VS中编写的aspx.cs类,而ASP.webform1_aspx则是一个继承自ASPNETTest1.WebForm1的子类,ASP.webform1_aspx代码是根据aspx内容动态生成的构建网页内容类。综上,aspx最终也会生成一个类,这个类是继承自aspx.cs中的类。查看反编译以后的代码,可以看到就是编译生成了普通的.Net代码。因为aspx生成的代码是cs类的子类,所以就明白了为什么“aspx中调用cs的成员级别必须是Protected或者Public,不能是Private的。”
Page类成员:
1、Request、Response、Server属性:对Context.Request、Context.Response、Context.Server的简化调用。
2、AppRelativeVirtualPath属性:获得页面相对于应用根路径的路径,比如~/Default2.aspx
3、FindControl(CtrlId),根据控件的id找到控件。一般情况下直接在棋友中写控件Id引用控件就可以了,但是对于有些场合:使用ListView等控件的模板、编写自定义控件等则需要使用FindControl("TextBox1").txtBox.Text="aaa"。
4、IsPostBack、Session
5、ResolveClientUrl(url)将虚拟路径转换为客户端访问时的路径,比如ResolveClientUrl("~/a/b.aspx")结果是a/b.aspx,这通常在ListView等控件的模板中输出Html使用。基本就是对VirtualPathUtility.ToAbsolute简化调用。考虑当前页面的相对路径,生成的路径短。
6、ResolveUrl(url)将虚拟路径转换为相对于网站根目录的路径,比如ResolveUrl("~/a/b.aspx")的结果是/WebSite4/a/b.aspx。不考虑当前页面。VirtualPathUtility.ToAbsolute直接转换为一个全路径。
Aspx页面:
form id="form1" runat="server"> <div> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" /> </div> </form>
后台代码:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class _17Page类1_17Page类1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Response.Write(this.AppRelativeVirtualPath+"<br/>"); Response.Write(ResolveClientUrl("~/17Page类1/17Page类1.aspx")+"<br/>"); Response.Write(ResolveUrl("~/17Page类1/17Page类1.aspx")); } protected void Button1_Click(object sender, EventArgs e) { //TextBox1.Text = "1"; TextBox txt = FindControl("TextBox1") as TextBox; txt.Text = "2"; } }
标签:
原文地址:http://www.cnblogs.com/wujianwei/p/4988518.html