这是本次项目的最后一个问题了,把这个问题解决了,项目就彻底竣工了。先说说要达到的效果:点击一个关键字,使用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");
这下终于好了,为什么汉字总是这么麻烦,老要转来转去的,老外的字母就没有问题…
项目终于快搞定了,就差最后那么一点点了…从开工到现在一直都有数据乱码的问题存在,起初也没太在意,先把整体搞定了再慢慢研究研究这个,现在是时候了!
数据是从我现在用的这个博客的ACCESS数据库转到MSSQL中的,有些文章显示一切正常,有些则全部是乱码,经过再三查看,我确定所有原来是以FCK模式发布的文章都没有问题,唯独UBB模式的都是乱码。先来试试读取的时候直接对数据转码看看:
article.setContent(new String(rs.getBytes(6), "GBK"));
结果是:转成GBK、GB2312以及iso-8859-1之后显示还是乱码。无奈之下只好百度了。
看到某人也碰到了和我同样的遭遇,同样也是一个接一个的测试,最后用utf-16le读取出来正常了。
晕倒~我这还真是头一次听到还有个utf-16le编码格式,这下长见识了。
当对数据库中的一张表进行操作且同时又要对与之相关的其他表的数据进行修改时,要如何操作呢?一种方法是写出每一个操作的SQL语句,然后逐一执行。但是,如果碰到银行转账这种业务,我们需要先从A帐户扣钱,然后给B帐户加钱。若A帐户扣钱成功但是B帐户加钱失败,我们还得再多写一条SQL用以在失败的时候再把A的钱给加上。这样太麻烦而且需要我们在底层写很多数据库操作代码,容易出错也不安全。
此时就可以用触发器与事务来处理,当对A帐户进行修改时自动触发执行对B帐户的修改,并且当其中任何一个操作失败时,事务会自动回滚,取消之前所有的操作,这样就无形的减少了不少代码及安全隐患。
我所做的项目就有这样的一个需要:有一个文章表和一个文章类别表,文章类别表中有个字段用于存储该类别下的文章数量。所以,增加或删除一篇文章同时需要对类别表进行修改,只需要使用如下的代码创建一个触发器即可。
- CREATE TRIGGER trig_Article_Ins–开始创建触发器
- ON dbo.blog_Article–指明要对哪个表创建
- FOR INSERT–创建INSERT类型的触发器
- AS
- BEGIN TRANSACTION–开始事务
- DECLARE @cateId int,@errorSum int–声明变量,分别用于存储类别ID和错误统计
- SET @errorSum = 0–错误统计初值为0
- SELECT @cateId=log_CateID FROM inserted–查到新插入的文章的类别ID
- SET @errorSum=@errorSum+@@ERROR–累计错误
- UPDATE blog_Category SET cate_Count=cate_Count+1 WHERE cate_ID=@cateId–对相应的表进行更新
- SET @errorSum=@errorSum+@@ERROR–累计错误
- IF @errorSum <> 0–若执行期间出现错误则回滚,否则提交事务
- BEGIN
- print ‘创建失败,回滚事务!’
- ROLLBACK TRANSACTION
- END
- ELSE
- BEGIN
- print ‘创建成功,提交事务!’
- COMMIT TRANSACTION
- END
- GO
另外,使用这个还有另外一个原因:好久没有写过这些东西了,该练练手了。所以,今天在编写这个触发器的时候还小查了一下帮助手册。
今天在操作数据库的时候报出了一个错误:java.sql.SQLException: [Microsoft][ODBC SQL Server Driver][SQL Server]插入错误: 列名或所提供值的数目与表定义不匹配。怎么可能呢,我昨天对同样的数据库同样的表执行同样的操作时候是没有问题的啊!开玩笑呢吧!开始抓虫…
首先从程序的代码入手,顺便用企业管理器打开对应的表,检查了一下字段数是对的呀,难道代码中的SQL我拼错了?给代码中又加了句System.out.println(sql)。
- public static boolean createCategory(CategoryBean cate)
- {
- String sql = "INSERT INTO blog_Category VALUES(‘"+cate.getCateName()+"’,"+cate.getCateCount()+")";
- dbc = new DataSource();
- System.out.println(sql);
- stmt = dbc.getPreparedStatement(sql);
- return dbc.executeUpdate(stmt);
- }
接着再次执行了一下还是报错:
INSERT INTO blog_Category VALUES(‘测试’,0)
java.sql.SQLException: [Microsoft][ODBC SQL Server Driver][SQL Server]插入错误: 列名或所提供值的数目与表定义不匹配。
从输出的结果来看,我的SQL是绝对没有拼错的!OK,静下心来整理一下思路:
假如数据库中有张表,此表包含3个字段且均为必填,如果有一个字段是自增类型的标识列,那么我们写SQL的时候就不需要管它。反之,在插入一条数据的时候就必须有这一字段的数据,否则就会报出这样的错误。
OK,想明白了,也知道自己的SQL是的确没有拼错,那么问题就是出在我的标识列上了,仔细检查了一下这张表的主键列,结果发现竟然忘记设置它为标识列并令其自增了。这时突然想起来我昨天貌似把数据库重新覆盖过一次,用来覆盖的那个数据库是还没有设计完整的!我狂晕~
不过,其实遇到问题遇到错误是件好事,从中可以学到很多知识,没有问题才是最大的问题!切记:遇到问题时不要头大,或许当时有点烦躁,但一定要冷静一下,静下心来仔细想想为什么会出现这样的问题,你会发现原来问题如此简单!
作为一个搞软件开发的IT工作者来说,时时关注行业中最需要的技能是什么是很重要的事情。
Robert Half Technology调查了1400位CIO,调查问题是:他们最需要的IT技能是什么?调查结果反映的也是当今IT的局势及其未来走向。
所有的被提问者(CIO们)均工作在有100名以上员工的公司,可以选择不止一项技能。以下是在2008年9月的调查结果:
• 网络管理员( Network administration (LAN, WAN)): 70%
• Wiondows管理员(Windows administration): 69%
• 桌面支持(Desktop support): 69%
• 数据库管理( Database management): 58%
• 无线网络管理( Wireless network management): 47%
• 通讯支持(Telecommunications support): 44%
• Web开发/网站设计(Web development/Website design): 42%
• 商业情报/报告服务(Business intelligence/reporting services): 33%
• 虚拟化(Virtualization): 32%
• .NET开发(.NET development): 22%
• CRM 实施(CRM implementation): 22%
• ERP 实施(ERP implementation): 20%
• Linux/Unix 管理员(Linux/Unix administration): 20%
• Java开发(Java development): 17%
• 开源开发(Open source development): 17%
• XML 开发(XML development): 17%
从上面的调查可以看出我将要从事的都在20%左右徘徊…
在拿到WEBSHELL进行入侵提权经常会碰到可以执行CMD但是却不能执行例如net user neeke /add这样的操作的情况,这一下就把一条提权的捷径封杀了,不过现在有了Churrasco.exe碰到这种事就真的成了捷径了。
今天在“腾讯西安文明群”看到皇子大牛(群签名“父亲”)在说话:
父亲 12:28:53
谁有win2003的 webshell 能执行命令的
父亲 12:29:01
丢来一个 我看看提权
父亲 12:29:25
快点
父亲 12:29:28
刚拿到的东西
父亲 12:31:07
麻痹算了我自己去找shell
竟然没人鸟他,汗~~十分钟后皇子发图:
父亲(543261053) 12:41:40

