这是本次项目的最后一个问题了,把这个问题解决了,项目就彻底竣工了。先说说要达到的效果:点击一个关键字,使用URL重写传值给Servlet,然后从数据库中查出所有包含这个关键字的文章。
然而在JSP中我们点击这样一个超级链接:http://localhost:8080/wwwroot/tagservlet?tag=数据库
可是这样传递的值我们在Servlet中是没法获取到的,我们需要对这里的汉字进行URL编码,这样我们才能够在Servlet中使用request.getParameter("tag")获得我们传递过来的值。为此,我们需要导入java.net.URLEncoder这个包,接下来使用URLEncoder.encode("数据库")来将“数据库”这三个字进行转码得到的结果是“%CA%FD%BE%DD%BF%E2”,如此一来我们的链接就变为了:http://localhost:8080/wwwroot/tagservlet?tag=%CA%FD%BE%DD%BF%E2
然而,当用request.getParameter("tag")获取这个值并将其输出,你会发现是一堆“?”,这里还得在转码才能得到汉字:
String tag = request.getParameter("tag");
tag = new String(tag.getBytes("ISO-8859-1"),"GBK");
这下终于好了,为什么汉字总是这么麻烦,老要转来转去的,老外的字母就没有问题…
以前写的那个[jsp中实现分页显示数据] 方法不通用,得找个分页标签。关于pager-taglib的使用方法网上虽说一大堆,但是同样费我不少脑细胞去看。有的代码不齐全,有的根本就是错误的,有的写的很潦草,看的我是云里雾里的,经过本人耗费大量的脑细胞奋战N个小时终于搞出了点猫腻。
想明白了,写出来运行成功了,我才发现其实用法很简单,没那些人写的那么麻烦。
首先当然还是要导pager-taglib.jar包了,可以去http://jsptags.com/tags/navigation/pager/index.jsp下载,从下载下来的war文件中找到pager-taglib.jar包以及pager-taglib.tld文件。将pager-taglib.tld文件放在WEB-INF目录下,将pager-taglib.jar放在WEB-INF/lib目录下(JBuilder中的导入方法可以参考[Servlet中实现文件上传] )。
新建一个bean文件定义如下两个方法(不一定非要这个样子):
- /**
- * 该方法用于获得数据总数
- * @return int
- */
- public int getArticlesCount() {
- String sql = "SELECT COUNT(*) FROM blog_Article";
- dbc = new DataSource();
- stmt = dbc.getPreparedStatement(sql);
- rs = dbc.getResultSet(stmt);
- int count = 0;
- try {
- if(rs.next()){
- count = rs.getInt(1);
- }
- } catch (SQLException ex) {
- ex.printStackTrace();
- } finally {
- dbc.closeConnection();
- }
- return count;
- }
- /**
- * 该方法用于数据分页
- * @param begin int
- * @param end int
- * @return ArrayList
- */
- public ArrayList getArticlesAll(int begin,int end) {
- String sql = "select top "+end+" * from blog_Article where (log_ID <= (select min(log_ID) from (select top "+begin+" log_ID from blog_Article order by log_PostTime desc) as t)) order by log_PostTime desc";
- ArrayList list = new ArrayList();
- dbc = new DataSource();
- stmt = dbc.getPreparedStatement(sql);
- rs = dbc.getResultSet(stmt);
- try {
- while (rs.next()) {
- ArticleBean article = new ArticleBean();
- article.setId(rs.getInt(1));
- //赋值操作…
- article.setTag(rs.getString(10));
- article.setIsTop(rs.getByte(11));
- list.add(article);
- }
- } catch (SQLException ex) {
- ex.printStackTrace();
- } finally {
- dbc.closeCon
nection(); - }
- return list;
- }
接下来新建一个JSP页面(需要JSTL):
- <%@ page contentType="text/html; charset=GBK" import="java.util.*,com.nkblog.bean.ArticleBean,com.nkblog.dbc.*" %>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
- <%@ taglib uri="WEB-INF/pager-taglib.tld" prefix="pg" %>
- <%
- request.setCharacterEncoding("GBK");
- String offset = request.getParameter("pager.offset");//pager.offset为此分页标签内置变量用于传递页数
- int page = 0;
- if(offset == null){
- //若offset为null则是第一次访问,所以显示第一页
- page= 1;
- }else{
- //反之,则按照传递来的页数来分
- page = Integer.parseInt(offset)+1;
- }
- //获取数据总数
- int dataTotal = ArticleManager.getArticlesCount();
- //查询分页数据,10为每页要显示的数据量,可自定义
- ArrayList articles = ArticleManager.getArticlesAll(page,10);
- %>
- <%
- for(int i=0;i<articles.size();i++)
- {
- //输出取出的10条数据…
- }
- %>
- <pg:pager scope="request" maxIndexPages="5" index="center" maxPageItems="10" url="index.jsp" items="<%=dataTotal%>" %>" export="currentPageNumber=pageNumber">
- <pg:first><a href="${pageUrl}">首页</a></pg:first>
- <pg:prev><a href="${pageUrl}">前页</a></pg:prev>
- <pg:page>
- </pg:page>
- <pg:pages>
- <c:choose>
- <c:when test="${pageNumber eq currentPageNumber }">
- <font color="red">${pageNumber }</font>
- </c:when>
- <c:otherwise>
- <a href="${pageUrl }">${pageNumber}</a>
- </c:
otherwise> - </c:choose>
- </pg:pages>
- <pg:next><a href="${pageUrl}">下页</a></pg:next>
- <pg:last><a href="${pageUrl}">尾页</a></pg:last>
- </pg:pager>
接下来具体说明上面的分页标签<pg:pager>
maxIndexPages为分页条个数,形如:< << 1 2 3 4 5 >> >
maxPageItems为每页要显示的数据量
url为处理分页请求的文件,可以是JSP或Servlet。当程序运行起来后,会以index.jsp?pager.offset=5的形式传递页数。
items为数据总数,pager-taglib会自动根据以上参数进行分页。
需要进行修改的也就上面这几个参数,起初看别人写的方法,我以为只要导入这个标签然后给定一个List数据集合,它就完全自动的跟也并显示数据了,汗~~也不知道我这么写会有几个看得懂的,估计懂的人有,但是能看明白我写的就少了…我发现原来代码写起来简单,但是要给别人将明白了真的好难啊。
很久以前写的那个[文件上传] 是在JSP页面中实现的,此次我们来要在Servlet中实现文件的上传处理,两种方法的代码是不一样的,我看到有很多人说在Servlet中要使用commons-fileupload.jar的话还需要commons-io.jar提供支持而在JSP中不需要,经过我的测试,MS在Servlet中也是不需要这个commons-io.jar。
下面我们开始实现Servlet处理文件上传,首先我们需要去下载上面提到的两个jar包,下载地址分别为:
http://commons.apache.org/fileupload
http://commons.apache.org/io/
然后将这两个包导入到你的Web工程下的WEB-INF中的lib目录中去,没有lib的话请自建。另外如果你使用的是JBuilder来做的话,这样放进去是没有用的,使用的时候会提示DOES NOT EXIST。需要按照如下方法导入:
1.选则菜单栏中的“Tools”->“Configure”->“Libraies”。
2.点击“New”,Name这里随便输入(例如:CommonsFileUpload),最好是见其名知其意,Location这里可以不管他。
3.点击“Add”,将那两个包都导入进来,最后一路“OK”。
4.在你的工程命(例如:Neeke.jpx)上右键并选择“Properties”进入“Properties for ‘Neeke.jpx’”对话框,选择“Required Libraries”项,选择“Add”,找到你刚才新建的那个“CommonsFileUpload”然后一路“OK”。
现在就可以正常使用了,且JBuilder会自动将这两个包放在lib下。
新建一个upload.jsp,其代码如下:
<%@ page contentType="text/html; charset=GBK" %> <html> <head> <title> upload </title> </head> <body> <form action="../uploadservlet" method="POST" enctype="Multipart/form-data"> <input type="file" name="file" /><input type="submit" value="上传" /> </form> </body> </html>
需要注意的是表单form的enctype属性一定要写“Multipart/form-data”,没有什么为什么!
接下来我们新建一个Servlet为UploadServlet(要有DOPOST方法)并导入如下几个包:
org.apache.commons.fileupload.*;
org.apache.commons.io.*;
org.apache.commons.fileupload.servlet.ServletFileUpload;
org.apache.commons.fileupload.disk.DiskFileItemFactory;
由于在JBuilder中doPost()调用了doGet()方法,所以我们可以直接改写doGet()方法,具体代码如下:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();
DiskFileItemFactory fac = new DiskFileItemFactory();
//设置缓存文件大小
fac.setSizeThreshold(1024*1024);
//缓存文件位置,这里取的是默认的位置
fac.setRepository(fac.getRepository());
ServletFileUpload upload = new ServletFileUpload(fac);
//设置最大允许上传的文件大小,这里是5MB
upload.setFileSizeMax(1024*1024*5);
List fileList = null;
try {
fileList = upload.parseRequest(request);
} catch (FileUploadException ex) {
response.sendRedirect("admin/upload.jsp?result=size");
ex.printStackTrace();
return;
}
Iterator iter = fileList.iterator();
while(iter.hasNext()){ i>
FileItem fileItem = (FileItem)iter.next();
if(!fileItem.isFormField()){
String name = fileItem.getName();
String fileSize = new Long(fileItem.getSize()).toString();
if(name == null || name.equals("") || fileSize.equals("0"))
continue;
//截取出纯文件名
name = name.substring(name.lastIndexOf("\\")+1);
//存储文件
File saveFile = new File("d:\\upload\\"+name);
try {
fileItem.write(saveFile);
} catch (Exception ex1) {
ex1.printStackTrace();
return;
}
}
}
out.close();
}
这样这个Servlet就能够实现对文件的上传了,这里文件的缓存大小及文件大小限制都可以动态的设置,我想这个问题具体就不需要我写了吧,嘿嘿。
前天做了那个[JSP网站RSS的实现] ,此乃用Java写XML文件,学东西我们要学全(尽可能的全),不能只知其一不知其二,今天来实现在Java中对XML类型文件的读取。
在Java中要实现对XML文件的读取,首先,我们要导入一下几个包。
javax.xml.parsers.DocumentBuilder;
相关解释:
定义 API, 使其从 XML 文档获取 DOM 文档实例。使用此类,应用程序员可以从 XML 获取一个 Document。
javax.xml.parsers.DocumentBuilderFactory;
相关解释:
定义工厂 API,使应用程序能够从 XML 文档获取生成 DOM 对象树的解析器。
org.w3c.dom.Document;
相关解释:
Document 接口表示整个 HTML 或 XML 文档。从概念上讲,它是文档树的根,并提供对文档数据的基本访问。
org.w3c.dom.NodeList;
相关解释:
NodeList 接口提供对节点的有序集合的抽象,没有定义或约束如何实现此集合。DOM 中的 NodeList 对象是活动的。
NodeList 中的项可以通过从 0 开始的整数索引进行访问。
了解了这些知识,我们就可以在Java中像写JavaScript一样获得XML文件中的数据了。
下面是一个具体的方法用于读取前天创建的RSS文件:
- public static void getArticleList(String fpath){
- try{
- DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
- DocumentBuilder db=dbf.newDocumentBuilder();
- Document doc=db.parse(fpath);
- NodeList nodeList=doc.getElementsByTagName("item");
- for(int i=0;i<nodeList.getLength();i++){
- String title=doc.getElementsByTagName("title").item(i).getFirstChild().getNodeValue();
- String author=doc.getElementsByTagName("author").item(i).getFirstChild().getNodeValue();
- String pubDate=doc.getElementsByTagName("pubDate").item(i).getFirstChild().getNodeValue();
- String description=doc.getElementsByTagName("description").item(i).getFirstChild().getNodeValue();
- String category=doc.getElementsByTagName("category").item(i).getFirstChild().getNodeValue();
- System.out.println(title);
- System.out.println(author);
- System.out.println(pubDate);
- System.out.println(description);
- System.out.println(category);
- }
- }catch(Exception e){
- System.out.println(e.getMessage());
- }
- }
虽然也学了这么久的J2EE了(基础型),但是我一直都搞不明白:MVC模式是要分三层的,显示层、控制层(业务逻辑层)及数据层,业务逻辑层才能访问数据层,而显示层不能够直接访问数据层。既然如此,那么网站的首页所需要的数据都是来自数据库的,是要动态读取的,这就要在jsp中直接访问数据层,岂不是相互矛盾了吗?
去了几个技术论坛看了看,有人说那只是一个高效的框架模型,不一定非要遵守,可以直接在首页jsp中调用数据层的方法,活人岂能被尿憋死?话虽说有那么点道理,不过我还是不想这么试。
我是这么想的:
方法1.能不能打开网站地址时候访问的第一个文件是一个Servlet,在Servlet中获得首页用于显示所需要的所有数据,然后将它放在request中,接着在使用
request.getRequestDispatcher("index.jsp").forward(request, response);
跳转到真实的首页,如此即可在index.jsp中从request中获得所要的数据了。那么,如何才能打开网址就直接访问Servlet呢?在网站的根目录下WEB-INF中的web.xml中有这么一对标签:
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
类似于配置IIS中的默认首页 ,我们把这里的index.jsp改为对应的Servlet即可。
方法2.创建一个过滤器,专门用来过滤首页文件,当请求首页文件时就在过滤器中取数据。过滤器本身其实也是Servlet。
我就晕了,无论用这里面哪一个方法,那这里面的Servlet到底属于哪一层啊?是不是我太钻牛角尖了?
昨天写了那个 [自定义配置文件] ,今天就又碰到问题了。如果我们定义的这个配置信息要能够支持HTML代码呢?比如要能够直接支持保存超级链接,统计代码啦诸如此类,那可就会爆出问题来。
例如,我们现在要保存一段51啦的统计代码:
<scrip tlanguage="javascript" type="text/javascript" src="http://js.users.51.la/1642363.js"></script>
<noscript>
<a href="http://www.51.la/?1642363" target="_blank"><img alt="我要啦免费统计" src="http://img.users.51.la/1642363.asp" style="border:none" /></a>
</noscript>
将它保存的时候是不会出错的,保存到配置文件中会是这样的:
Const ZC_BLOG_COPYRIGHT="<scrip tlanguage="javascript"
type="text/javascript"src="http://js.users.51.la/1642363.js"></script><noscript><a href="http://www.51.la/?1642363" target="_blank"><img alt="我要啦免费统计" src="http://img.users.51.la/1642363.asp" style="border:none" /></a></noscript>";
当我们第二次读取它的时候,按照我们上次的方法(截取“=”号和“;”之间部分)就出错啦。来看看它给我们截出来的是什么吧,先找到“=”紧接着找第一个“;”,然后把它们之间的截取出来,那就是:
<scrip tlanguage="javascript" type="text/javascript" src="http://js.users.51.la/1642363.js"></script>
<noscript><a href="http://www.51.la/?1642363" target="_blank"><img alt="ء
这不就完全错了吗,根本就不是我们想要得到的那段代码。怎么办?
首先,从“=”开始截取我们不用修改,我们要让它截取到该行的最后一个“;”,这样以来截取出来的就是我们想要的了,代码只需将int end = line.indexOf(";");修改为:int end = line.lastIndexOf(";");这样就OK了。
接下来还有一个问题,我们的配置文件的格式这这样的Const ZC_BLOG_COPYRIGHT="HTML代码";如果直接将“HTML代码”替换成上面的统计代码,虽然在我们修改读取的时候不会有问题,但是如果在网页的脚本中需要用的话那就出错了,为什么呢?因为统计代码中有“"”。
这个又要如何修改呢?也很简单,我们在写入代码到配置文件中之前先使用
copyRight = copyRight.replace(‘\"’,'\”);
将其中所有的双引号都替换为单引号,而取值修改的时候我们再反过来
copyRight = copyRight.replace(‘\”,’\"’);
把读取出来的代码中的所有单引号替换为双引号,这样用户在修改的时候看到的就和原来的一样了,而实际上的配置文件中却不是这样的,有点障眼法的意思,呵呵。
有个地方需要用到文件的物理路径,因为我需要调用方法来删除用户上传至服务器的文件。代码如下:
- /**
- * 用于删除上传的附件
- * @param url String
- * @return boolean
- */
- public static boolean deleteUploadFileByUrl(String url)
- {
- File file = new File(url);
- boolean ok = false;
- if(file.exists())
- {
- if(file.delete())
- {
- ok = true;
- }
- }
- return ok;
- }
可是直接传文件的相对路径当然不行啦。起初我想到的是request.getContextPath(),结果返回的是网站的相对根目录,没用,PASS掉!哎~忘了是用哪个了,我记得好像有个Server中的一个方法可以用来获取物理路径,晕~ASP.NET学时间长了,J2EE忘了差不多了…经过我分别对request和response的个个方法逐一查看(速度还是很快的)。终于被我给找到了。
request.getSession().getServletContext().getRealPath(“”);
这样就可以获得网站所在的物理路径了,OK问题解决,继续折腾…
(1) Http是一个无状态协议,它使用端口发送和接收消息,80端口是为Http开放的。
(2) Http请求消息使用GET或POST方法在Web上传输请求。GET方法传递的数据对客户端是可见的,查询字符串的长度是有长度限制的。POST方法发送数据对客户端不可见,且对发送的数据量没有限制。
(3) 基于Java技术的Web应用程序由Servlet、JSP页面、图像、HTML文件、JavaBean、Applet等组成。将Web应用程序文件打包成 .war文件,要求按特定的目录机构组织文件。目录层次演示:
http://127.0.0.1:8080/myroot
myroot 顶层目录
——myhtm.html 静态文本
——myjsp.jsp JSP页面文件
——WEB-INF 特定目录,不可引用
——classes Web应用程序所需要的任何其它类都存储在该文件夹下
——lib Web应用程荀所需的 .jar文件
——tags .tag标签文件存放目录
——.tld 标记库描述符文件
——web.xml 应用程序部署描述符文件
(4) Servlet是一个用Java编写的应用程序,在服务器上运行,处理请求的信息并将其发送到客户端。其生命周期为:实例化初始化[init()]服务[service()]破坏[destroy()]不可用。
(5) Servlet是实现javax.servlet.Servlet接口的对象,大多数Servlet从GenericServlet或HttpServlet类扩展而来。Servlet API包含于javax.servlet和javax.servlet.http两个包中。
(6) javax.servlet包的主要接口和类:
a) ServletInputStream类 用于从客户端读取二进制数据
b) ServletOutputStream类 向客户端发送二进制数据
c) GenericServlet类 抽象类
d) ServletRequest接口 用于封装客户向Servlet的请求信息
e) ServletResponse接口 Servlet响应对象
f) ServletContext接口 用于获取Servlet容器信息
g) ServletConfig接口 Servlet初始化时Servlet容器传递给Servlet的配置信息
h) Servlet接口 定义所有Servlet必须实现的方法
(7) javax.servlet.http包的主要接口和类:
a) HttpServletRequest接口 扩展ServletRequest接口
b) HttpServletResponse接口 扩展ServletResponse接口
c) HttpSession接口 用于标识客户端并存储有关客户端的信息
d) HttpServlet类 扩展GenericServlet类
e) Cookie类 用户创建Cookie
(8) 会话跟踪的常用方法包括URL重写、隐藏表单域和使用Cookie等技术。
(9) HttpServletRequest接口的getSession()和getSession(boolean value)方法用于创建会话,其中getSession(true)等同于getSession,当没有与当前请求关联的会话时,创建一个会话,而getSession(false)此时则会返回null。
(10) Servlet运行于一个称为ServletContext的环境,该环境描述了与Servlet关联的各种参数,一个Servlet只属于一个ServletContext。
(11) Servlet间通信的3种基本需求为:
a) 访问当前从Servlet加载的Servlet
b) 使用其它Servlet功能执行一项任务
c) 共享Servlet之间的状态信息
(12) JavaMail API用于发送和接收邮件,使用POP3或IMAP接收消息,使用SMTP发送消息。
(13) Javax.mail包中的类和接口:
a) Message抽象类 email邮件消息模型
b) Folder抽象类 用于管理email邮件,包含消息和自文件夹
c) Store抽象类 提供对文件加的访问方法并验证连接
d) Session类 定义了用来与邮件系统进行通信的邮件会话