转自:蓝色理想
文章链接如下:
http://bbs.blueidea.com/thread-2729956-1-1.html
CODE:
开发环境配置 -----------------------------------------------------------------------1楼
IDE eclipse的安装-------------------------------------------------------------------2楼
第二个jsp文件------------------------------------------------------------------------7楼
Get/Post方法-----------------------------------------------------------------------10楼
JSP中实现文件上传---------------------------------------------------------------13楼
JSP中实现文件上传2(Eclipse得简单使用)--------------------------------14楼
jsp页面上传完成版总结-----------------------------------------------------------15楼
Application,session变量-----------------------------------------------------------16楼
request,response对象简介-------------------------------------------------------17楼
通过response对象实现文件下载--------------------------------------------------18楼
Cookie操作的例子。
JSP中实现文件下载-----------------------------------------------------------------19楼
数据库连接----------------------------------------------------------------------------20楼
连接池的概念与简单实现----------------------------------------------------------22楼
从最简单的留言本开始-1----------------------------------------------------------26楼
IDE eclipse的安装-------------------------------------------------------------------2楼
第二个jsp文件------------------------------------------------------------------------7楼
Get/Post方法-----------------------------------------------------------------------10楼
JSP中实现文件上传---------------------------------------------------------------13楼
JSP中实现文件上传2(Eclipse得简单使用)--------------------------------14楼
jsp页面上传完成版总结-----------------------------------------------------------15楼
Application,session变量-----------------------------------------------------------16楼
request,response对象简介-------------------------------------------------------17楼
通过response对象实现文件下载--------------------------------------------------18楼
Cookie操作的例子。
JSP中实现文件下载-----------------------------------------------------------------19楼
数据库连接----------------------------------------------------------------------------20楼
连接池的概念与简单实现----------------------------------------------------------22楼
从最简单的留言本开始-1----------------------------------------------------------26楼
-开发环境配置
所在的部门做的是VB + Oralce的erp开发,Java方面的知识已经忘得差不多了,最近部门的项目不是很紧,准备写点东西,在win操作系统下,从环境配置开始,一点一点进行java方面的web开发。希望能对大家有点帮助。
许多人做java的web开发还是在windows操作系统下,而要做开发的第一步,就是配置开发环境。Java的开发环境,基本的就是java虚拟机和web服务器,当然,数据库服务器环境也是比较重要的一个环节,但是由于开发时所用的数据库有很多,比如Oracle,MySQL,SQLServer,Access等等,而且对于web开发者来说,只要数据库的服务存在,提供了数据库链接的基本信息(url,username,password,driverClass),利用JDK或者第三方提供的JDBC驱动类的包,很容易链接到数据库。所以现在所说的java web开发环境,主要针对java虚拟机和web服务器来说。而在开发过程中,许多开发者喜欢用的web服务器是tomcat,所以就简单讲讲JDK和TOMCAT的安装配置。
而开发所用的IDE,只能用我经常用的Eclipse为例了。
JDK的安装,版本选择。
JDK有很多版本,J2SE 1.3.1 ,1.4.2 ,1.5 ,1.6。。。下载JDK安装文件,请到sun公司的网站进行下载。
比如,现在JDK1.6的下载页面为http://java.sun.com/javase/downloads/index.jsp
而以前的版本的下载页面为http://java.sun.com/javase/downloads/previous.jsp
这里选择1.4.2版本的JDK。
注:
开发过程中,JDK版本的选择,如果是由你决定,那么可以根据个人的喜欢下载相应的版本就可以了。同时要注意所选择的web服务器对JDK版本的要求。如果项目统一要求,或者以前已经有了开发好的项目,那么就用指定的版本的JDK。毕竟,不同版本的JDK中类和包的结构可能存在一些差别。
这里,选择1.4.2版本的JDK。
在下载页面,(http://java.sun.com/j2se/1.4.2/download.html)可以看到提供了JDK,JRE以及JDK文档的下载。JRE是给操作系统提供Java运行环境,比如IE中的Applet程序的运行支持。开发者要用JDK,JDK安装后,自带JRE。
PS>建议下栽JDK安装文件的同时下栽JDK文档,这是开发过程中最好的帮手,可以查到各种类,方法的说明和参数,以及可能抛出的异常等基本信息。如果要做Java WEB开发,这个帮助文档是不应该缺少的。
安装JDK以及JDK版本的管理。
JDK有不同的版本,开发过程中不同的项目使用的JDK版本可能不同。所以建议安装的时候把JDK安装到某个目录下,并给每个JDK的文件夹用JDK版本命名,同时建议不要安装在系统盘上,安装到别的盘下,避免重装系统等情况下对JDK有影响。(重装系统后原有的JDK能否使用没有确认。)比如,我的JDK都安装在E盘JDK目录下,已有的JDK是1.6版本的和1.3.1_12版本,现在安装的时候,就在同级目录下建了1.4.2_13版本的目录。
[attach]31072[/attach]
设定好安装目录后,开始进行安装,安装目录只是个人喜好,对JDK没有影响。安装完成后,重启,设定生效。重启后,在开始-运行-进入cmd窗口,输入 java –version,正常的情况下会打印出你JDK的版本信息。表明安装成功。
[attach]31073[/attach]
PS>安装了多个版本的JDK,如何更改当前系统使用的JDK?
比如我的机器现在有的JDK分别为1.3.1_12,1.4.2_13,1.6三个版本。
安装的目录分别为:
1.3.1_12 C:\jdk1.3.1_12
1.4.2_13 E:\JDK\j2sdk1.4.2_13
1.6 E:\JDK\jdk1.6
如果我要用1.3.1_12版本的JDK,只要在“我的电脑”点右键进入系统属性界面,详细设定窗口点击环境变量,在系统环境变量中找到path的参数,在参数前输入你的JDK目录\bin;确定后,再打开一个新的cmd窗口,输入java –version,版本信息是不是变了?比如,我想用1.3.1_12版本的JDK,加上C:\jdk1.3.1_12\bin;后,再次查看版本信息,就变成如下的信息了。
[attach]31074[/attach]
[attach]31075[/attach]
注意要打开一个新的cmd窗口,原来的cmd窗口打印不出新的版本信息。
PS>有时候安装了某个程序或者服务后,会发现JDK版本被更改。比较常见的是Oracle9i安装后,发现JDK的版本信息变成了1.3.1的,这是因为安装Oracle过程中,Oracle自己安装了JDK。安装Oracle后,在“我的电脑”点右键进入系统属性界面,详细设定窗口点击环境变量,在系统环境变量中找到path的参数,在这里察看Oracle设定的参数,会发现几个Oracle安装目录下的jre参数,一般有1.3.1,1.1.8,这2个版本,把他们删掉后你自己的JDK就可以正常使用了。
TOMCAT的下栽及安装。
Tomcat要到http://tomcat.apache.org/网站上下载。可以选择不同的版本,选择的时候注意其对JDK版本的要求,如果tomcat对JDK版本有要求的时候,下栽目录会有提示。
在这里选择tomcat4.0.6,exe用的tomcat进行下栽安装。
安装的文件有zip的exe的等。2者都可以,个人感觉zip的解压了就可以用,要方便些。
解压完了,打开tomcat的目录,可以看到以下几个接触比较多的文件夹。Bin,common,里,conf,webapps,work。
Bin:这个文件夹下放着tomcat的批处理文件,tomcat的开始和关闭的批处理文件都在这里。Startup.bat,shutdown.bat
Common,lib:tomcat提供的jar包在这2个文件夹下,开发项目时设定classpath的时候,可以到这里找要用的jar包。
Conf:tomcat的配置文件,其中server.xml可能经常会用到,可以在里面进行配置新的应用,加载数据源等各种配置。
Webapps:正常情况下,用户自己配置的应用是放在这里的。当然,这不是绝对的,可以在刚才说的server.xml里进行设定,那么你的程序放在哪里都可以了。
Work:这个目录还是比较有用的,开发过程中写好的jsp文件,在tomcat中初次运行的时候,会被tomcat解释成java文件并编译成class文件放在这里。有时候jsp文件出异常的时候,报错的文件不是用户自己写的文件,而是和jsp文件名有些相似的java文件,那就到这里来找。
现在,到bin目录下点击startup.bat来启动tomcat服务。如果按照我上面介绍的步骤,可以发现tomcat启动不了。
[attach]31076[/attach]
这是因为有些参数我们还没有设定。
打开startup.bat文件。(rem是注释,就不需要去管了,摘出来会被运行的语句)
@echo off
if "%OS%" == "Windows_NT" setlocal
if not "%CATALINA_HOME%" == "" goto gotHome
set CATALINA_HOME=.
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
set CATALINA_HOME=..
:gotHome
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome
set EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat
if exist "%EXECUTABLE%" goto okExec
echo Cannot find %EXECUTABLE%
echo This file is needed to run this program
goto end
:okExec
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs
call "%EXECUTABLE%" start %CMD_LINE_ARGS%
:end
这种语言并不熟悉,不过可以大致看它的意思。在确定操作系统为windows后,判断CATALINA_HOME变量是否定义,如果没有定义,将当前的tomcat目录作为tomcat服务器的目录。如果定义的话,将定义的CATALINA_HOME作为tomcat服务器的目录。
执行catalina.bat批处理,在这个批处理里,我们看到这句话,JAVA_HOME Must point at your Java Development Kit installation.,这个表明要运行tomcat,我们必须设定JAVA_HOME为某个安装了的JDK。所以,在“我的电脑”点右键进入系统属性界面,详细设定窗口点击环境变量,在系统环境变量中设定JAVA_HOME为自己的JDK目录。
[attach]31077[/attach]
设定完成后,再次点击startup.bat,正常情况下,应该看到如下界面。
[attach]31078[/attach]
表明tomcat服务已经正常启动。这时候打开IE,输入http://localhost:8080/,可以看到tomcat正常启动的画面。
[attach]31079[/attach]
这时候,tomcat安装已经正常完了。
PS>tomcat的默认端口是8080,这个可以在server.xml中进行配置,以后再作介绍。有时候如果别的服务占用了8080的端口,可以去更改tomcat的端口,来让tomcat可以正常运行。
安装好的tomcat的主页面中,有一些链接的信息还是有些用处的。
JSP,Servlet,WebDAV Examples这些,提供了一些简单的jsp和servlet,让你了解tomcat下web开发的一些最基本的方式。
Tomcat Documentation中提供了关于当前版本的Tomcat的各种资料,有些很详细,有些比较潦草。不过看看还是很有帮助的。
如果有兴趣,看看server.xml,web.xml那里边有许多被注释掉的sample。可以了解tomcat的各种配置等信息。
IDE:
我用的IDE是Eclipse,Eclipse的安装比较简单,暂时就不提了。
环境配置好了,我们不妨写出第一个jsp文件,来看看在tomcat下能否正常运行。
文件名:HelloWorld.jsp
文件内容:
CODE:
<%
String str = "Hello World";
out.print(str);
%>
String str = "Hello World";
out.print(str);
%>
很简单,定义一个字符串,将这个字符串打印出来。
把这个文件拷贝到tomcat下的webapps/examples/文件夹下。
然后在IE中输入以下地址
http://localhost:8080/examples/HelloWorld.jsp
正常的情况下将会看到打印出的字符串。
[attach]31080[/attach]
OK,第一个jsp程序成功运行。
之所以作这样一个页面,是要带出2个小问题点。
一, JSP以及Java是严格区分大小写的,以前没做过jsp开发的朋友要注意这点。
比如,你输入http://localhost:8080/examples/helloworld.jsp,将无法访问到这个页面。
二, 成功运行后,我们到work目录下去找这个文件,在work\Standalone\localhost\examples目录下,可以看到一个HelloWorld$jsp.java和HelloWorld$jsp.class,这就是tomcat把我们写的jsp文件生成java文件,然后在编译成class文件。Tomcat运行的时候直接调用这个class文件。
打开,看一下源码。
CODE:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class HelloWorld$jsp extends HttpJspBase {
static {
}
public HelloWorld$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
// begin [file="/HelloWorld.jsp";from=(0,2);to=(3,0)]
String str = "Hello World";
out.print(str);
// end
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class HelloWorld$jsp extends HttpJspBase {
static {
}
public HelloWorld$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
// begin [file="/HelloWorld.jsp";from=(0,2);to=(3,0)]
String str = "Hello World";
out.print(str);
// end
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
有意思吧。做java开发,可以有点这样的想法,所有的程序,最终都可以看作给java虚拟机运行的一堆类文件。Jsp,开发中要用到的jar包,甚至是tomcat本身,所以,开发中出现问题的时候,其实就是你用错误的方法操作了一堆别人提供的类,所以,1,要知道游戏规则(语法规范),2,要知道你在跟谁玩,并且要了解跟你玩的对象(开发过程中要用到的各种资源),这2点都知道的话,出了问题也很容易找出来了。
-----------------------------------------------------------------
IDE eclipse的安装
eclipse可以到http://www.eclipse.org/downloads/下载,拿版本3.2.2为例,简单介绍下安装过程。
下载后,解压到指定的目录,发现可以直接运行。初次运行的时候,会弹出窗口,提示设定eclipse的工作空间,选择选择一个工作空间,并勾上以后不再提示,这样以后这个对话创就不会弹出。这样,eclipse就安装完成了,以后会以eclipse为基础,介绍怎么样利用eclipse用tomcat做服务器,开发简单的jsp,servlet程序。
这里先说一点,在操作系统上存在多个版本的JDK的时候,eclipse怎样更改JDK版本。
拿3.2.2版本为例,打开window->preferences,(中文版的是 窗口->首选项),有eclipse的各种设置信息,其中java栏中有Installed JRE选项,在这里可以添加和更改当前安装的不同版本的JDK。
比如,现在eclipse识别的JDK是1.4.2_13,已经安装的1.4.2_11没有识别出来,在这里可以手动添加。点击ADD按钮,弹出如下界面,在jre name中填写版本信息,在JRE home directory中选中JDK安装的目录,Eclipse会自动读出可用的jar包和可用的javaDocs。点击OK后,该版本的JDK添加成功。然后可以选中某个你开发需要的JDK版本。
当然,在建立工程开发的过程中也可以更改JDK版本,这个以后再介绍。
到现在,tomcat下的jsp开发只是提了冰山一角。具体开发中还有许多知识。但是看了以上部分后,开发过程中就可以轻松的搭建环境,为开发做好准备工作。(这里没有说数据库的搭建,和第三方技术支持的搭建。因为这些知识本身已经不属于一个web程序员的范畴了,对于一个web开发的程序员来说,只要知道第三方控件的接口或者数据库连接的参数即可,至于数据库搭建的各种知识,则是DBA这类人的任务了。)
下载后,解压到指定的目录,发现可以直接运行。初次运行的时候,会弹出窗口,提示设定eclipse的工作空间,选择选择一个工作空间,并勾上以后不再提示,这样以后这个对话创就不会弹出。这样,eclipse就安装完成了,以后会以eclipse为基础,介绍怎么样利用eclipse用tomcat做服务器,开发简单的jsp,servlet程序。
这里先说一点,在操作系统上存在多个版本的JDK的时候,eclipse怎样更改JDK版本。
拿3.2.2版本为例,打开window->preferences,(中文版的是 窗口->首选项),有eclipse的各种设置信息,其中java栏中有Installed JRE选项,在这里可以添加和更改当前安装的不同版本的JDK。
比如,现在eclipse识别的JDK是1.4.2_13,已经安装的1.4.2_11没有识别出来,在这里可以手动添加。点击ADD按钮,弹出如下界面,在jre name中填写版本信息,在JRE home directory中选中JDK安装的目录,Eclipse会自动读出可用的jar包和可用的javaDocs。点击OK后,该版本的JDK添加成功。然后可以选中某个你开发需要的JDK版本。
当然,在建立工程开发的过程中也可以更改JDK版本,这个以后再介绍。
到现在,tomcat下的jsp开发只是提了冰山一角。具体开发中还有许多知识。但是看了以上部分后,开发过程中就可以轻松的搭建环境,为开发做好准备工作。(这里没有说数据库的搭建,和第三方技术支持的搭建。因为这些知识本身已经不属于一个web程序员的范畴了,对于一个web开发的程序员来说,只要知道第三方控件的接口或者数据库连接的参数即可,至于数据库搭建的各种知识,则是DBA这类人的任务了。)
------------------------------------------------------------------------
用TOMCAT作简单的Java web开发-第二个jsp文件
题外话:
呵呵,感谢楼上的各位的鼓励,只是一点碎碎的入门,自己技术不是很强,有幸被红色黑客推荐为斑竹,有幸得到蓝色等管理者的信任,成为后台区的斑竹。经典藏龙卧虎,高手云集,技术单薄的我,只有尽力兢兢业业的管理下论坛,做些自己能做的事情。而公司不允许上url中有bbs,game,news,sexy等等关键字的网站,上了的话,会被关小黑屋的。所以,只有在每天下班回来,9,10点开始,上来看下论坛。被俺家领导严正警告,每次下班回来,眼里只有经典论坛。-_-b。主要是感觉白天不在论坛,所以晚上还是要拿些时间来到这里,白天的管理,这里向后台其他斑竹说声,辛苦你们了。
我是打算这个帖子写一小段时间的,从搭建环境开始,然后用这个环境,一步一步写出jsp文件,写出简单的Servlet,最后用Servlet写出一个简单的MVC结构的框架。力求最后这个Servlet每一步,每个小细节都能介绍到,达到最后能够对简单的Servlet开发有个简单的认识和理解的目的。同时,在介绍过程中,把自己有一些点点滴滴的经验,想法说出来,虽然不成熟,不够专业,不过一些小细节,对开发还是有一点点帮助的。如果有很多朋友关心此帖并且能得到帮助的话,有问题可以在这里发问,能够解决的话,最后达到一个小的汇总也是我的希望。
最后说句jsp web开发入门,因为这个问题在后台区好像是每隔一段时间就会出现一次。仁者见仁,智者见智,我也只是谈谈我的看法。
一,确立你的目标。
学jsp要达到什么程度,要能够写jsp页面,能够通过全部是jsp页面(无class)来做成一个网站?还是要学到servlet知识,能够搭建简单的基于MVC结构的servlet系统,能够快速的理解并使用已经成形的servlet框架。还是能够自己分析用户需求,根据用户需求选择合适的bs系统,搭建出合适的J2EE框架,能够熟练的使用各种J2EE技术。
给自己定好目标,才有前进的方向。对自己要求不同,付出的努力是不同的,实现目标所需要的时间和精力也不同,当然,得到的回报也是不同的。
如果刚入手的话,建议先从简单的jsp为基础的开发开始,当然,如果你有机会接触比较成熟的J2EE项目,那么,开始就向纵深发展也是可能的。这个因人而异,因环境而异。
我就曾经看到过一个比较知名的网站设计开发公司,他们给一个客户作的jsp网站里面全部是jsp文件,没有一个class文件。并不是说这样不好,但是看到一个所有的处理,数据库的连接,业务逻辑等等全部和HTML tag糅合在一起的jsp程序,确实感觉不是很爽。但是这样的网站仍然能够满足他们的客户的要求。在没有严重问题的情况下,能够满足客户的要求,应该也算一个成功的项目。但是换一个客户,换一套业务的时候,这堆jsp文件的移植性基本等于0。没有利用好jsp web开发的优点,这也算是败笔之处。
所以,要确立好自己的目标,戒骄戒躁,一步一步来实现自己的目的。想像学asp那样,在一个星期里写出1个留言本。1个月内利用已经成形的后台管理程序开发出一个自己的网站管理系统。3个月左右就能自己开发大部分简单的asp程序,还是有些困难。学jsp的话,就要定好目标,一步一个脚印,一点一点的积累自己的知识。
二,最好还是要有面向对象的基础和java的基础。
当然,刚开始学习的时候,并不一定需要这些。但是随着学习的深入,这些基础还是很有必要的。在论坛里有时候看到有些朋友问的问题,其实他们不是被困扰在jsp开发中,而是被某个类的用法所困扰。或者被某段代码的实现方法所困扰。
有了对java的整体的认识和理解,比如,数据类型,各种对象,以及面向对象在java中的体现等等,在看别人的程序和自己编写程序的时候,会有很大的帮助,并且很容易找到自己问题发生的场所并对应解决。
Java提供的那相对庞大的类库,有许多丰富多彩的类供我们使用,而且SUN在提供JDK的同时,也提供了JDK的帮助文件。那里对每个类,每个方法都有详尽的介绍。当然,它只简单的告诉你能做什么。但是并没有告诉你什么时候,该怎么样来用它。这些知识,就需要知识的积累和经验的积累。比如,java提供了许多容器类,数组,ArrayList,Vector,Hashtable等,他们有各自的特点,也有各自的优势,同时处理数据的时候,效率也不同。那么在使用的时候,就要根据自己要处理的数据的数据结构,要对数据作的操作,以及数据的数量,来选择合适的容器类来存储数据,使数据处理的时候,在效率上,实现难易上等方面达到一个最优的平衡。这,首先就要知道这些类的特点,然后,就是经验的积累了。
其实,我们并没有必要理解所有的JDK的类,这也是不现实的,在理解了一部分类,并对java开发有了一定的认识后,电脑里有一份JDK帮助文档就可以了。遇到不会用的类,不会用的方法是很正常的事情,这个时候就要查JDK帮助文档了。
如果愿意花一段时间学习java的话,可看的书有Thinking In java和Java核心思想这2本书。这2本书都是千页的大部头,而且有许多文字需要揣摩理解,变成自己的东西,不是1天2天能掌握的。需要时时看,时时理解。
三,对jsp开发要有个整体的概念。
刚使用一种语言的时候,最令人兴奋而且容易产生动力的就是能自己开发出一个能运行的程序。那样才会有成就感,是了,就是成就感,每个人都需要成就感的。所以许多人刚上来还是不太愿意闷头看Thinking In Java这样的书,确实,很有可能闷了半年,对java的理解并没有深入,最后连一个jsp程序都写不出来。而别人上来就学jsp的,可能已经在向你展示他做的系统了。所以,刚才说的二可以先无视。等你真的感觉到学习这些知识的必要性的时候,再回头看看吧。但是这样可能造成的一点就是,自己在开发过程中经常遇到使用java类的问题。
那么,就从jsp开始吧,jsp开始的话,就要理解jsp页面里各种tag的意义,了解你使用的web服务器,以及jsp程序运行的基本原理等。这方面,没看过什么书,曾经翻看过一个同事的孙卫琴的JSP,TOMCAT等一系列的书,感觉还可以吧。一点一点啃下来,还是能学到不少东西的。
总体上感觉,1和3是必要的,2在开始的时候可以自己选择。
还有一点就是,如果你在一个jsp的项目里,那么进步的速度绝对比自学要快很多的。自己可以得到锻炼,同时能学到别人解决问题的方法,从别人那里得到经验和帮助。
总之,让程序运行起来不是我们的最终目的,知道程序怎么运行才是我们的最终目的。
现在开始介绍第二个jsp文件。
在开发环境配置中已经做成了一个jsp文件并让它运行,但是我们是在tomcat下的webapps/sample文件夹下运行的这个jsp文件。怎么搭建我们自己的应用呢?或者说自己的web系统,自己的机能呢?首先,我们发现tomcat所运行的网站程序都放在webapps下(实际不是这样的,是可以设定的,下面马上就说到。)上次的那个jsp文件的路径是webapps/examples/HelloWorld.jsp,而访问的时候是http://localhost:8080/examples/HelloWorld.jsp。似乎我们只要在webapps下建立一个文件夹,再放进去这个jsp文件,就可以访问了,是不是这样呢?尝试一下看看。我们在webapps下建立一个文件夹myJSP,并把上次能够运行的jsp文件HelloWorld.jsp放到下面,启动tomcat,然后在IE中输入以下地址http://localhost:8080/myJSP/HelloWorld.jsp,结果发现报以下错误。
为什么呢?因为在tomcat的conf/server.xml中,配置设定了当前可用的网站应用。Tomcat在启动的时候,会先到这里读取各个配置好的网站应用,找到之后,服务器才会识别这些应用。(当然,如果你的网站应用已经配置好了web.xml,也没有必要在server.xml中配置设定,这个以后再提)现在我们就为自己的网站配置应用,打开conf/server.xml,找到</host>,在这个tag上面加上如下代码。
Context有3个参数。
Path :服务器所识别的文件路径
docBase :文件所放置的位置(相对地址的话,tomcat会识别为webapps下的,如果是绝对路径,就是你本机的绝对路径了。所以说tomcat所运行的网站程序都放在webapps下是不对的)
reloadable :是否可以reload,一般情况下设定为true就可以。
这样设定完成后,重启tomcat(不重启的话,tomcat不会重新读取server.xml文件)。
访问http://localhost:8080/JSP/HelloWorld.jsp,发现可以正常访问了。
注意这个时候访问的URL中是JSP而不是myJSP,因为我们在context中设定的path是/JSP。
关于TOMCAT的启动。
现在开始在myJSP这个文件夹下编写第二个jsp文件。
显示日期的类。名字为HelloDate.jsp
代码也很简单,如下:
编写完成,拷贝到刚才建立的myJSP文件夹下,不需要重启TOMCAT,访问http://localhost:8080/JSP/HelloDate.jsp可以看到如下画面。打印出了系统当前时间。
现在介绍下jsp下嵌入一部分java代码的基本样式。
Jsp中的编码主要通过<%%>包含。有一些基本的形式,跟asp比较相似。
Expression形式
主要是用来输出java代码中的变量,上面程序中的<%=new java.util.Date().toString() %>其中的代码new java.util.Date().toString()其实也是返回一个String的变量。
Scriptlet(Java编码)
主要用来包含java的执行代码。比如:
<% int sum =0;
for(int i=0; i<10; i++) {
sum += i;
}
%>
@Page指令的属性
上面为@page指令的常用属性,[]中括号表示其属性可设定也可不设定。像上面的jsp文件中的
就设定了传输的数据的类型,编码方式,以及文件的编码方式。
开发中文简体bs系统,经常指定页面的编码方式为GBK或者gb2312的,日文bs系统的话,常用的编码方式是Shift_JIS,中文繁体应该是big5吧?
一些使用page指令的例子。
<%@ page import="java.util.*, java.lang.*" %>
<%@ page buffer="5kb" autoFlush="false" %>
<%@ page errorPage="error.jsp" %>
Include 指令 (包含其他的文件)
了解了以上的基本知识,我们修改一下这个jsp文件。
再次访问这个页面,显示正常。
至此,简单的jsp页面制作完成。这时候我们再回头看看TOMCAT将这个jsp文件生成的java文件。在\work\Standalone\localhost\JSP下找到这个java文件(为什么是JSP而不是myJSP呢?),HelloDate$jsp.java。
和我们的源文件对应一下,发现<%@page import=”java.util.Date”%>被解释成import java.util.Date;在文件开始的位置被导入。
<%@ page contentType="text/html;
charset=GBK"
pageEncoding="GBK" %>
被解释成response.setContentType("text/html; \r\ncharset=GBK");
而jsp页面里的HTML代码被用out.print()方法输出。
真正写的那个java代码被直接搬过来。
看完了上面的页面后,会发现,我们写的jsp文件,最终都被解释成java文件。大家有兴趣不妨看看自己的jsp文件生成的java文件,或者写出各种tag,看看最终服务器会把他们解释成什么样子,会发现对象无处不在,你能够使用的东西,归根结底都是java的对象。所以说,如果有了面向对象的概念和java的基础,jsp开发起来会进步很快。
另,
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
这4句也很有意思,它告诉了我们平时我们在jsp中用的session,out之类的对象是从哪里来的。以后有机会再介绍相关的知识吧。
另,大家如果看下上次帖子里那个很简单的jsp生成的java文件,会发现一些细节的不同。
比如response.setContentType("text/html; \r\ncharset=GBK");这句就不同,设定的编码方式不同,这是jsp开发中出现乱码的一个原因,在不指定编码方式的情况下,将用系统的默认编码方式,那么,中文乱码的问题就出现了。当然,中文乱码还有一些情况,以后有机会整理一下现象及对应的方法吧。
注:如果感觉看work目录下TOMCAT生成的java文件很烦,看不懂,可以不管这部分介绍,对开发没有影响。有兴趣的可以看看http://localhost:8080/index.html下的JSP Examples,代码很简单,主要是要理解它使用的技术。
呵呵,感谢楼上的各位的鼓励,只是一点碎碎的入门,自己技术不是很强,有幸被红色黑客推荐为斑竹,有幸得到蓝色等管理者的信任,成为后台区的斑竹。经典藏龙卧虎,高手云集,技术单薄的我,只有尽力兢兢业业的管理下论坛,做些自己能做的事情。而公司不允许上url中有bbs,game,news,sexy等等关键字的网站,上了的话,会被关小黑屋的。所以,只有在每天下班回来,9,10点开始,上来看下论坛。被俺家领导严正警告,每次下班回来,眼里只有经典论坛。-_-b。主要是感觉白天不在论坛,所以晚上还是要拿些时间来到这里,白天的管理,这里向后台其他斑竹说声,辛苦你们了。
我是打算这个帖子写一小段时间的,从搭建环境开始,然后用这个环境,一步一步写出jsp文件,写出简单的Servlet,最后用Servlet写出一个简单的MVC结构的框架。力求最后这个Servlet每一步,每个小细节都能介绍到,达到最后能够对简单的Servlet开发有个简单的认识和理解的目的。同时,在介绍过程中,把自己有一些点点滴滴的经验,想法说出来,虽然不成熟,不够专业,不过一些小细节,对开发还是有一点点帮助的。如果有很多朋友关心此帖并且能得到帮助的话,有问题可以在这里发问,能够解决的话,最后达到一个小的汇总也是我的希望。
最后说句jsp web开发入门,因为这个问题在后台区好像是每隔一段时间就会出现一次。仁者见仁,智者见智,我也只是谈谈我的看法。
一,确立你的目标。
学jsp要达到什么程度,要能够写jsp页面,能够通过全部是jsp页面(无class)来做成一个网站?还是要学到servlet知识,能够搭建简单的基于MVC结构的servlet系统,能够快速的理解并使用已经成形的servlet框架。还是能够自己分析用户需求,根据用户需求选择合适的bs系统,搭建出合适的J2EE框架,能够熟练的使用各种J2EE技术。
给自己定好目标,才有前进的方向。对自己要求不同,付出的努力是不同的,实现目标所需要的时间和精力也不同,当然,得到的回报也是不同的。
如果刚入手的话,建议先从简单的jsp为基础的开发开始,当然,如果你有机会接触比较成熟的J2EE项目,那么,开始就向纵深发展也是可能的。这个因人而异,因环境而异。
我就曾经看到过一个比较知名的网站设计开发公司,他们给一个客户作的jsp网站里面全部是jsp文件,没有一个class文件。并不是说这样不好,但是看到一个所有的处理,数据库的连接,业务逻辑等等全部和HTML tag糅合在一起的jsp程序,确实感觉不是很爽。但是这样的网站仍然能够满足他们的客户的要求。在没有严重问题的情况下,能够满足客户的要求,应该也算一个成功的项目。但是换一个客户,换一套业务的时候,这堆jsp文件的移植性基本等于0。没有利用好jsp web开发的优点,这也算是败笔之处。
所以,要确立好自己的目标,戒骄戒躁,一步一步来实现自己的目的。想像学asp那样,在一个星期里写出1个留言本。1个月内利用已经成形的后台管理程序开发出一个自己的网站管理系统。3个月左右就能自己开发大部分简单的asp程序,还是有些困难。学jsp的话,就要定好目标,一步一个脚印,一点一点的积累自己的知识。
二,最好还是要有面向对象的基础和java的基础。
当然,刚开始学习的时候,并不一定需要这些。但是随着学习的深入,这些基础还是很有必要的。在论坛里有时候看到有些朋友问的问题,其实他们不是被困扰在jsp开发中,而是被某个类的用法所困扰。或者被某段代码的实现方法所困扰。
有了对java的整体的认识和理解,比如,数据类型,各种对象,以及面向对象在java中的体现等等,在看别人的程序和自己编写程序的时候,会有很大的帮助,并且很容易找到自己问题发生的场所并对应解决。
Java提供的那相对庞大的类库,有许多丰富多彩的类供我们使用,而且SUN在提供JDK的同时,也提供了JDK的帮助文件。那里对每个类,每个方法都有详尽的介绍。当然,它只简单的告诉你能做什么。但是并没有告诉你什么时候,该怎么样来用它。这些知识,就需要知识的积累和经验的积累。比如,java提供了许多容器类,数组,ArrayList,Vector,Hashtable等,他们有各自的特点,也有各自的优势,同时处理数据的时候,效率也不同。那么在使用的时候,就要根据自己要处理的数据的数据结构,要对数据作的操作,以及数据的数量,来选择合适的容器类来存储数据,使数据处理的时候,在效率上,实现难易上等方面达到一个最优的平衡。这,首先就要知道这些类的特点,然后,就是经验的积累了。
其实,我们并没有必要理解所有的JDK的类,这也是不现实的,在理解了一部分类,并对java开发有了一定的认识后,电脑里有一份JDK帮助文档就可以了。遇到不会用的类,不会用的方法是很正常的事情,这个时候就要查JDK帮助文档了。
如果愿意花一段时间学习java的话,可看的书有Thinking In java和Java核心思想这2本书。这2本书都是千页的大部头,而且有许多文字需要揣摩理解,变成自己的东西,不是1天2天能掌握的。需要时时看,时时理解。
三,对jsp开发要有个整体的概念。
刚使用一种语言的时候,最令人兴奋而且容易产生动力的就是能自己开发出一个能运行的程序。那样才会有成就感,是了,就是成就感,每个人都需要成就感的。所以许多人刚上来还是不太愿意闷头看Thinking In Java这样的书,确实,很有可能闷了半年,对java的理解并没有深入,最后连一个jsp程序都写不出来。而别人上来就学jsp的,可能已经在向你展示他做的系统了。所以,刚才说的二可以先无视。等你真的感觉到学习这些知识的必要性的时候,再回头看看吧。但是这样可能造成的一点就是,自己在开发过程中经常遇到使用java类的问题。
那么,就从jsp开始吧,jsp开始的话,就要理解jsp页面里各种tag的意义,了解你使用的web服务器,以及jsp程序运行的基本原理等。这方面,没看过什么书,曾经翻看过一个同事的孙卫琴的JSP,TOMCAT等一系列的书,感觉还可以吧。一点一点啃下来,还是能学到不少东西的。
总体上感觉,1和3是必要的,2在开始的时候可以自己选择。
还有一点就是,如果你在一个jsp的项目里,那么进步的速度绝对比自学要快很多的。自己可以得到锻炼,同时能学到别人解决问题的方法,从别人那里得到经验和帮助。
总之,让程序运行起来不是我们的最终目的,知道程序怎么运行才是我们的最终目的。
现在开始介绍第二个jsp文件。
在开发环境配置中已经做成了一个jsp文件并让它运行,但是我们是在tomcat下的webapps/sample文件夹下运行的这个jsp文件。怎么搭建我们自己的应用呢?或者说自己的web系统,自己的机能呢?首先,我们发现tomcat所运行的网站程序都放在webapps下(实际不是这样的,是可以设定的,下面马上就说到。)上次的那个jsp文件的路径是webapps/examples/HelloWorld.jsp,而访问的时候是http://localhost:8080/examples/HelloWorld.jsp。似乎我们只要在webapps下建立一个文件夹,再放进去这个jsp文件,就可以访问了,是不是这样呢?尝试一下看看。我们在webapps下建立一个文件夹myJSP,并把上次能够运行的jsp文件HelloWorld.jsp放到下面,启动tomcat,然后在IE中输入以下地址http://localhost:8080/myJSP/HelloWorld.jsp,结果发现报以下错误。
为什么呢?因为在tomcat的conf/server.xml中,配置设定了当前可用的网站应用。Tomcat在启动的时候,会先到这里读取各个配置好的网站应用,找到之后,服务器才会识别这些应用。(当然,如果你的网站应用已经配置好了web.xml,也没有必要在server.xml中配置设定,这个以后再提)现在我们就为自己的网站配置应用,打开conf/server.xml,找到</host>,在这个tag上面加上如下代码。
CODE:
<Context path="/JSP" docBase="myJSP" reloadable="true" />
Context有3个参数。
Path :服务器所识别的文件路径
docBase :文件所放置的位置(相对地址的话,tomcat会识别为webapps下的,如果是绝对路径,就是你本机的绝对路径了。所以说tomcat所运行的网站程序都放在webapps下是不对的)
reloadable :是否可以reload,一般情况下设定为true就可以。
这样设定完成后,重启tomcat(不重启的话,tomcat不会重新读取server.xml文件)。
访问http://localhost:8080/JSP/HelloWorld.jsp,发现可以正常访问了。
注意这个时候访问的URL中是JSP而不是myJSP,因为我们在context中设定的path是/JSP。
关于TOMCAT的启动。
CODE:
1.TOMCAT关闭的时候,可以点击shutdown.bat,或者直接在tomcat运行窗口按CTRL + C键就可以结束这个运行窗口。
2.TOMCAT下开发的程序有修改的时候,如果是jsp文件的话,不需要重新启动tomcat,直接刷新页面,jsp文件就会被重新编译。而其他的修改,比如xml,properties配置文件,以及java class的修改的时候,需要重新启动TOMCAT,不然这些修改不会被重新加载。
2.TOMCAT下开发的程序有修改的时候,如果是jsp文件的话,不需要重新启动tomcat,直接刷新页面,jsp文件就会被重新编译。而其他的修改,比如xml,properties配置文件,以及java class的修改的时候,需要重新启动TOMCAT,不然这些修改不会被重新加载。
现在开始在myJSP这个文件夹下编写第二个jsp文件。
显示日期的类。名字为HelloDate.jsp
代码也很简单,如下:
CODE:
<%@ page contentType="text/html;
charset=GBK"
pageEncoding="GBK" %>
<html>
<head>
<title>Test DATE</title>
</head>
<body>
<h2><%=new java.util.Date().toString() %></h2>
</body>
</html>
charset=GBK"
pageEncoding="GBK" %>
<html>
<head>
<title>Test DATE</title>
</head>
<body>
<h2><%=new java.util.Date().toString() %></h2>
</body>
</html>
编写完成,拷贝到刚才建立的myJSP文件夹下,不需要重启TOMCAT,访问http://localhost:8080/JSP/HelloDate.jsp可以看到如下画面。打印出了系统当前时间。
现在介绍下jsp下嵌入一部分java代码的基本样式。
Jsp中的编码主要通过<%%>包含。有一些基本的形式,跟asp比较相似。
Expression形式
CODE:
<%= ... %>
主要是用来输出java代码中的变量,上面程序中的<%=new java.util.Date().toString() %>其中的代码new java.util.Date().toString()其实也是返回一个String的变量。
Scriptlet(Java编码)
CODE:
<% ... %>
主要用来包含java的执行代码。比如:
<% int sum =0;
for(int i=0; i<10; i++) {
sum += i;
}
%>
@Page指令的属性
CODE:
<%@ page
[ language="java" ]
[ extends="package .class" ]
[ import="{package .class | package.*}, ..." ]
[ session="true|false" ]
[ buffer="none|8kb|sizekb" ]
[ autoFlush="true|false" ]
[ isThreadSafe="true|false" ]
[ info="text" ]
[ errorPage="relativeURL" ]
[ contentType="mimeType [ ;charset=characterSet ]" |
"text/html ; charset=ISO-8859-1" ]
[ isErrorPage="true|false" ]
%>
[ language="java" ]
[ extends="package .class" ]
[ import="{package .class | package.*}, ..." ]
[ session="true|false" ]
[ buffer="none|8kb|sizekb" ]
[ autoFlush="true|false" ]
[ isThreadSafe="true|false" ]
[ info="text" ]
[ errorPage="relativeURL" ]
[ contentType="mimeType [ ;charset=characterSet ]" |
"text/html ; charset=ISO-8859-1" ]
[ isErrorPage="true|false" ]
%>
上面为@page指令的常用属性,[]中括号表示其属性可设定也可不设定。像上面的jsp文件中的
CODE:
<%@ page contentType="text/html;
charset=GBK"
pageEncoding="GBK" %>
charset=GBK"
pageEncoding="GBK" %>
就设定了传输的数据的类型,编码方式,以及文件的编码方式。
开发中文简体bs系统,经常指定页面的编码方式为GBK或者gb2312的,日文bs系统的话,常用的编码方式是Shift_JIS,中文繁体应该是big5吧?
一些使用page指令的例子。
<%@ page import="java.util.*, java.lang.*" %>
<%@ page buffer="5kb" autoFlush="false" %>
<%@ page errorPage="error.jsp" %>
Include 指令 (包含其他的文件)
CODE:
<%@ include file="relativeURL" %>
了解了以上的基本知识,我们修改一下这个jsp文件。
CODE:
<%@ page contentType="text/html;
charset=GBK"
pageEncoding="GBK" %>
<%@ page import="java.util.Date"%>
<html>
<head>
<title>Test DATE</title>
</head>
<body><%
Date dt = new Date();
out.println("<h2>" + dt + "</h2>");
%>
</body>
</html>
charset=GBK"
pageEncoding="GBK" %>
<%@ page import="java.util.Date"%>
<html>
<head>
<title>Test DATE</title>
</head>
<body><%
Date dt = new Date();
out.println("<h2>" + dt + "</h2>");
%>
</body>
</html>
再次访问这个页面,显示正常。
至此,简单的jsp页面制作完成。这时候我们再回头看看TOMCAT将这个jsp文件生成的java文件。在\work\Standalone\localhost\JSP下找到这个java文件(为什么是JSP而不是myJSP呢?),HelloDate$jsp.java。
CODE:
package org.apache.jsp;
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class HelloDate$jsp extends HttpJspBase {
static {
}
public HelloDate$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
[color=Green] response.setContentType("text/html; \r\ncharset=GBK");[/color]
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
// HTML // begin [file="/HelloDate.jsp";from=(2,21);to=(3,0)]
out.write("\r\n");
// end
// HTML // begin [file="/HelloDate.jsp";from=(3,34);to=(8,6)]
out.write("\r\n<html>\r\n<head>\r\n<title>Test DATE</title>\r\n</head>\r\n<body>");
// end
// begin [file="/HelloDate.jsp";from=(8,8);to=(11,1)]
Date dt = new Date();
out.println("<h2>" + dt + "</h2>");
// end
// HTML // begin [file="/HelloDate.jsp";from=(11,3);to=(14,0)]
out.write("\r\n</body>\r\n</html>\r\n");
// end
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class HelloDate$jsp extends HttpJspBase {
static {
}
public HelloDate$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
[color=Green] response.setContentType("text/html; \r\ncharset=GBK");[/color]
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
// HTML // begin [file="/HelloDate.jsp";from=(2,21);to=(3,0)]
out.write("\r\n");
// end
// HTML // begin [file="/HelloDate.jsp";from=(3,34);to=(8,6)]
out.write("\r\n<html>\r\n<head>\r\n<title>Test DATE</title>\r\n</head>\r\n<body>");
// end
// begin [file="/HelloDate.jsp";from=(8,8);to=(11,1)]
Date dt = new Date();
out.println("<h2>" + dt + "</h2>");
// end
// HTML // begin [file="/HelloDate.jsp";from=(11,3);to=(14,0)]
out.write("\r\n</body>\r\n</html>\r\n");
// end
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
和我们的源文件对应一下,发现<%@page import=”java.util.Date”%>被解释成import java.util.Date;在文件开始的位置被导入。
<%@ page contentType="text/html;
charset=GBK"
pageEncoding="GBK" %>
被解释成response.setContentType("text/html; \r\ncharset=GBK");
而jsp页面里的HTML代码被用out.print()方法输出。
真正写的那个java代码被直接搬过来。
看完了上面的页面后,会发现,我们写的jsp文件,最终都被解释成java文件。大家有兴趣不妨看看自己的jsp文件生成的java文件,或者写出各种tag,看看最终服务器会把他们解释成什么样子,会发现对象无处不在,你能够使用的东西,归根结底都是java的对象。所以说,如果有了面向对象的概念和java的基础,jsp开发起来会进步很快。
另,
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
这4句也很有意思,它告诉了我们平时我们在jsp中用的session,out之类的对象是从哪里来的。以后有机会再介绍相关的知识吧。
另,大家如果看下上次帖子里那个很简单的jsp生成的java文件,会发现一些细节的不同。
比如response.setContentType("text/html; \r\ncharset=GBK");这句就不同,设定的编码方式不同,这是jsp开发中出现乱码的一个原因,在不指定编码方式的情况下,将用系统的默认编码方式,那么,中文乱码的问题就出现了。当然,中文乱码还有一些情况,以后有机会整理一下现象及对应的方法吧。
注:如果感觉看work目录下TOMCAT生成的java文件很烦,看不懂,可以不管这部分介绍,对开发没有影响。有兴趣的可以看看http://localhost:8080/index.html下的JSP Examples,代码很简单,主要是要理解它使用的技术。
------------------------------------------------------------------------
用TOMCAT作简单的Java web开发-Get/Post方法
今天说说JSP页面之间传值。说起这个之前,说说jsp中request,response,session,out,application对象都是哪里来的。
在最初接触动态网站开发的时候,用 到request,session之类的对象的时候,总感觉很奇怪,不知道这些对象哪里来的,于是对服务器有一种很神秘的感觉,似乎服务器里做了许多非常人可以实现的不可思议的事情。
我们知道,java中,我们是这样使用对象的。比如有一个对象ObjectA,这个对象有setName(String str)方法和getName()方法(非静态方法)。如果要使用这个对象的这2个方法,我们必须初始化这个对象,然后才可以使用这些方法。
另,我们所开发的程序,可以看成是就是在用别人提供的很成熟的类来实现我们的功能。(当然里边有很多的技术和知识)TOMCAT下,jsp页面中的代码最终被TOMCAT解释成了java的类,那么session,request这些东西有什么变化呢?实际上他们没有变化,看看昨天HelloDate.jsp生成的java文件。
我们会发现,我们写的jsp代码,都被生成到public void _jspService(HttpServletRequest request, HttpServletResponse response)方法中,而在这个方法中在运行我们的代码之前,已经作了一些操作。
首先,request, response对象是作为参数传入的。
而application,config,session,out对象在方法开始被定义,在执行过程中从 pageContent对象中取得。
所以说来,我们所用的applicaion,config,session,out,request,response对象,并不是很神秘的东西,它们也只是普通的对象,我们只要知道这些对象提供了什么方法,以及一些重要方法的用途就能够很好地运用它们。
现在简单的介绍get/post方法。
页面间传值有get和post方法之分,而它们代码的差别似乎只是form控件中的action属性不同,执行起来的现象也只是get方法会把参数显示在URL的后面,而post方法不会。
Get方法。
测试get方法很简单,我们需要一个有form控件的html页面和一个接受form控件提交的值的jsp页面。
一般情况下,Jsp页面中取得前页面传递的值方法是request对象的getParameter()方法。
其实,这里也就是要介绍这点,知道的朋友直接跳过即可。
先写一个有form表单的html文件,名字叫HelloGet.html
代码很简单,记得method设定为get,action为这个form提交的页面。Form中有2个input控件。
然后再写一个取得form提交的参数的jsp页面HelloGet.jsp(即刚才action指向的页面)。
这里我们没有见过的代码只有request的2个方法。
request.setCharacterEncoding(String str)
request.getParameter(String str)
setCharacterEncoding()方法是设定取得request对象中的参数和数据的时候使用的编码方式。一般情况下,在中文系统下,设定了服务器的编码方式为GBK的时候(charset=GBK),是不需要设定这句的,但是我现在的系统是日文系统,如果不强制指定编码方式,传递中文参数的时候会出现乱码。
不妨用//注释掉这句话,再让程序执行看看。
getParameter(String str)是取得指定name参数所对应的值。这里,name就是form表单中input控件的名字。
在tomcat下,访问这个HTML页面,输入数据,点击submit按钮,正常的话会跳转到HelloGet.jsp页面,并打印出你提交的信息。如:
大家注意第2页的URL地址栏,会看到被传过来的各个参数,注意中文在传输过程中编码方式被转换过。
很简单吧,POST方法其实和get方法一样,只是提交的过程中在URL后面不会显示提交的参数。例如:
包含form表单的页面。HelloPost.html
接受提交的JSP页面仍然用HelloGet.jsp
访问这个HTML页面,输入数据,点击submit按钮,正常的话会跳转到HelloGet.jsp页面,并打印出你提交的信息。注意这时候地址栏中没有了提交的参数。
很简单,似乎就完了。但是有一点,就是提交的form中有上传文件的控件的时候,参数是怎么传递的呢?
首先,了解下文件上传的时候form表单的参数要求。
首先,method为post,form表单有个enctype参数,默认的情况下为application/x-www-form-urlencoded,文件上传时,要设定为multipart/form-data,form表单中肯定要有1个或n个type为file的input控件。我们先写出这个HTML页面。存为HelloPostFile.html
测试这个页面的时候,却发现取不到name和age参数。难道没传过去?没有的,只是enctyoe更改了属性后,传输的方式也变了。现在我们找到这些传输的数据。
页面跳转的过程中传输的数据是存放在request中的,我们可以从request中将其取出来。在这里,我们以流的方式将数据取出来,使用的方法是request.getInputStream(),这个方法返回的是ServletInputStream对象,Stream这类流对象有一个特点,说得通俗些就是它们像流水一样,操作一次之后,无法返回重新操作。
操作流有许多方法,一种常见的方法就是将这个流一段一段的取出来,进行分析。分段有的按照固定的大小,有的按行读取。这里,按照行读取。
使用的方法是ServletInputStream对象的readLine([]byte b,int offset,int length)
参数说明
b – 用来存放读取数据的数组。
off – 存入数组中的起始位置。
len – 读取的最大行数。
这个方法返回的是读取的byte的数目。
为了避免读取的一行数据过多,byte数组b尽量设定的大一些。但也没必要太夸张。
下面先写出这段jsp代码。
刚才已经介绍了代码中的主要方法,现在没有看到的方法就是new String( buffs, 0, rtnPos )这是一个String对象初始化的方法。将byte数组中指定开始,结束索引的数据生成一个字符串。
而程序的逻辑,如果有过文件读取操作基础的话,很容易理解这段代码的逻辑。
大体逻辑如下
1. 按行读取取得的流中的数据。
2. 如果取得的数据的长度为-1,表明读取完了,结束循环。
3. 如果读取的长度不为-1,表明成功读取,进入循环操作。
4. 将读取的byte数组生成一个新的字符串,在页面中打印这个字符串,打印换行符。
将这个文件存为HelloPostFile.jsp。然后启动tomcat,运行程序,在文件选择框里选择一个小的文本文件,(因为这里面将文件里的数据全打印出来了,如果文件太大,会很慢的)
运行,会发现打印出一段数据。一会再分析这个数据,现在存在一个小问题,如果在输入参数或者上传的文件中有中文的话,很可能出现乱码。我这里是日文的操作系统,在参数输入中文的时候,出现了乱码。
为什么会出现乱码呢?因为数据被我们进行了一遍过滤,将数据取出来,然后重新生成字符串。而new String( buffs, 0, rtnPos )方法生成字符串对象的时候,使用的是默认的编码方式,这样还是存在危险的,所以用new String( buffs, 0, rtnPos ,”GBK”)来代替这个方法,这次再次运行,乱码没有出现。这个方法 与原来的方法的功能相似,只是这个方法指定了编码方式为GBK,这样,就会按照指定的编码方式生成字符串。运行后,可以看到如下的界面。
在这个界面里,可以找到类似这样的数据。
这里注意到我们的参数名和设定的值都被传输过来即可。
还会看到和以下类似的数据。
其中有你的type为file的控件的名字,文件的路径,以及文件的type。在它的下面,打印出了你上传的文件的内容。
由此可见,所有的数据都可以由request对象的getInputStream()方法取得。这样,我们可以自己分析这个流,并将其重新设定,以给我们的程序所用。具体实现方法,等谈到怎么实现文件上传的时候再说吧。
对于get/post方法页面间传值来说,这已经跑题了。
注意:
以上看似很简单,但是细节上还是有不少问题的,需要考虑周密,才能保证写出来的代码bug不会太多。文件上传这个功能,留到以后再作介绍。
Enctype为multipart/form-data时,我们打印出了request对象中getInputStream()取得的流。那可以看看Enctype为application/x-www-form-urlencoded时情况下,打印出来getInputStream()方法取得的流是什么样子的。不过这样的操作会造成request.getParameter()取不到参数。
具体原因,上面已经说过。
注意:
在最初接触动态网站开发的时候,用 到request,session之类的对象的时候,总感觉很奇怪,不知道这些对象哪里来的,于是对服务器有一种很神秘的感觉,似乎服务器里做了许多非常人可以实现的不可思议的事情。
我们知道,java中,我们是这样使用对象的。比如有一个对象ObjectA,这个对象有setName(String str)方法和getName()方法(非静态方法)。如果要使用这个对象的这2个方法,我们必须初始化这个对象,然后才可以使用这些方法。
CODE:
//初始化对象
ObjectA objA = new ObjectA();
//使用这个对象的方法。
objA.setName(“SinNeR”);
String sname = objA.getName();
ObjectA objA = new ObjectA();
//使用这个对象的方法。
objA.setName(“SinNeR”);
String sname = objA.getName();
另,我们所开发的程序,可以看成是就是在用别人提供的很成熟的类来实现我们的功能。(当然里边有很多的技术和知识)TOMCAT下,jsp页面中的代码最终被TOMCAT解释成了java的类,那么session,request这些东西有什么变化呢?实际上他们没有变化,看看昨天HelloDate.jsp生成的java文件。
我们会发现,我们写的jsp代码,都被生成到public void _jspService(HttpServletRequest request, HttpServletResponse response)方法中,而在这个方法中在运行我们的代码之前,已经作了一些操作。
首先,request, response对象是作为参数传入的。
而application,config,session,out对象在方法开始被定义,在执行过程中从 pageContent对象中取得。
所以说来,我们所用的applicaion,config,session,out,request,response对象,并不是很神秘的东西,它们也只是普通的对象,我们只要知道这些对象提供了什么方法,以及一些重要方法的用途就能够很好地运用它们。
现在简单的介绍get/post方法。
页面间传值有get和post方法之分,而它们代码的差别似乎只是form控件中的action属性不同,执行起来的现象也只是get方法会把参数显示在URL的后面,而post方法不会。
Get方法。
测试get方法很简单,我们需要一个有form控件的html页面和一个接受form控件提交的值的jsp页面。
一般情况下,Jsp页面中取得前页面传递的值方法是request对象的getParameter()方法。
其实,这里也就是要介绍这点,知道的朋友直接跳过即可。
先写一个有form表单的html文件,名字叫HelloGet.html
代码很简单,记得method设定为get,action为这个form提交的页面。Form中有2个input控件。
CODE:
<html>
<head>
<title>Test DATE</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<form name="frm" method="get" action="HelloGet.jsp">
USERNAME : <input type="text" name="username" value="">
AGE : <input type="text" name="age" value="">
<input type="submit" name="submit" value="submit">
</form>
</body>
</html>
<head>
<title>Test DATE</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<form name="frm" method="get" action="HelloGet.jsp">
USERNAME : <input type="text" name="username" value="">
AGE : <input type="text" name="age" value="">
<input type="submit" name="submit" value="submit">
</form>
</body>
</html>
然后再写一个取得form提交的参数的jsp页面HelloGet.jsp(即刚才action指向的页面)。
CODE:
<%@ page contentType="text/html; charset=GBK" %>
<html>
<head>
<title>get method</title>
</head>
<body>
<%
request.setCharacterEncoding("GBK");
String username = request.getParameter("username");
String age = request.getParameter("age");
out.println("<h3> hello," + username + ". you are " + age + "</h3>");
%>
</body>
</html>
<html>
<head>
<title>get method</title>
</head>
<body>
<%
request.setCharacterEncoding("GBK");
String username = request.getParameter("username");
String age = request.getParameter("age");
out.println("<h3> hello," + username + ". you are " + age + "</h3>");
%>
</body>
</html>
这里我们没有见过的代码只有request的2个方法。
request.setCharacterEncoding(String str)
request.getParameter(String str)
setCharacterEncoding()方法是设定取得request对象中的参数和数据的时候使用的编码方式。一般情况下,在中文系统下,设定了服务器的编码方式为GBK的时候(charset=GBK),是不需要设定这句的,但是我现在的系统是日文系统,如果不强制指定编码方式,传递中文参数的时候会出现乱码。
不妨用//注释掉这句话,再让程序执行看看。
getParameter(String str)是取得指定name参数所对应的值。这里,name就是form表单中input控件的名字。
在tomcat下,访问这个HTML页面,输入数据,点击submit按钮,正常的话会跳转到HelloGet.jsp页面,并打印出你提交的信息。如:
大家注意第2页的URL地址栏,会看到被传过来的各个参数,注意中文在传输过程中编码方式被转换过。
很简单吧,POST方法其实和get方法一样,只是提交的过程中在URL后面不会显示提交的参数。例如:
包含form表单的页面。HelloPost.html
CODE:
<html>
<head>
<title>Test DATE</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<form name="frm" method="post" action="HelloGet.jsp">
USERNAME : <input type="text" name="username" value="">
AGE : <input type="text" name="age" value="">
<input type="submit" name="submit" value="submit">
</form>
</body>
</html>
<head>
<title>Test DATE</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<form name="frm" method="post" action="HelloGet.jsp">
USERNAME : <input type="text" name="username" value="">
AGE : <input type="text" name="age" value="">
<input type="submit" name="submit" value="submit">
</form>
</body>
</html>
接受提交的JSP页面仍然用HelloGet.jsp
访问这个HTML页面,输入数据,点击submit按钮,正常的话会跳转到HelloGet.jsp页面,并打印出你提交的信息。注意这时候地址栏中没有了提交的参数。
很简单,似乎就完了。但是有一点,就是提交的form中有上传文件的控件的时候,参数是怎么传递的呢?
首先,了解下文件上传的时候form表单的参数要求。
首先,method为post,form表单有个enctype参数,默认的情况下为application/x-www-form-urlencoded,文件上传时,要设定为multipart/form-data,form表单中肯定要有1个或n个type为file的input控件。我们先写出这个HTML页面。存为HelloPostFile.html
CODE:
<html>
<head>
<title>Test DATE</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<form name="frm" method="post" action="HelloPostFile.jsp" ENCTYPE="multipart/form-data">
USERNAME : <input type="text" name="username" value="">
AGE : <input type="text" name="age" value="">
<input type="file" name="filept">
<input type="submit" name="submit" value="submit">
</form>
</body>
</html>
<head>
<title>Test DATE</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<form name="frm" method="post" action="HelloPostFile.jsp" ENCTYPE="multipart/form-data">
USERNAME : <input type="text" name="username" value="">
AGE : <input type="text" name="age" value="">
<input type="file" name="filept">
<input type="submit" name="submit" value="submit">
</form>
</body>
</html>
测试这个页面的时候,却发现取不到name和age参数。难道没传过去?没有的,只是enctyoe更改了属性后,传输的方式也变了。现在我们找到这些传输的数据。
页面跳转的过程中传输的数据是存放在request中的,我们可以从request中将其取出来。在这里,我们以流的方式将数据取出来,使用的方法是request.getInputStream(),这个方法返回的是ServletInputStream对象,Stream这类流对象有一个特点,说得通俗些就是它们像流水一样,操作一次之后,无法返回重新操作。
操作流有许多方法,一种常见的方法就是将这个流一段一段的取出来,进行分析。分段有的按照固定的大小,有的按行读取。这里,按照行读取。
使用的方法是ServletInputStream对象的readLine([]byte b,int offset,int length)
参数说明
b – 用来存放读取数据的数组。
off – 存入数组中的起始位置。
len – 读取的最大行数。
这个方法返回的是读取的byte的数目。
为了避免读取的一行数据过多,byte数组b尽量设定的大一些。但也没必要太夸张。
下面先写出这段jsp代码。
CODE:
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="javax.servlet.ServletInputStream" %>
<html>
<head>
<title>get method</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<%
request.setCharacterEncoding("GBK");
int BUFSIZE = 1024 * 8;
int rtnPos = 0;
byte[] buffs = new byte[ BUFSIZE * 8 ];
ServletInputStream sis = request.getInputStream();
while( (rtnPos = sis.readLine( buffs, 0, buffs.length )) != -1 ){
String strBuff = new String( buffs, 0, rtnPos );
out.print(strBuff);
out.print("<br>");
}
%>
</body>
</html>
<%@ page import="javax.servlet.ServletInputStream" %>
<html>
<head>
<title>get method</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<%
request.setCharacterEncoding("GBK");
int BUFSIZE = 1024 * 8;
int rtnPos = 0;
byte[] buffs = new byte[ BUFSIZE * 8 ];
ServletInputStream sis = request.getInputStream();
while( (rtnPos = sis.readLine( buffs, 0, buffs.length )) != -1 ){
String strBuff = new String( buffs, 0, rtnPos );
out.print(strBuff);
out.print("<br>");
}
%>
</body>
</html>
刚才已经介绍了代码中的主要方法,现在没有看到的方法就是new String( buffs, 0, rtnPos )这是一个String对象初始化的方法。将byte数组中指定开始,结束索引的数据生成一个字符串。
而程序的逻辑,如果有过文件读取操作基础的话,很容易理解这段代码的逻辑。
大体逻辑如下
1. 按行读取取得的流中的数据。
2. 如果取得的数据的长度为-1,表明读取完了,结束循环。
3. 如果读取的长度不为-1,表明成功读取,进入循环操作。
4. 将读取的byte数组生成一个新的字符串,在页面中打印这个字符串,打印换行符。
将这个文件存为HelloPostFile.jsp。然后启动tomcat,运行程序,在文件选择框里选择一个小的文本文件,(因为这里面将文件里的数据全打印出来了,如果文件太大,会很慢的)
运行,会发现打印出一段数据。一会再分析这个数据,现在存在一个小问题,如果在输入参数或者上传的文件中有中文的话,很可能出现乱码。我这里是日文的操作系统,在参数输入中文的时候,出现了乱码。
为什么会出现乱码呢?因为数据被我们进行了一遍过滤,将数据取出来,然后重新生成字符串。而new String( buffs, 0, rtnPos )方法生成字符串对象的时候,使用的是默认的编码方式,这样还是存在危险的,所以用new String( buffs, 0, rtnPos ,”GBK”)来代替这个方法,这次再次运行,乱码没有出现。这个方法 与原来的方法的功能相似,只是这个方法指定了编码方式为GBK,这样,就会按照指定的编码方式生成字符串。运行后,可以看到如下的界面。
在这个界面里,可以找到类似这样的数据。
CODE:
-----------------------------7d72ac15110506
Content-Disposition: form-data; name="username"
测试
Content-Disposition: form-data; name="username"
测试
这里注意到我们的参数名和设定的值都被传输过来即可。
还会看到和以下类似的数据。
CODE:
Content-Disposition: form-data; name="filept"; filename="C:\Documents and Settings\uki.TYO\デスクトップ\SinNeR.txt"
Content-Type: text/plain
Content-Type: text/plain
其中有你的type为file的控件的名字,文件的路径,以及文件的type。在它的下面,打印出了你上传的文件的内容。
由此可见,所有的数据都可以由request对象的getInputStream()方法取得。这样,我们可以自己分析这个流,并将其重新设定,以给我们的程序所用。具体实现方法,等谈到怎么实现文件上传的时候再说吧。
对于get/post方法页面间传值来说,这已经跑题了。
注意:
CODE:
通过从request对象的getInputStream()方法取得页面传输的数据,我们可以看到,似乎作文件上传并不是很难。
技术方面的无非以下几点:
1. 能够从request中取得传递的数据流,并将其读取出来。
2.知道读取出来的流的格式,能够用正确的方法分析,解析这个流。
3.能够将一些参数等非文件的流还原给request对象,以使页面的其他功能不受到影响。
4.要知道怎么进行文件操作,可以将文件中的数据存储到服务器指定的目录下。
技术方面的无非以下几点:
1. 能够从request中取得传递的数据流,并将其读取出来。
2.知道读取出来的流的格式,能够用正确的方法分析,解析这个流。
3.能够将一些参数等非文件的流还原给request对象,以使页面的其他功能不受到影响。
4.要知道怎么进行文件操作,可以将文件中的数据存储到服务器指定的目录下。
以上看似很简单,但是细节上还是有不少问题的,需要考虑周密,才能保证写出来的代码bug不会太多。文件上传这个功能,留到以后再作介绍。
Enctype为multipart/form-data时,我们打印出了request对象中getInputStream()取得的流。那可以看看Enctype为application/x-www-form-urlencoded时情况下,打印出来getInputStream()方法取得的流是什么样子的。不过这样的操作会造成request.getParameter()取不到参数。
具体原因,上面已经说过。
注意:
CODE:
HttpServletRequest对象的getInputStream()是会抛出异常的,但是由于现在还没有介绍到异常,所以没有主动捕获异常,这是非常不可取的。
-----------------------------------------------------------------------------------
用TOMCAT作简单的Java web开发-JSP中实现文件上传
用TOMCAT作简单的Java web开发-JSP中实现文件上传
介绍了jsp页面中get/post方法传递参数后,在后面介绍到的文件上传部分,我们似乎已经看到了我们能够通过HttpServletRequest对象的getInputStream()方法取得form表单传递的文件以及其他参数的流,能够得到它,原则上我们就已经可以实现jsp间文件上传的功能了。剩下的主要就是细节问题了。今天就从准备工作开始,一点一点介绍怎么实现jsp间的文件上传。大体分以下步骤。
其中5测试是程序开发中必要的一个环节,即使你的代码能够实现你的功能,你也要测试在不同的情况下程序的正确性。因为用户并不一定会完全按照你预想的操作步骤来执行你的程序,那么,就需要考虑到在不同情况下程序的反应,避免出错。但是这里,就进行简单测试了。如果有朋友对这个功能感兴趣,使用的时候,出现问题可以反馈给我。
好了,现在开始进行准备工作,分析需要的技术。
a,对String对象要非常熟悉,因为在我们取得String流之后,按行取得字符串之后,剩下的操作主要就是对String对象的各种操作。String对象是基础,对它的熟悉是必要的,要了解它的各种方法的用法。
b,对java的文件操作要非常熟悉,至少,要了解如何生成一个文件。
c,要了解文件上传的一些知识。前2点都是很容易就可以找到来学习的。现在说说文件上传知识从哪里来的。首先,文件上传的时候,form表单的enctype为multipart/form-data。这很容易让我们想到要查找DHTML的知识,到微软的MSDN上找到FORM表单的介绍,发现并没有提供我们想要了解的知识。那么,就去W3C(http://www.w3.org/TR/REC-html32.html)去找找看吧,在w3c网站上找到的FORM信息如下:
似乎也没有讲到我们要的信息,注意最后一句,Further details on handling forms are given in RFC 1867. 那么我们再找RFC 1867文档来看看,搜索后在http://www.faqs.org/rfcs/rfc1867.html找到了RFC 1867的介绍。翻看了一下,在Example中发现了这么一段
是不是发现这里有许多东西在昨天的getInputStream()的输出里很相似?OK!就是它了,简单看了下整篇介绍,对文件上传有了大体的了解。
注意:
技术方面,主要就是这3点了,主要是通过自己的学习和搜索引擎得到的。现在开始分析如何实现文件上传的功能。
在分析之前,我们要考虑一点细节问题。比如,如果form中普通的input控件有2个名字相同,在getInputStream流中是什么样的呢?如果上传多个文件,在getInputStream流中是什么样的呢?如果form中普通的input控件和文件上传控件名字相同,在getInputStream流中是什么样的呢?这些可能不太符合规范,但是如果可以这样写,我们就要考虑这种情况出现的时候,该怎样应对。动手前想的越多,写出来的代码可能存在的bug可能会越少。
现在就尝试一下,写一个html页面,form中有2个相同name的input控件,有2个相同name的文件上传控件,有一个input控件和文件上传控件的名字相同。代码如下:
HelloUpFormat.html
提交到昨天写的HelloGet.jsp页面。注意选择上传文件的时候选择不同的文件,以便区分清楚。
提交后显示的数据如下:
会发现相同的名称并没有影响参数以及文件的提交。那么,我们在实现功能的时候也要注意,保留用户的提交信息。不要丢失参数,同时,要正常的支持多文件上传的功能。
另外,如果文件上传控件没有选择文件的时候,得到的流是这样的。
这点也要注意。
至此,jsp文件上传的功能和技术准备我们大体已经准备的差不多了。现在开始分析业务流程以及实现细节的问题了。
Jsp页面程序的大体流程为:
1. 取得要分析的流。
2. 分析取得的流,将普通的参数保存,留作页面取得用。将文件的流和文件名保存,在服务器端保存文件。
3. 提供方法,使用户可以方便的取得要用的参数(这个不是必须的)
这样来看,难点就在对取得的流的分析上。这,就需要自己认真的思考了。
考虑后,可以大体画出以下的流程图。
大家可以尝试着走一遍这个流程图看看能不能走得通。
上面的流程图似乎已经比较清晰,比较严谨。但是有一个问题,如果上传的文件中有Content-Disposition: form-data; name="input_file"这样的字符串,这个流程图就会错误的把它当成一个新的参数来进行分析,这样程序就出现了严重的错误!这是一个危险的bug。那么,我们就要修正这个流程,仔细观察会发现,Content-Disposition: form-data; name="input_file"这个设定参数名字的字符串至会出现在-----------------------------7d71343b07f4的下面,所以,每次读到-----------------------------7d71343b07f4的时候,立刻再读一行,来读取参数的name,这样就避免了上面的问题。流程修改如下:
这时候可能细心的人会想到,如果上传的文件中还有-----------------------------7d71343b07f4字符串,那么对流的分析岂不是也乱了?这个不用担心,因为-----------------------------7d71343b07f4是一个随机的字符串,用户的文件碰巧和这个字符串一样的概率是微之甚微的。
这样,我们就分析完大体的流成了。剩下的工作就是编码实现了。
编码之前,还要交待一些程序实现的细节问题。
1.对于参数的value,用什么对象保存来好。
如开始所看到的,对应同一个name的参数,可能有几个value,如果我们用String来存储的话,就会造成数据丢失。很容易就想到了数组,但是数组在初始化的时候,必须为其定义大小,而在分析之前,我们是不知道同一个name的value有多少个的。这里,我们用ArrayList对象来存储value。
ArrayList对象其实也是一个类似数组的容器,但是它在初始化的时候不需要指定大小。每次有新的元素,尽管加进去就可以。
和数组相似,ArrayList也存在索引(index)。以下是ArrayList的一些常用方法。
2. 参数怎么保存。
传递过来的参数中,名字不同,会有很多,而且不知道他们的大小。我们用什么来保存它呢?
注意到这种数据的形式是,有一个name,对应一个value对象。这个,跟java中的Hashtable非常相似。
所以我们选择用Hashtable来保存数据。Hashtable比较适合保存一个name对应一个value对象的数据。
3. 文件的简单操作。
新建一个文件,并写入文件的内容,一般的情况下会以流的形式写入文件。
关于文件操作的介绍也很多,这里暂时就不详细介绍了。
有了以上的知识。
我们可以初步对代码进行实现了。注意,这是一个稍显复杂的过程,所以要一步一步,先写好大的逻辑,再写细节,这样可以保持清晰地结构,避免一下子就想写完,结果最后整的自己也搞不清逻辑了。同时写的过程中记得加入注释,这也是良好的习惯。对看清source有很大的帮助。
首先,我们拿出昨天的HelloGet.jsp,将我们刚才的流程图作为注释写入代码。
介绍了jsp页面中get/post方法传递参数后,在后面介绍到的文件上传部分,我们似乎已经看到了我们能够通过HttpServletRequest对象的getInputStream()方法取得form表单传递的文件以及其他参数的流,能够得到它,原则上我们就已经可以实现jsp间文件上传的功能了。剩下的主要就是细节问题了。今天就从准备工作开始,一点一点介绍怎么实现jsp间的文件上传。大体分以下步骤。
CODE:
1. 准备工作,分析需要的技术。技术包括Java方面的,以及文件上传的技术文档。
2.分析业务流程以及实现的一些细节问题。
3. 初步实现功能。
4. 抽出class,使此功能共通化。
5. 测试。
2.分析业务流程以及实现的一些细节问题。
3. 初步实现功能。
4. 抽出class,使此功能共通化。
5. 测试。
其中5测试是程序开发中必要的一个环节,即使你的代码能够实现你的功能,你也要测试在不同的情况下程序的正确性。因为用户并不一定会完全按照你预想的操作步骤来执行你的程序,那么,就需要考虑到在不同情况下程序的反应,避免出错。但是这里,就进行简单测试了。如果有朋友对这个功能感兴趣,使用的时候,出现问题可以反馈给我。
好了,现在开始进行准备工作,分析需要的技术。
a,对String对象要非常熟悉,因为在我们取得String流之后,按行取得字符串之后,剩下的操作主要就是对String对象的各种操作。String对象是基础,对它的熟悉是必要的,要了解它的各种方法的用法。
b,对java的文件操作要非常熟悉,至少,要了解如何生成一个文件。
c,要了解文件上传的一些知识。前2点都是很容易就可以找到来学习的。现在说说文件上传知识从哪里来的。首先,文件上传的时候,form表单的enctype为multipart/form-data。这很容易让我们想到要查找DHTML的知识,到微软的MSDN上找到FORM表单的介绍,发现并没有提供我们想要了解的知识。那么,就去W3C(http://www.w3.org/TR/REC-html32.html)去找找看吧,在w3c网站上找到的FORM信息如下:
CODE:
FORM
<!ENTITY % HTTP-Method "GET | POST"
-- as per HTTP specification
-->
<!ELEMENT FORM - - %body.content -(FORM)>
<!ATTLIST FORM
action %URL #IMPLIED -- server-side form handler --
method (%HTTP-Method) GET -- see HTTP specification --
enctype %Content-Type; "application/x-www-form-urlencoded"
>
This is used to define an HTML form, and you can have more than one form in the same document. Both the start and end tags are required. For very simple forms, you can also use the ISINDEX element. Forms can contain a wide range of HTML markup including several kinds of form fields such as single and multi-line text fields, radio button groups, checkboxes, and menus.
action
This specifies a URL which is either used to post forms via email, e.g. action="mailto:foo@bar.com", or used to invoke a server-side forms handler via HTTP, e.g. action="http://www.acme.com/cgi-bin/register.pl"
method
When the action attribute specifies an HTTP server, the method attribute determines which HTTP method will be used to send the form's contents to the server. It can be either GET or POST, and defaults to GET.
enctype
This determines the mechanism used to encode the form's contents. It defaults to application/x-www-form-urlencoded.
Further details on handling forms are given in RFC 1867.
<!ENTITY % HTTP-Method "GET | POST"
-- as per HTTP specification
-->
<!ELEMENT FORM - - %body.content -(FORM)>
<!ATTLIST FORM
action %URL #IMPLIED -- server-side form handler --
method (%HTTP-Method) GET -- see HTTP specification --
enctype %Content-Type; "application/x-www-form-urlencoded"
>
This is used to define an HTML form, and you can have more than one form in the same document. Both the start and end tags are required. For very simple forms, you can also use the ISINDEX element. Forms can contain a wide range of HTML markup including several kinds of form fields such as single and multi-line text fields, radio button groups, checkboxes, and menus.
action
This specifies a URL which is either used to post forms via email, e.g. action="mailto:foo@bar.com", or used to invoke a server-side forms handler via HTTP, e.g. action="http://www.acme.com/cgi-bin/register.pl"
method
When the action attribute specifies an HTTP server, the method attribute determines which HTTP method will be used to send the form's contents to the server. It can be either GET or POST, and defaults to GET.
enctype
This determines the mechanism used to encode the form's contents. It defaults to application/x-www-form-urlencoded.
Further details on handling forms are given in RFC 1867.
似乎也没有讲到我们要的信息,注意最后一句,Further details on handling forms are given in RFC 1867. 那么我们再找RFC 1867文档来看看,搜索后在http://www.faqs.org/rfcs/rfc1867.html找到了RFC 1867的介绍。翻看了一下,在Example中发现了这么一段
CODE:
6. Examples
Suppose the server supplies the following HTML:
<FORM ACTION="http://server.dom/cgi/handle"
ENCTYPE="multipart/form-data"
METHOD=POST>
What is your name? <INPUT TYPE=TEXT NAME=submitter>
What files are you sending? <INPUT TYPE=FILE NAME=pics>
</FORM>
and the user types "Joe Blow" in the name field, and selects a text
file "file1.txt" for the answer to 'What files are you sending?'
The client might send back the following data:
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
If the user also indicated an image file "file2.gif" for the answer
to 'What files are you sending?', the client might client might send
back the following data:
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"
Content-type: multipart/mixed, boundary=BbC04y
--BbC04y
Content-disposition: attachment; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--BbC04y
Content-disposition: attachment; filename="file2.gif"
Content-type: image/gif
Content-Transfer-Encoding: binary
...contents of file2.gif...
--BbC04y--
--AaB03x--
Suppose the server supplies the following HTML:
<FORM ACTION="http://server.dom/cgi/handle"
ENCTYPE="multipart/form-data"
METHOD=POST>
What is your name? <INPUT TYPE=TEXT NAME=submitter>
What files are you sending? <INPUT TYPE=FILE NAME=pics>
</FORM>
and the user types "Joe Blow" in the name field, and selects a text
file "file1.txt" for the answer to 'What files are you sending?'
The client might send back the following data:
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
If the user also indicated an image file "file2.gif" for the answer
to 'What files are you sending?', the client might client might send
back the following data:
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"
Content-type: multipart/mixed, boundary=BbC04y
--BbC04y
Content-disposition: attachment; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--BbC04y
Content-disposition: attachment; filename="file2.gif"
Content-type: image/gif
Content-Transfer-Encoding: binary
...contents of file2.gif...
--BbC04y--
--AaB03x--
是不是发现这里有许多东西在昨天的getInputStream()的输出里很相似?OK!就是它了,简单看了下整篇介绍,对文件上传有了大体的了解。
注意:
CODE:
如果仔细观看给的文档的时候,会发现Content-type: multipart/form-data, boundary=AaB03x这句我们没有看到过,这句是哪里来的呢?查看下request方法,发现有个getContentType()方法,在JSP中输出一看,发现是multipart/form-data; boundary=---------------------------7d723f29b07f4,是不是跟文档中的那个非常相似。
原来,在这里,定义了这个随机的字符串,---------------------------7d723f29b07f4,getInputStream()中取得的每个参数,都是以这个字符串做分割的,对比一下,发现getInputStream()中对应的字符串比ContentType中的这个字符串前端多了”--”,而结束的字符串又在后端多了”--”,这些信息很有用,对我们分析getInputStream()取得的流很有帮助!
原来,在这里,定义了这个随机的字符串,---------------------------7d723f29b07f4,getInputStream()中取得的每个参数,都是以这个字符串做分割的,对比一下,发现getInputStream()中对应的字符串比ContentType中的这个字符串前端多了”--”,而结束的字符串又在后端多了”--”,这些信息很有用,对我们分析getInputStream()取得的流很有帮助!
技术方面,主要就是这3点了,主要是通过自己的学习和搜索引擎得到的。现在开始分析如何实现文件上传的功能。
在分析之前,我们要考虑一点细节问题。比如,如果form中普通的input控件有2个名字相同,在getInputStream流中是什么样的呢?如果上传多个文件,在getInputStream流中是什么样的呢?如果form中普通的input控件和文件上传控件名字相同,在getInputStream流中是什么样的呢?这些可能不太符合规范,但是如果可以这样写,我们就要考虑这种情况出现的时候,该怎样应对。动手前想的越多,写出来的代码可能存在的bug可能会越少。
现在就尝试一下,写一个html页面,form中有2个相同name的input控件,有2个相同name的文件上传控件,有一个input控件和文件上传控件的名字相同。代码如下:
HelloUpFormat.html
CODE:
<html>
<head>
<title>Test DATE</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<form name="frm" method="post" action="HelloGet.jsp" ENCTYPE="multipart/form-data">
USERNAME : <input type="text" name="username" value="">
USERNAME2(same name as USERNAME) : <input type="text" name="username" value="">
<br>
TEST input 1<input type="text" name="input_file" value="">
TEST file 1(same name as TEST input 1)<input type="file" name="input_file">
<br>
file pt1<input type="file" name="filept">
same name as file pt1<input type="file" name="filept">
<input type="submit" name="submit" value="submit">
</form>
</body>
</html>
<head>
<title>Test DATE</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head>
<body>
<form name="frm" method="post" action="HelloGet.jsp" ENCTYPE="multipart/form-data">
USERNAME : <input type="text" name="username" value="">
USERNAME2(same name as USERNAME) : <input type="text" name="username" value="">
<br>
TEST input 1<input type="text" name="input_file" value="">
TEST file 1(same name as TEST input 1)<input type="file" name="input_file">
<br>
file pt1<input type="file" name="filept">
same name as file pt1<input type="file" name="filept">
<input type="submit" name="submit" value="submit">
</form>
</body>
</html>
提交到昨天写的HelloGet.jsp页面。注意选择上传文件的时候选择不同的文件,以便区分清楚。
提交后显示的数据如下:
CODE:
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="username"
SinNeR
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="username"
不可饶恕的罪人
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="input_file"
和上传文件控件名一样
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="input_file"; filename="C:\Documents and Settings\uki.TYO\デスクトップ\1.txt"
Content-Type: text/plain
aaaaaaaaaaaaaaaaaaaaa
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="filept"; filename="C:\Documents and Settings\uki.TYO\デスクトップ\2.txt"
Content-Type: text/plain
bbbbbbbbbbbbbbbbbbbbbbbbbbb
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="filept"; filename="C:\Documents and Settings\uki.TYO\デスクトップ\3.txt"
Content-Type: text/plain
cccccccccccccccccccccccccccccccc
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="submit"
submit
-----------------------------7d72451bb07f4--
Content-Disposition: form-data; name="username"
SinNeR
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="username"
不可饶恕的罪人
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="input_file"
和上传文件控件名一样
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="input_file"; filename="C:\Documents and Settings\uki.TYO\デスクトップ\1.txt"
Content-Type: text/plain
aaaaaaaaaaaaaaaaaaaaa
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="filept"; filename="C:\Documents and Settings\uki.TYO\デスクトップ\2.txt"
Content-Type: text/plain
bbbbbbbbbbbbbbbbbbbbbbbbbbb
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="filept"; filename="C:\Documents and Settings\uki.TYO\デスクトップ\3.txt"
Content-Type: text/plain
cccccccccccccccccccccccccccccccc
-----------------------------7d72451bb07f4
Content-Disposition: form-data; name="submit"
submit
-----------------------------7d72451bb07f4--
会发现相同的名称并没有影响参数以及文件的提交。那么,我们在实现功能的时候也要注意,保留用户的提交信息。不要丢失参数,同时,要正常的支持多文件上传的功能。
另外,如果文件上传控件没有选择文件的时候,得到的流是这样的。
CODE:
-----------------------------7d71343b07f4
Content-Disposition: form-data; name="input_file"; filename=""
Content-Type: application/octet-stream
Content-Disposition: form-data; name="input_file"; filename=""
Content-Type: application/octet-stream
这点也要注意。
至此,jsp文件上传的功能和技术准备我们大体已经准备的差不多了。现在开始分析业务流程以及实现细节的问题了。
Jsp页面程序的大体流程为:
1. 取得要分析的流。
2. 分析取得的流,将普通的参数保存,留作页面取得用。将文件的流和文件名保存,在服务器端保存文件。
3. 提供方法,使用户可以方便的取得要用的参数(这个不是必须的)
这样来看,难点就在对取得的流的分析上。这,就需要自己认真的思考了。
考虑后,可以大体画出以下的流程图。
大家可以尝试着走一遍这个流程图看看能不能走得通。
上面的流程图似乎已经比较清晰,比较严谨。但是有一个问题,如果上传的文件中有Content-Disposition: form-data; name="input_file"这样的字符串,这个流程图就会错误的把它当成一个新的参数来进行分析,这样程序就出现了严重的错误!这是一个危险的bug。那么,我们就要修正这个流程,仔细观察会发现,Content-Disposition: form-data; name="input_file"这个设定参数名字的字符串至会出现在-----------------------------7d71343b07f4的下面,所以,每次读到-----------------------------7d71343b07f4的时候,立刻再读一行,来读取参数的name,这样就避免了上面的问题。流程修改如下:
这时候可能细心的人会想到,如果上传的文件中还有-----------------------------7d71343b07f4字符串,那么对流的分析岂不是也乱了?这个不用担心,因为-----------------------------7d71343b07f4是一个随机的字符串,用户的文件碰巧和这个字符串一样的概率是微之甚微的。
这样,我们就分析完大体的流成了。剩下的工作就是编码实现了。
编码之前,还要交待一些程序实现的细节问题。
1.对于参数的value,用什么对象保存来好。
如开始所看到的,对应同一个name的参数,可能有几个value,如果我们用String来存储的话,就会造成数据丢失。很容易就想到了数组,但是数组在初始化的时候,必须为其定义大小,而在分析之前,我们是不知道同一个name的value有多少个的。这里,我们用ArrayList对象来存储value。
ArrayList对象其实也是一个类似数组的容器,但是它在初始化的时候不需要指定大小。每次有新的元素,尽管加进去就可以。
和数组相似,ArrayList也存在索引(index)。以下是ArrayList的一些常用方法。
CODE:
//初期化
ArrayList al = new ArrayList();
//返回ArrayList的大小。
int size = al.size();
//向ArrayList中添加数据。
al.add(object);
//取得索引为i的元素,注意索引越界。
al.get(i);
//判断一个元素在ArrayList中的索引位置,不存在的时候返回-1
al.indexOf(object);
//常用的遍历ArrayList的方法。
for(int i=0;i<al.size();i++)
al.get(i);
ArrayList al = new ArrayList();
//返回ArrayList的大小。
int size = al.size();
//向ArrayList中添加数据。
al.add(object);
//取得索引为i的元素,注意索引越界。
al.get(i);
//判断一个元素在ArrayList中的索引位置,不存在的时候返回-1
al.indexOf(object);
//常用的遍历ArrayList的方法。
for(int i=0;i<al.size();i++)
al.get(i);
2. 参数怎么保存。
传递过来的参数中,名字不同,会有很多,而且不知道他们的大小。我们用什么来保存它呢?
注意到这种数据的形式是,有一个name,对应一个value对象。这个,跟java中的Hashtable非常相似。
所以我们选择用Hashtable来保存数据。Hashtable比较适合保存一个name对应一个value对象的数据。
CODE:
//初期化
Hashtable ht = new Hashtable();
//存放数据
ht.put(name,value);
//取出对应name的数据。
ht.get(name);
Hashtable ht = new Hashtable();
//存放数据
ht.put(name,value);
//取出对应name的数据。
ht.get(name);
3. 文件的简单操作。
新建一个文件,并写入文件的内容,一般的情况下会以流的形式写入文件。
关于文件操作的介绍也很多,这里暂时就不详细介绍了。
CODE:
//新建一个文件。
File file = new File( FILEPATH );
//新建一个文件输出流。
FileOutputStream baos = new FileOutputStream( file );
//新建一个buffer输出流
BufferedOutputStream bos = new BufferedOutputStream( baos );
//向流中写入数据
bos.write( buffs, 0, rtnPos );
baos.flush();
File file = new File( FILEPATH );
//新建一个文件输出流。
FileOutputStream baos = new FileOutputStream( file );
//新建一个buffer输出流
BufferedOutputStream bos = new BufferedOutputStream( baos );
//向流中写入数据
bos.write( buffs, 0, rtnPos );
baos.flush();
有了以上的知识。
我们可以初步对代码进行实现了。注意,这是一个稍显复杂的过程,所以要一步一步,先写好大的逻辑,再写细节,这样可以保持清晰地结构,避免一下子就想写完,结果最后整的自己也搞不清逻辑了。同时写的过程中记得加入注释,这也是良好的习惯。对看清source有很大的帮助。
首先,我们拿出昨天的HelloGet.jsp,将我们刚才的流程图作为注释写入代码。
CODE:
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="javax.servlet.ServletInputStream" %>
<html>
<head>
<title>get method</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head><br
<%@ page import="javax.servlet.ServletInputStream" %>
<html>
<head>
<title>get method</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
</head><br