看看时差!就是10分钟的事,不得不佩服皇子啊!(拍个马屁)
看到这个图,我无语啦,太强大了!以前我碰到那么多可以执行CMD但是提不上去的…赶紧百度搜Churrasco…
不知道是这个名字怪还是真的知道的人很少,找出来的都是葡萄牙语“巴西烤肉”,怪不得皇子说“刚拿到的东西”,不过还是被我Google到了,看来老外还是牛!
拿到了就得亲自试用一下,找了找N久以前的一个可以执行CMD但无法提权的WEBSHELL,结果发现SHELL已经被删了,索性又夺了回来(几个月前的漏洞还在,无语~)。然后传了Churrasco.exe上去,接着执行/c d:\wwwroot\Churrasco.exe "net user neeke /add"竟然真的提示成功了!不敢相信,以为眼花了,又直接执行了/c net user test /add结果啥没反应都没有的,接着又用Churrasco.exe把neeke加到administrators里,然后登录服务器…我没啥要说的了。竟然还免杀!
以前写的那个[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());
- }
- }
这个依然是为我的项目而服务的,因为项目的功能需要此技术来实现,或许对很多人来说这个不难,可是对我这个不太对JavaScript感冒的人来说就不是那么容易了,办法只有一个:找资料现学呗。
先来了解一下相关知识:要实现框架中多窗体的不同元素的访问,则必须使用window对象中的Frames属性。Frames属性同样也是一个数组,它在父框架集中为每一个子框架设有一项。通过下标实现不同框架的访问:
parent.frames[Index1].docuement.forms[index2]
通过parent.frames.length确定窗口中窗体的数目。除了使用数组下标来访问窗体外还可以使用框架名和窗体名来实现各元素的访:
parent.framesName.document.getElementById()
OK,这么点资料就够用了,我们来小试一把。首先创建一个theFather.html,其代码如下:
- <html>
- <head>
- <title>frame get Value test</title>
- </head>
- <iframe frameborder="1" height="78" marginheight="0" marginwidth="0" scrolling="no" width="100%" src="theSon.html"></iframe>
- <h3>这是theFather的文本框</h3>
- <input type="text" name="txtFather" id="txtFather" />
- </html>
接下来创建theSon.html,其代码如下:
- <html>
- <head>
- <title>the son frame</title>
- <script type="text/javascript">
- //此方法用于向其父文本框类赋值
- function setValue(){
- //获得子类文本框中的值
- var sonValue=document.getElementById("txtSon").value;
- //弹出信息,此处仅用于测试
- alert("theSon的值为:"+sonValue);
- //向父类文本框赋值
- parent.document.getElementById("txtFather").value=sonValue;
- }
- </script>
- </head>
- <body>
- <h3>这是theSon的文本框及按钮</h3>
- <input type="text" name="txtSon" id="txtSon" />
- <input type="button" name="btnSon" id="btnSon" value="提交" onclick="setValue();" />
- </body<
span class="tag">> - </html>
接下来运行测试,达到了预期的效果,现在可以正式融入项目中了。