一.彻底明白操作系统环境变量
设置环境变量有两种方式:第一种是在命令提示符运行窗口中设置;第二种是通过单击“我的电脑→属性→高级”标签的“环境变量”按钮设置。需要注意的是,第一种设置环境变量的方式只对当前运行窗口有效,关闭运行窗口后,设置就不起作用了,而第二种设置环境变量的方式则是永久有效。
2.如何在命令提示符窗口中设置环境变量?
在“开始→运行”框中输入“cmd”后按“确定”按钮,出现命令运行窗口。在命令提示符下输入“set”即可查看环境变量设置。要查看具体某个环境变量的设置,比如要查看path环境变量的设置,可以输入“set path”。要创建一个环境变量,比如要创建一个名为aa的,值为“c:”的环境变量,可以输入“set aa=c:”命令。而要删除一个环境变量,比如要删除aa环境变量,则可输入“set aa=”命令(注意=后面不能有空格)。如何更改一个环境变量的设置呢?更改环境变量有两种情况:一是追加方式,即在不改变环境变量现有设置的情况下,增加变量的值,比如要给环境变量aa增加一个值为“D:”的设置,可以输入“set aa=%path%;D:”。另一种是完全修改方式,对于这种方式,我们可以采用直接创建一个环境变量的方法来实现。
3.用户变量和系统变量的关系是什么?
点击“我的电脑→属性→高级”标签的“环境变量”按钮,出现“环境变量”对话框,如果当前是以Administrator登录系统的用户,对话框的上面为Administrator的用户变量,对话框的下面为系统变量(即相当于系统中所有用户的用户变量)。有的时候我们会看到在用户变量和系统变量中都存在某一个环境变量,比如path,那么path的值到底是用户变量中的值还是系统变量中的值,或者两者都不是呢?答案是两者都不是。path变量的值是用户变量中的值与系统变量中的值的叠加。
4.改变环境变量和环境变量中的值应该注意什么?
环境变量和环境变量的值不要含有空格,也不要用中文,切记!
二.Windows系统环境变量
在Dos输入:SET 而不加参数,可以显示Windows当前的环境变量。
Windows 系统环境变量列表:
%ALLUSERSPROFILE% : 列出所有用户Profile文件位置。
%APPDATA% : 列出应用程序数据的默认存放位置。
%CD% : 列出当前目录。
%CLIENTNAME% : 列出联接到终端服务会话时客户端的NETBIOS名。
%CMDCMDLINE% : 列出启动当前cmd.exe所使用的命令行。
%CMDEXTVERSION% : 命令出当前命令处理程序扩展版本号。
%CommonProgramFiles% : 列出了常用文件的文件夹路径。
%COMPUTERNAME% : 列出了计算机名。
%COMSPEC% : 列出了可执行命令外壳(命令处理程序)的路径。
%DATE% : 列出当前日期。
%ERRORLEVEL% : 列出了最近使用的命令的错误代码。
%HOMEDRIVE% : 列出与用户主目录所在的驱动器盘符。
%HOMEPATH% : 列出用户主目录的完整路径。
%HOMESHARE% : 列出用户共享主目录的网络路径。
%LOGONSEVER% : 列出有效的当前登录会话的域名控制器名。
%NUMBER_OF_PROCESSORS% : 列出了计算机安装的处理器数。
%OS% : 列出操作系统的名字。(Windows XP 和 Windows 2000 列为 Windows_NT.)
%Path% : 列出了可执行文件的搜索路径。
%PATHEXT% : 列出操作系统认为可被执行的文件扩展名。
%PROCESSOR_ARCHITECTURE% : 列出了处理器的芯片架构。
%PROCESSOR_IDENTFIER% : 列出了处理器的描述。
%PROCESSOR_LEVEL% : 列出了计算机的处理器的型号。
%PROCESSOR_REVISION% : 列出了处理器的修订号。
%ProgramFiles% : 列出了Program Files文件夹的路径。
%PROMPT% : 列出了当前命令解释器的命令提示设置。
%RANDOM% : 列出界于0 和 32767之间的随机十进制数。
%SESSIONNAME% : 列出连接到终端服务会话时的连接和会话名。
%SYSTEMDRIVE% : 列出了Windows启动目录所在驱动器。
%SYSTEMROOT% : 列出了Windows启动目录的位置。
%TEMP% and %TMP% : 列出了当前登录的用户可用应用程序的默认临时目录。
%TIME% : 列出当前时间。
%USERDOMAIN% : 列出了包含用户帐号的域的名字。
%USERNAME% : 列出当前登录的用户的名字。
%USERPROFILE% : 列出当前用户Profile文件位置。
%WINDIR% : 列出操作系统目录的位置
三.明明白白XP系统环境变量
近日,笔者一个朋友的计算机出现了一个奇怪现象:在Windows XP系统的命令行模式中运行所有命令都提示该命令不是内部或外部命令,也不能运行可执行文件和或批处理文件。
解决篇:
笔者认为是执行这些命令的可执行文件被误删造成的,进入系统安装目录的system32目录中发现ipconfig等可执行文件仍然存在,并没有被删除或改动的迹象。
第一步:在目录中直接运行这些可执行文件发现可以调出一个命令执行窗口,不过一闪即逝。由此可断定这些可执行文件没有问题,是完好无损的。
第一步:输入“CMD”命令进入命令行模式,然后进入“c:\windows\system32”目录运行ipconfig命令,发现一切正常,IP地址及DNS等信息全部显示出来了。
第四步:笔者通过“我的电脑→属性→高级”标签的“环境变量”按钮查看相关信息。发现“系统变量”中的Path项值为空,这应该是问题的关键,由于默认Path为空,所以在任意路径下运行诸如ipconfig的命令系统将无法找到该程序。双击path参数为其设置变量值“c:\windows\system32”。
第六步:确定后再在命令行模式中输入“ipconfig”就能显示出正确的信息了。
小提示:如果你希望设置多个默认路径可以采用诸如c:\windows\system32;c:\windows的形式,即中间用“;”隔开。
思考篇:
分析本地故障原因就是因为环境变量中的默认路径被删除的结果,默认路径一经设置,当前系统如有程序运行时需要某些DLL或EXE文件,以及Active控件时就会到所有默认路径中去查找,如果在这些目录中查找到相应的程序则自动加载,查找不到则报告缺少某某文件的错误信息。
小知识:什么是环境变量?环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,比如临时文件夹位置和系统文件夹位置等。这点有点类似于DOS时期的默认路径,当你运行某些程时序除了在当前文件夹中寻找外,还会到设置的默认路径中去查找。简单地说这里的“Path”就是一个变量,里面存储了一些常用命令所存放的目录路径。
很多朋友会在自己的计算机上安装双系统,例如C盘安装Windows 98,D盘安装Windows XP。可是某些软件往往只在Windows 98系统中安装,Windows XP系统中是无法正常使用的,比较麻烦却有效的方法是再安装一遍。当我们了解了环境变量中的用途后就可以很好解决双系统的软件共用问题。
小提示:为什么在Windows 98中安装了的软件在Windows XP下无法运行呢(绿色软件除外)?原因是安装软件时往往须要向系统目录中复制某些文件,而使用另外一个系统时会由于缺少这些文件而无法运行。因此,我们可以通过设置环境变量的方法来解决这个问题。
实例:通过设置默认路径解决双系统共用应用软件的问题
任务描述:
安装了双系统的本地计算机,C盘为Windows 98系统,安装了COOL 3D,D盘为Windows XP系统。希望在不重新安装软件的前提下可以在D盘系统中正常运行COOL 3D程序。
在Windows XP中直接找到COOL 3D的安装目录并运行其执行文件,会弹出报错对话框。
实现方法:
第一步:在Windows XP系统中的“我的电脑”上点鼠标右键选择“属性”。在系统属性窗口选择“高级”标签并按“环境变量”按钮。
第二步:将环境变量设置窗口中的系统变量里的Path值中添加如下目录:“c:\windows\system32;c:\windows\system;c:\windows”(不含引号)。
第三步:设置完毕后就可以顺利运行COOL 3D了,如果需要系统文件会自动到上面提到的C盘Windows 98相应目录去查找。
总结:
有些软件不用通过在环境变量中修改默认路径的方法就可以在D盘系统中正常启动,例如超级解霸等软件,因为它们并没有往系统目录中复制任何文件。另外修改默认路径法也并不是对所有软件有效,对于一些启动需要加载注册表中键值才能使用的软件,可能会出现提示要求输入注册码的窗口,输入正确数值后仍即可正常使用。如果通过修改默认路径法仍然无法启动软件的话,很有可能是因为Windows 98与Windows XP系统的某些DLL文件有区别而无法通用,这种情况只能在Windows XP系统中重新安装该软件。
四.我使用的系统为Windows XP,我想把系统临时文件(Temp文件)存放区由C盘移到其他分区(如D盘),请问该如何操作?
在“我的电脑”上点鼠标右键,选择“属性”,然后进入高级选项,点“环境变量”,在“系统变量”下拉菜单中找到“TEMP”、“TMP”这两项,分别编辑,将变量值设为你需要改变的路径(例如D:\temp),注意两项改为相同路径,然后在D盘上建立一个temp目录,最后重启计算机即可。
posted @
2007-07-23 04:24 jadmin 阅读(95) |
评论 (0) |
编辑 收藏
1 最基本的乱码问题。
这个乱码问题是最简单的乱码问题。一般新会出现。就是页面编码不一致导致的乱码。
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page contentType="text/html;charset=iso8859-1"%>
<html>
<head>
<title>中文问题</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
</head>
<body>
我是个好人
</body>
</html>
三个地方的编码。
第一个地方的编码格式为jsp文件的存储格式。Eclipse会根据这个编码格式保存文件。并编译jsp文件
,包括里面的汉字。
第二处编码为解码格式。因为存为UTF-8的文件被解码为iso8859-1,这样 如有中文肯定出乱码。也就
是必须一致。而第二处所在的这一行,可以没有。缺省也是使用iso8859-1的编码格式。所以如果没有
这一行的话,“我是个好人”也会出现乱码。必须一致才可以。
第三处编码为控制浏览器的解码方式。如果前面的解码都一致并且无误的话,这个编码格式没有关系
。有的网页出现乱码,就是因为浏览器不能确定使用哪种编码格式。因为页面有时候会嵌入页面,导致
浏览器混淆了编码格式。出现了乱码。
2 表单使用Post方式提交后接收到的乱码问题
这个问题也是一个常见的问题。这个乱码也是tomcat的内部编码格式iso8859-1在捣乱,也就是说post
提交时,如果没有设置提交的编码格式,则会以iso8859-1方式进行提交,接受的jsp却以utf-8的方式
接受。导致乱码。既然这样的原因,下面有几种解决方式,并比较。
A 接受参数时进行编码转换
String str = new String(request.getParameter("something").getBytes("ISO-8859-1"),"utf-8")
; 这样的话,每一个参数都必须这样进行转码。很麻烦。但确实可以拿到汉字。
B 在请求页面上开始处,执行请求的编码代码, request.setCharacterEncoding("UTF-8"),把提交内
容的字符集设为UTF-8。这样的话,接受此参数的页面就不必在转码了。直接使用
String str = request.getParameter("something");即可得到汉字参数。但每页都需要执行这句话。
这个方法也就对post提交的有效果,对于get提交和上传文件时的enctype="multipart/form-data"是无
效的。稍后下面单独对这个两个的乱码情况再进行说明。
C 为了避免每页都要写request.setCharacterEncoding("UTF-8"),建议使用过滤器对所有jsp
进行编码处理。这个网上有很多例子。请大家自己查阅。
3 表单get提交方式的乱码处理方式。
如果使用get方式提交中文,接受参数的页面也会出现乱码,这个乱码的原因也是tomcat的内部编码格
式iso8859-1导致。Tomcat会以get的缺省编码方式iso8859-1对汉字进行编码,编码后追加到url,导致
接受页面得到的参数为乱码/、。
解决办法:
A 使用上例中的第一种方式,对接受到的字符进行解码,再转码。
B Get走的是url提交,而在进入url之前已经进行了iso8859-1的编码处理。要想影响这个编码则需要在
server.xml的Connector节点增加useBodyEncodingForURI="true"
属性配置,即可控制tomcat对get方式的汉字编码方式,上面这个属性控制get提交也是用
request.setCharacterEncoding("UTF-8")所设置的编码格式进行编码。所以自动编码为utf-8,接受页
面正常接受就可以了。但我认为真正的编码过程是,tomcat又要根据
<Connector port="8080"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000" useBodyEncodingForURI="true"
disableUploadTimeout="true" URIEncoding=”UTF-8”/>
里面所设置的URIEncoding=”UTF-8”再进行一次编码,但是由于已经编码为utf-8,再编码也不会有变
化了。如果是从url获取编码,接受页面则是根据URIEncoding=”UTF-8”来进行解码的。
4 上传文件时的乱码解决
上传文件时,form表单设置的都是enctype="multipart/form-data"。这种方式以流方式提交文件。
如果使用apach的上传组件,会发现有很多乱码想象。这是因为apach的先期commons-fileupload.jar有
bug,取出汉字后进行解码,因为这种方式提交,编码又自动使用的是tomcat缺省编码格式iso-8859-1
。但出现的乱码问题是: 句号,逗号,等特殊符号变成了乱码,汉字如果数量为奇数,则会出现乱码
,偶数则解析正常。
解决方式: 下载commons-fileupload-1.1.1.jar 这个版本的jar已经解决了这些bug。
但是取出内容时仍然需要对取出的字符进行从iso8859-1到utf-8转码。已经能得到正常所有汉字以及字
符。
5 Java代码关于url请求,接受参数的乱码
url的编码格式,取决于上面所说的URIEncoding=”UTF-8”。 如果设定了这个编码格式,则意味着所
有到url的汉字参数,都必须进行编码才可以。否则得到的汉字参数值都是乱码,例如
一个链接 Response.sendDerect(“/a.jsp?name=张大维”);而在a.jsp里面直接使用
String name");得到的就是乱码。因为规定了必须是utf-8才可以,所以,这个转向应该这样写:
Response.sendDerect(“/a.jsp?name=URLEncode.encode(“张大维”,”utf-8”);才可以。
如果不设置这个参数URIEncoding=”UTF-8”, 会怎么样呢? 不设置则就使用了缺省的编码格式
iso8859-1。问题又出来了,第一就是参数值的个数如果是奇数个数,则就可以正常解析,如果使偶数
个数,得到最后字符就是乱码。还有就是如果最后一个字符如果是英文,则就能正常解析,但中文的标
点符号仍出现乱码。权宜之计,如果您的参数中没有中文标点符号,则可以在参数值最后加一个英文符
号来解决乱码问题,得到参数后再去掉这个最后面的符号。也可以凑或使用。
6 脚本代码关于url请求,接受到的参数乱码
脚本中也会进行页面转向的控制,也会涉及到附带参数,并在接受页面解析这个参数的情况。如果这个
汉字参数不进行URIEncoding=”UTF-8”所指定的编码处理,则接受页面接受到的汉字也是乱码。脚本
处理编码比较麻烦,必须有相应的编码脚本对应文件,然后调用脚本中的方法对汉字进行编码即可。
7 关于jsp在MyEclipse中打开的乱码问题
对于一个已经存在的项目,Jsp文件的存储格式可能是utf-8。如果新安装的eclipse,则缺省打开使用
的编码格式都是iso8859-1。所以导致jsp里面的汉字出现乱码。这个乱码比较容易解决,直接到
eclipse3.1的偏好设置里面找到general-〉edidor,设置为您的文件打开编码为utf-8即可。Eclipse会
自动重新以新的编码格式打开。汉字即可正常显示。
8 关于html页面在eclipse中打开出现乱码情况
由于大部分页面都是由dreamweaver制作,其存储格式跟eclipse的识别有差别导致。
一般这种情况,在eclipse中新建一个jsp,直接从dreamweaver复制页面内容粘贴到jsp即可。
//////////////////////////////////////////////////////////////////////////////////////////
jsp中文乱码问题的解决办法 jsp中java中文编码问题的个人经验|终于看到一个完全解决的方案
四月 5th, 2006
====================http://www.glgg.net/blog===================
开发java应用出现乱码是很常见的,毕竟现在unicode的使用还不是很广泛,在使用gb2312(包含了gbk
简体,big5繁体)的系统中要正确实现
中文的display和数据库的存储是最基本的要求。
========================http://www.glgg.net/blog===============
1,首先developer要明确自己为什么会遇到乱码,遇到什么样的乱码(无意义的符号还是一串问号或者
其它什么东西)。
新手遇到一堆很乱的字符时通常不知所措,最直接的反映就是打开google搜索”java中文”(这个字符
串在搜索引擎上的查询频率非常高),
然后一个一个的去看别人的解决方法。这样做没有错,但是很难达到目的,原因下面会提到。
总之,出现乱码的原因是非常多的,解决的方法也完全不一样,要解决问题必须先分析自己的”上下文
环境”。
========================http://www.glgg.net/blog===============
2,具体说来,需要哪些信息才能确定项目中的乱码的根源。
a,开发者所用的操作系统
b,j2ee容器的名称,版本
c,数据库的名称,版本(精确版本)以及jdbc驱动的版本
d,出现乱码的source code(比如是system out 出来的,还是jsp页面中的,如果是jsp中的,那么头
部声明的情况也很重要)
=========================http://www.glgg.net/blog==============
3,如何初步分析乱码出现的原因。
有了上述的信息,基本上就可以发帖求助了,相信放到javaworld等论坛上,很快就会有高手给你提出
有效的解决方案的。
当然不能总靠发帖求助,也要试试自行解决问题。如何下手呢?
a,分析一下你的”乱码”到底是什么编码。这个其实不难,比如
System.out.println(testString);
这一段出现了乱码,那么不妨用穷举法猜测一下它的实际编码格式。
System.out.println(new String(testString.getBytes(”ISO-8859-1〃),”gb2312〃));
System.out.println(new String(testString.getBytes(”UTF8〃),”gb2312〃));
System.out.println(new String(testString.getBytes(”GB2312〃),”gb2312〃));
System.out.println(new String(testString.getBytes(”GBK”),”gb2312〃));
System.out.println(new String(testString.getBytes(”BIG5〃),”gb2312〃));
等等,上述代码的意思是用制定的编码格式去读取testString这个”乱码”,并转换成gb2312(此处仅
以中文为例)
然后你看哪一个转换出来的结果是ok的,那就。。。
b,如果用上面的步骤能得到正确的中文,说明你的数据肯定是在的,只不过是界面中没有正确显示而
已。那么第二步就该纠正你的view部分了
,通常需要检查的是jsp中是否选择了正确的页面编码。
在此要声明被很多人误解的一点,那就是<%@ page contentType=”text/html; charset=GB2312〃 %>
指令和<META http-equiv=Content-Type
content=”text/html; charset=gb2312〃>两者的不同。通常网上的很多文章在提到中文问题时都是说
数据库中选择unicode或者gb2312存储,同
时在jsp中用page指令声明编码就可以解决。但是我觉得这种说法很不负责任,害的我费了N多时间为本
来并不存在的乱码而郁闷。实际上page
的作用是在jsp被编译成为html的过程中提供编码方式让java来”读取”表达式当中的String(有点类
似于上面的第三个语句的作用),而meta
的作用是众所周知的为IE浏览器提供编码选择,是用来”显示”最后的数据的。但是没有看到有人提醒
这一点,我一直把page当成meta在用,
导致本来是iso-8859的数据,被page指令读成gb2312,于是乱码,所以又加了编码转化的函数把所有的
string数据都从iso8859转到gb2312(为
什么这么转,当时也没考虑这么多,因为这么做可以正常显示了,所以就这么改了,呵呵当时实在没有
时间慢慢排查问题了)。
===========================http://www.glgg.net/blog==============
4,数据库选择什么样的编码比较好。
目前流行的DB主要有sql server,mysql,oracle,DB2等,其中mysql作为免费DB中的老大,性能和功
能是得到公认的,安装配置比较方便,相
应的driver也比较完善,性价比是绝对的OK。所以就以mysql为例。
我个人建议采用mysql的默认编码来存储,也就是iso-8859-1(在mysql的选项中对应于latin-1)。理
由主要有这么几个,一是iso-8859-1对中
文的支持不错;二是跟java中的默认编码一致,至少在很多地方免除了转换编码的麻烦;三是默认的比
较稳定,兼容性也更好,因为多编码的
支持是由具体的DB产品提供的,别说跟其它的DB会不兼容,即使自身的不同版本也可能出现兼容性的问
题。
例如mysql 4.0以前的产品中,很多中文的解决方案是利用connection中的characterEncoding字段来制
定编码,比如gb2312什么的,这样是ok
的,因为原数据都是ISO8859_1编码,jdbc驱动会采用url里面指定的character set来进行编码,
resultSet.getString(*)取出的就是编码后的
字符串。这样就直接拿到gb2312的数据了。
但是mysql 4.1的推出给很多dbadmin带来了不小的麻烦,因为mysql4.1支持column level的character
set,每个table,column都可以指定编码
,不指定就是ISO8895_1,因此jdbc取出数据后会根据column的character set来进行编码,而不再是用
一个全局的参数来取所有的数据了。
这从另一个方面也说明了乱码问题的产生实在是很复杂的事情,原因太多了。我也只是针对自己遇
////////////////////////////////////////////////////////////////////////////////////////
jsp中文问题解决之道[转载]
和Java一样,JSP是目前比较热门的一个话题。它是一种在服务器端编译执行的Web设计语言,因为脚本
语言采用了Java,所以JSP继承了Java的所有优点。可是在使用JSP程序的过程中,常遇到中文乱码问题
,很多人为此头疼不已,初学的时候我就深受其害,而且使用平台不同,中文乱码问题的解决方法也不
同,无形中增加了学习JSP的难度。其实,在彻底了解相关原因后,问题还是比较容易解决的。,以下
是我总结的解决方法,相信对读者会有一定的借鉴意义。 (因为我使用得最多的是Tomcat环境,所以
主要是以Tomcat为例,其它的环境只会提及一下,但解决办法也是差不多的!
每个国家(或区域)都规定了计算机信息交换用的字符编码集,如美国的扩展ASCII码、中国的GB2312
-80、日本的 JIS 等,作为该国家(区域)信息处理的基础,有着统一编码的重要作用。由于各本地字
符集代码范围重叠,相互间信息交换困难,软件本地化版本独立维护成本较高。因此有必要将本地化工
作中的共性抽取出来,做一致性处理,将特殊的本地化处理内容降低到最少,这就是所谓的国际化
(I18N)。各种语言信息被规范为本地信息,而底层字符集采用包含了所有字符的Unicode。
相信了解JSP代码的读者对ISO8859-1一定不陌生,ISO8859-1是我们平时使用比较多的一个CodePage,
它属于西欧语系。GB2312-80 是在国内计算机汉字信息技术发展初始阶段制订的,其中包含了大部分
常用的一、二级汉字和9区的符号。该字符集是几乎所有的中文系统和国际化的软件都支持的中文字符
集,这也是最基本的中文字符集。
GBK 是 GB2312-80 的扩展,是向上兼容的。它包含了20902个汉字,其编码范围是 0x8140~
0xFEFE,剔除高位 0x80 的字位,其所有字符都可以一对一映射到 Unicode 2.0,也就是说 Java
实际上提供了对 GBK 字符集的支持。
>GB18030-2000(GBK2K) 在 GBK 的基础上进一步扩展了汉字,增加了藏、蒙等少数民族的文字。
GBK2K 从根本上解决了字位不够、字形不足的问题。
1.Tomcat 4开发平台
这个版本应该是我们经常用到的版本,所以讨论得会比较详细。
Windows 98/2000下的Tomcat 4以上版本都会出现中文问题(而在Linux下和Tomcat 3.x中则没有问
题),主要表现是页面显示乱码。
为解决这个问题,最简单的方法就是在每个JSP的页面开始处加上<%@ page language=“Java”
contentType=“text/html; charset=gb2312”%>。不过,这还不够,虽然这时显示了中文,但是发现
从数据库读出的字段变成了乱码。经过分析发现: 在数据库中保存的中文字符是正常的,数据库用
ISO8859-1字符集存取数据,而Java程序在处理字符时默认采用统一的ISO8859-1字符集(这也体现了
Java国际化思想),所以在数据添加的时候Java和数据库都是以ISO8859-1方式处理,这样不会出错。但
是在读取数据的时候就出现问题了,因为数据读出也采用ISO8859-1字符集,而 JSP的文件头中有语句
<%@ page language=“Java” contentType=“text/html; charset=gb2312”%>,这说明页面采用
GB2312的字符集显示,这样就和读出的数据不一样。这时页面显示从数据库中读出的字符是乱码,解决
的方法是对这些字符转码,从ISO8859-1转成GB2312,就可以正常显示了。这个解决办法对很多平台具
有通用性,读者可以灵活运用。具体的方法会在以下详细讲解。另外,对于不同的数据库如SQL
Server,Oracle,Mysql,Sybase等,字符集的选择很重要。如果考虑多语言版本,数据库的字符集就
应该统一采用ISO8859-1,需要输出的时候在不同的字符集之间做转换就可以了。
以下是针对不同平台的总结:
(1) JSWDK只适合于普通开发,稳定性和其他问题可能不如商业软件。 由于JDK 1.3版性能要好于
JDK 1.2.2很多,并且对中文的支持也较好,所以应该尽量采用。 现在jdk已经出到1.4版本了,所以
如果允许最好升级到最新的版本,这样对中文的也会较好,而且还可以得到更多的支持。
(2) Tomcat仅仅是一个对JSP 1.1、Servlet 2.2标准的实现, 我们不应该要求这个免费软件在细节
和性能上都面面俱到, 它主要考虑英文用户, 这也是为什么不做特殊转换,汉字用URL方法传递就有
问题的原因。大部分IE浏览器缺省始终以UTF-8发送, 这似乎是Tomcat的一个不足, 另外Tomcat不管
当前的操作系统是什么语言, 都按ISO8859去编译JSP, 似乎也欠妥。
2.JSP代码的中文处理
(1)如果与数据无关的操作,可以在页面首行加入
(2)将Form中的值传送到数据库中再取出来后全变成了“?”。Form用POST提交数据,代码中使用了语
句:
String st=new(request.getParameter(“name”).getBytes(“ISO8859_1”)), 而且也声明了
charset=gb2312。
要处理Form中传递的中文参数,应该在JSP中加入下面的代码,另外定义一个专门解决这个问题的
getStr类,然后对接收到的参数进行转换:
String keyword1=request.getParameter(“keyword1”);
keyword1=getStr(keyword1);
这样就可以解决问题了,代码如下:
<%@ page contentType=“text/html;charset=gb2312”%>
<%!
public String getStr(String str){
try{String temp_p=str;
byte[] temp_t=temp_p.getBytes(“ISO8859-1”);
String temp=new String(temp_t);
return temp;
}
catch(Exception e){ }
return “NULL”;
}
%>
<%--http://www.cndes.com测试--%>
<% String keyword=“创联网络技术中心欢迎您的到来”;
String keyword1=request.getParameter(“keyword1”);
keyword1=getStr(keyword1);
out.print(keyword);
out.print(keyword1);
%>
另外,流行的关系数据库系统都支持数据库Encoding,也就是说在创建数据库时可以指定它自己的字符
集设置,数据库的数据以指定的编码形式存储。当应用程序访问数据时,在入口和出口处都会有
Encoding 转换。对于中文数据,数据库字符编码的设置应当保证数据的完整性。 GB2312、GBK、
UTF-8 等都是可选的数据库 Encoding,也可以选择 ISO8859-1 (8-bit), 但会增加了编程的复
杂度,ISO8859-1不是推荐的数据库 Encoding。在JSP/Servlet编程时,可以先用数据库管理系统提供
的管理功能检查其中的中文数据是否正确。
(3)JDBC Driver的字符转换
目前大多数JDBC Driver采用本地编码格式来传输中文字符,例如中文字符“0x4175”会被转成“0x41
”和“0x75”进行传输。因此需要对JDBC Driver返回的字符以及要发给JDBC Driver的字符进行转换
。当用JDBC Driver向数据库中插入数据时,需要先将Unicode转成Native code; 当 JDBC Driver
从数据库中查询数据时,则需要将Native code转换成Unicode。下面给出了这两种转换的实现:
String native2Unicode(String s) {
if (s == null || s.length() == 0) {
return null;
}
byte[] buffer = new byte[s.length()];
for (int i = 0; i s.length(); i++) { if (s.charAt(i)>= 0x100) {
c = s.charAt(i);
byte []buf = (“”+c).getBytes();
buffer[j++] = (char)buf[0];
buffer[j++] = (char)buf[1];
}
else {buffer[j++] = s.charAt(i);}
}
return new String(buffer, 0, j);
}
要注意的是,有些JDBC Driver如果通过JDBC Driver Manager设置了正确的字符集属性,以上方法
就不需要了。具体情况可参考相关JDBC的资料。
其实理解了,中文乱码就这么一回事!反复使用就会摸出一定的门道了!我觉得以上的三种方法,只要你
真的能弄懂,在遇到中文问题时,在这三种方法多试尝,我保证你不再会使这种中文问题所烦!
以上只是自己的一些经验所谈,如果有什么不对,希望能提出,共同学习!
//////////////////////////////////////////////////////////////////////////////////////////
我的乱码之路——JSP与MySQL交互的中文乱码解决方案及总结
首先实现了一个StringConvert bean(GBtoISO()和ISOtoGB()两个方法),解决了与MySQL数据库
交互的时候的部分中文乱码问题:在JSP程序中读取MySQL的中文内容,用这两个方法可以解决乱码问题
。
但是从JSP写入到MySQL的中文内容都成了乱码,并且再读出来的时候也显示为“??”,在这里应
该出现了编码转换过程中的字符信息丢失。郁闷的是,我在命令行窗口中登陆到MySQL后,执行如
“Insert INTO customerVALUES('字符',...)”这样的语句时,写入到数据表中的中文内容又是显示正
常的!!!数据库使用的字符集是utf8。
碰壁多次,终于发现一条解决问题的路径:查看MySQL手册的时候,看到一条这样的语句:
Toallow multiple character sets to be sent from the client, the "UTF-8"encoding should be
used, either by configuring "utf8" as the defaultserver character set, or by configuring
the JDBC driver to use "UTF-8"through the characterEncoding property.
此外,在查阅《MySQL权威指南》时,发现在查询语句中可以使用这样的语法将字符串转换到一个
给定的字符集:_charset str。
其中charset必须是服务器支持的某个字符集。在本例中,shopdb数据库使用的默认字符集是utf8
,于是开始测试:
先输入Insert INTO publish Values('8',_gb2312 '高等教育出版社') 写入后中文变成“??
”
再试Insert INTO publish Values('8',_gbk '高等教育出版社') 结果同上
Insert INTO publish Values('8',_utf8 '高等教育出版社') 这下更干脆,什么都没有!!
快疯了!!没办法,用show character set;命令查看MySQL支持的字符集,心想我都试一遍总
有一个能成功吧。浏览了一下,发现没有几个熟悉的字符集,就只剩下一个latin1(ISO-8859-1)比较常
见了,不会是它吧,一试之下果然便是。
Insert INTO publish Values('8',_latin1 '高等教育出版社') 输入中文能够正确显示。
这下总算找到方法了,把Tomcat下配置的数据库连接池的url改为
”...characterEncoding=UTF-8”,然后把写入数据库的中文内容用
String s2 = new String(s1.getBytes("gb2312"),"ISO-8859-1")进行转码,其中s1为中文字符
串.然后再写入到数据库一切显示正常。
为解决这个问题查看了n多资料,现作一个总结:由于字符集和字符编码方式的不同,在OS以
及程序之间传递数据(尤其是multiple character sets中的数据)时便会产生乱码以及字符信息的丢
失.解决这个问题的关键便是了解数据输出端和接收端使用的字符集和字符编码方式,如果这两种编码
方式不同,便需要在数据出口或入口处进行 转码。一般的说,在编写代码,编译,以及运行期间都会字
符数据的传递,因此需要特别小心。
在编写代码的时候,你可能会使用某种开发工具,例如我正在使用的Eclipse.或许在写的时候
一切正常,可是一旦保存后再次打开文档,所有的中文字符都变成了乱码。这是因为在编写的时候,这
些字符数据都在内存的某个stream中,ok,这没问题,可是保存的时候这个stream中的数据会被写入到
硬盘,使用的就是你的开发工具默认的编码方式,如果很不幸你的开发工具默认编码方式是ISO-8859-1
,中文字符信息就不能正确地存储。Eclipse中可以这样查看并修改默认字符编码方式:Project-
>Properties->info,这里有”default encoding for text file”。如果设置为GBK,那么编写代码
并保存这关就过了。
对于JSP程序而言,编写完代码后就交给Container,首先它们会被转成.java文件,然后编译成
.class才能提交给服务器执行.这个过程也存在字符编码问题.java编译器(javac)使用操作系统的语
言环境作为默认的字符编码方式,JRE(Java Runtime Environment)也是这样。只有当编译和运行环境
的字符编码方式与存储源文件的编码方式相同时,中文字符才能正确地显示。否则就需要在运行时进行
转码,使它们使用兼容的编码。这里的设置可以分为几个层次:操作系统层支持的语言,这是最重要的
,因为它会影响JVM的默认字符编码方式,同时对字符的显示,如字体等有直接影响;J2EE服务器层
,大多数服务器都可以对字符编码进行自定义的配置,例如Tomcat就可以通过web.xml中设置
javaEncoding参数设置字符编码,默认是UTF-8.
IE也可以设置成总是使用UTF-8编码来发送请求.应用程序层,每个配置在服务器下的程序都可以
设置自己的编码方式,这个我目前还没有用到,以后再学习。
运行时的转码,运行时期,应用程序很可能需要与外部系统进行交互,例如对数据库进行读写
,对外部文件进行读写.在这些情况下,应用程序免不了要和外部系统进行数据交换。那么对于中文字
符, 数据出入口的编码方式就显得特别重要了。一般外部系统都有自己的字符编码方式,我的例子中
配置的MySQL就是使用的UTF-8编码。JSP页面通过设定”charset=gb2312”,
使用gb2312编码,在它与数据库交互的时候就需要进行显式的转码才能正确处理中文字符。
posted @
2007-07-22 02:19 jadmin 阅读(61) |
评论 (0) |
编辑 收藏
一、安装篇
jspSmartUpload是由www.jspsmart.com网站开发的一个可免费使用的全功能的文件上传下载组件,适于嵌入执行上传下载操作的JSP文件中。该组件有以下几个特点:
1、使用简单。在JSP文件中仅仅书写三五行JAVA代码就可以搞定文件的上传或下载,方便。
2、能全程控制上传。利用jspSmartUpload组件提供的对象及其操作方法,可以获得全部上传文件的信息(包括文件名,大小,类型,扩展名,文件数据等),方便存取。
3、能对上传的文件在大小、类型等方面做出限制。如此可以滤掉不符合要求的文件。
4、下载灵活。仅写两行代码,就能把Web服务器变成文件服务器。不管文件在Web服务器的目录下或在其它任何目录下,都可以利用jspSmartUpload进行下载。
5、能将文件上传到数据库中,也能将数据库中的数据下载下来。这种功能针对的是MYSQL数据库,因为不具有通用性,所以本文不准备举例介绍这种用法。
jspSmartUpload组件可以从www.jspsmart.com网站上自由下载,压缩包的名字是jspSmartUpload.zip。下载后,用WinZip或WinRAR将其解压到Tomcat的webapps目录下(本文以Tomcat服务器为例进行介绍)。解压后,将webapps/jspsmartupload目录下的子目录Web-inf名字改为全大写的WEB-INF,这样一改jspSmartUpload类才能使用。因为Tomcat对文件名大小写敏感,它要求Web应用程序相关的类所在目录为WEB-INF,且必须是大写。接着重新启动Tomcat,这样就可以在JSP文件中使用jspSmartUpload组件了。
注意,按上述方法安装后,只有webapps/jspsmartupload目录下的程序可以使用jspSmartUpload组件,如果想让Tomcat服务器的所有Web应用程序都能用它,必须做如下工作:
1.进入命令行状态,将目录切换到Tomcat的webapps/jspsmartupload/WEB-INF目录下。
2.运行JAR打包命令:jar cvf jspSmartUpload.jar com
(也可以打开资源管理器,切换到当前目录,用WinZip将com目录下的所有文件压缩成jspSmartUpload.zip,然后将jspSmartUpload.zip换名为jspSmartUpload.jar文件即可。)
3.将jspSmartUpload.jar拷贝到Tomcat的shared/lib目录下。
二、相关类说明篇
㈠ File类
这个类包装了一个上传文件的所有信息。通过它,可以得到上传文件的文件名、文件大小、扩展名、文件数据等信息。
File类主要提供以下方法:
1、saveAs作用:将文件换名另存。
原型:
public void saveAs(java.lang.String destFilePathName)
或
public void saveAs(java.lang.String destFilePathName, int optionSaveAs)
其中,destFilePathName是另存的文件名,optionSaveAs是另存的选项,该选项有三个值,分别是SAVEAS_PHYSICAL,SAVEAS_VIRTUAL,SAVEAS_AUTO。SAVEAS_PHYSICAL表明以操作系统的根目录为文件根目录另存文件,SAVEAS_VIRTUAL表明以Web应用程序的根目录为文件根目录另存文件,SAVEAS_AUTO则表示让组件决定,当Web应用程序的根目录存在另存文件的目录时,它会选择SAVEAS_VIRTUAL,否则会选择SAVEAS_PHYSICAL。
例如,saveAs("/upload/sample.zip",SAVEAS_PHYSICAL)执行后若Web服务器安装在C盘,则另存的文件名实际是c:\upload\sample.zip。而saveAs("/upload/sample.zip",SAVEAS_VIRTUAL)执行后若Web应用程序的根目录是webapps/jspsmartupload,则另存的文件名实际是webapps/jspsmartupload/upload/sample.zip。saveAs("/upload/sample.zip",SAVEAS_AUTO)执行时若Web应用程序根目录下存在upload目录,则其效果同saveAs("/upload/sample.zip",SAVEAS_VIRTUAL),否则同saveAs("/upload/sample.zip",SAVEAS_PHYSICAL)。
建议:对于Web程序的开发来说,最好使用SAVEAS_VIRTUAL,以便移植。
2、isMissing
作用:这个方法用于判断用户是否选择了文件,也即对应的表单项是否有值。选择了文件时,它返回false。未选文件时,它返回true。
原型:public boolean isMissing()
3、getFieldName
作用:取HTML表单中对应于此上传文件的表单项的名字。
原型:public String getFieldName()
4、getFileName
作用:取文件名(不含目录信息)
原型:public String getFileName()
5、getFilePathName
作用:取文件全名(带目录)
原型:public String getFilePathName
6、getFileExt
作用:取文件扩展名(后缀)
原型:public String getFileExt()
7、getSize
作用:取文件长度(以字节计)
原型:public int getSize()
8、getBinaryData
作用:取文件数据中指定位移处的一个字节,用于检测文件等处理。
原型:public byte getBinaryData(int index)。其中,index表示位移,其值在0到getSize()-1之间。
㈡ Files类
这个类表示所有上传文件的集合,通过它可以得到上传文件的数目、大小等信息。有以下方法:
1、getCount
作用:取得上传文件的数目。
原型:public int getCount()
2、getFile
作用:取得指定位移处的文件对象File(这是com.jspsmart.upload.File,不是java.io.File,注意区分)。
原型:public File getFile(int index)。其中,index为指定位移,其值在0到getCount()-1之间。
3、getSize
作用:取得上传文件的总长度,可用于限制一次性上传的数据量大小。
原型:public long getSize()
4、getCollection
作用:将所有上传文件对象以Collection的形式返回,以便其它应用程序引用,浏览上传文件信息。
原型:public Collection getCollection()
5、getEnumeration
作用:将所有上传文件对象以Enumeration(枚举)的形式返回,以便其它应用程序浏览上传文件信息。
原型:public Enumeration getEnumeration()
㈢ Request类
这个类的功能等同于JSP内置的对象request。只所以提供这个类,是因为对于文件上传表单,通过request对象无法获得表单项的值,必须通过jspSmartUpload组件提供的Request对象来获取。该类提供如下方法:
1、getParameter
作用:获取指定参数之值。当参数不存在时,返回值为null。
原型:public String getParameter(String name)。其中,name为参数的名字。
2、getParameterValues
作用:当一个参数可以有多个值时,用此方法来取其值。它返回的是一个字符串数组。当参数不存在时,返回值为null。
原型:public String[] getParameterValues(String name)。其中,name为参数的名字。
3、getParameterNames
作用:取得Request对象中所有参数的名字,用于遍历所有参数。它返回的是一个枚举型的对象。
原型:public Enumeration getParameterNames()
㈣ SmartUpload类这个类完成上传下载工作。
A.上传与下载共用的方法:
只有一个:initialize。
作用:执行上传下载的初始化工作,必须第一个执行。
原型:有多个,主要使用下面这个:
public final void initialize(javax.servlet.jsp.PageContext pageContext)
其中,pageContext为JSP页面内置对象(页面上下文)。
B.上传文件使用的方法:
1、upload
作用:上传文件数据。对于上传操作,第一步执行initialize方法,第二步就要执行这个方法。
原型:public void upload()
2、save
作用:将全部上传文件保存到指定目录下,并返回保存的文件个数。
原型:public int save(String destPathName)
和public int save(String destPathName,int option)
其中,destPathName为文件保存目录,option为保存选项,它有三个值,分别是SAVE_PHYSICAL,SAVE_VIRTUAL和SAVE_AUTO。(同File类的saveAs方法的选项之值类似)SAVE_PHYSICAL指示组件将文件保存到以操作系统根目录为文件根目录的目录下,SAVE_VIRTUAL指示组件将文件保存到以Web应用程序根目录为文件根目录的目录下,而SAVE_AUTO则表示由组件自动选择。
注:save(destPathName)作用等同于save(destPathName,SAVE_AUTO)。
3、getSize
作用:取上传文件数据的总长度
原型:public int getSize()
4、getFiles
作用:取全部上传文件,以Files对象形式返回,可以利用Files类的操作方法来获得上传文件的数目等信息。
原型:public Files getFiles()
5、getRequest
作用:取得Request对象,以便由此对象获得上传表单参数之值。
原型:public Request getRequest()
6、setAllowedFilesList
作用:设定允许上传带有指定扩展名的文件,当上传过程中有文件名不允许时,组件将抛出异常。
原型:public void setAllowedFilesList(String allowedFilesList)
其中,allowedFilesList为允许上传的文件扩展名列表,各个扩展名之间以逗号分隔。如果想允许上传那些没有扩展名的文件,可以用两个逗号表示。例如:setAllowedFilesList("doc,txt,,")将允许上传带doc和txt扩展名的文件以及没有扩展名的文件。
7、setDeniedFilesList
作用:用于限制上传那些带有指定扩展名的文件。若有文件扩展名被限制,则上传时组件将抛出异常。
原型:public void setDeniedFilesList(String deniedFilesList)
其中,deniedFilesList为禁止上传的文件扩展名列表,各个扩展名之间以逗号分隔。如果想禁止上传那些没有扩展名的文件,可以用两个逗号来表示。例如:setDeniedFilesList("exe,bat,,")将禁止上传带exe和bat扩展名的文件以及没有扩展名的文件。
8、setMaxFileSize
作用:设定每个文件允许上传的最大长度。
原型:public void setMaxFileSize(long maxFileSize)
其中,maxFileSize为为每个文件允许上传的最大长度,当文件超出此长度时,将不被上传。
9、setTotalMaxFileSize
作用:设定允许上传的文件的总长度,用于限制一次性上传的数据量大小。
原型:public void setTotalMaxFileSize(long totalMaxFileSize)
其中,totalMaxFileSize为允许上传的文件的总长度。
1、setContentDisposition
作用:将数据追加到MIME文件头的CONTENT-DISPOSITION域。jspSmartUpload组件会在返回下载的信息时自动填写MIME文件头的CONTENT-DISPOSITION域,如果用户需要添加额外信息,请用此方法。
原型:public void setContentDisposition(String contentDisposition)
其中,contentDisposition为要添加的数据。如果contentDisposition为null,则组件将自动添加"attachment;",以表明将下载的文件作为附件,结果是IE浏览器将会提示另存文件,而不是自动打开这个文件(IE浏览器一般根据下载的文件扩展名决定执行什么操作,扩展名为doc的将用word程序打开,扩展名为pdf的将用acrobat程序打开,等等)。
2、downloadFile
作用:下载文件。
原型:共有以下三个原型可用,第一个最常用,后两个用于特殊情况下的文件下载(如更改内容类型,更改另存的文件名)。
① public void downloadFile(String sourceFilePathName)
其中,sourceFilePathName为要下载的文件名(带目录的文件全名)
② public void downloadFile(String sourceFilePathName,String contentType)
其中,sourceFilePathName为要下载的文件名(带目录的文件全名),contentType为内容类型(MIME格式的文件类型信息,可被浏览器识别)。
③ public void downloadFile(String sourceFilePathName,String contentType,String destFileName)
其中,sourceFilePathName为要下载的文件名(带目录的文件全名),contentType为内容类型(MIME格式的文件类型信息,可被浏览器识别),destFileName为下载后默认的另存文件名。
三、文件上传篇
㈠ 表单要求
对于上传文件的FORM表单,有两个要求:
1、METHOD应用POST,即METHOD="POST"。
2、增加属性:ENCTYPE="multipart/form-data"
下面是一个用于上传文件的FORM表单的例子:
<FORM METHOD="POST" ENCTYPE="multipart/form-data"
ACTION="/jspSmartUpload/upload.jsp">
<INPUT TYPE="FILE" NAME="MYFILE">
<INPUT TYPE="SUBMIT">
</FORM>
㈡ 上传的例子
1、上传页面upload.html
本页面提供表单,让用户选择要上传的文件,点击"上传"按钮执行上传操作。
页面源码如下:
<!--
文件名:upload.html
作 者:纵横软件制作中心雨亦奇(zhsoft88@sohu.com)
-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>文件上传</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body>
<p> </p>
<p align="center">上传文件选择</p>
<FORM METHOD="POST" ACTION="jsp/do_upload.jsp"
ENCTYPE="multipart/form-data">
<input type="hidden" name="TEST" value="good">
<table width="75%" border="1" align="center">
<tr>
<td><div align="center">1、
<input type="FILE" name="FILE1" size="30">
</div></td>
</tr>
<tr>
<td><div align="center">2、
<input type="FILE" name="FILE2" size="30">
</div></td>
</tr>
<tr>
<td><div align="center">3、
<input type="FILE" name="FILE3" size="30">
</div></td>
</tr>
<tr>
<td><div align="center">4、
<input type="FILE" name="FILE4" size="30">
</div></td>
</tr>
<tr>
<td><div align="center">
<input type="submit" name="Submit" value="上传它!">
</div></td>
</tr>
</table>
</FORM>
</body>
</html>
2、上传处理页面do_upload.jsp
本页面执行文件上传操作。页面源码中详细介绍了上传方法的用法,在此不赘述了。
页面源码如下:
<%--
文件名:do_upload.jsp
作 者:纵横软件制作中心雨亦奇(zhsoft88@sohu.com)
--%>
<%@ page contentType="text/html; charset=gb2312" language="java"
import="java.util.*,com.jspsmart.upload.*" errorPage="" %>
<html>
<head>
<title>文件上传处理页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body>
<%
// 新建一个SmartUpload对象
SmartUpload su = new SmartUpload();
// 上传初始化
su.initialize(pageContext);
// 设定上传限制
// 1.限制每个上传文件的最大长度。
// su.setMaxFileSize(10000);
// 2.限制总上传数据的长度。
// su.setTotalMaxFileSize(20000);
// 3.设定允许上传的文件(通过扩展名限制),仅允许doc,txt文件。
// su.setAllowedFilesList("doc,txt");
// 4.设定禁止上传的文件(通过扩展名限制),禁止上传带有exe,bat,
jsp,htm,html扩展名的文件和没有扩展名的文件。
// su.setDeniedFilesList("exe,bat,jsp,htm,html,,");
// 上传文件
su.upload();
// 将上传文件全部保存到指定目录
int count = su.save("/upload");
out.println(count+"个文件上传成功!<br>");
// 利用Request对象获取参数之值
out.println("TEST="+su.getRequest().getParameter("TEST")
+"<BR><BR>");
// 逐一提取上传文件信息,同时可保存文件。
for (int i=0;i<su.getFiles().getCount();i++)
{
com.jspsmart.upload.File file = su.getFiles().getFile(i);
// 若文件不存在则继续
if (file.isMissing()) continue;
// 显示当前文件信息
out.println("<TABLE BORDER=1>");
out.println("<TR><TD>表单项名(FieldName)</TD><TD>"
+ file.getFieldName() + "</TD></TR>");
out.println("<TR><TD>文件长度(Size)</TD><TD>" +
file.getSize() + "</TD></TR>");
out.println("<TR><TD>文件名(FileName)</TD><TD>"
+ file.getFileName() + "</TD></TR>");
out.println("<TR><TD>文件扩展名(FileExt)</TD><TD>"
+ file.getFileExt() + "</TD></TR>");
out.println("<TR><TD>文件全名(FilePathName)</TD><TD>"
+ file.getFilePathName() + "</TD></TR>");
out.println("</TABLE><BR>");
// 将文件另存
// file.saveAs("/upload/" + myFile.getFileName());
// 另存到以WEB应用程序的根目录为文件根目录的目录下
// file.saveAs("/upload/" + myFile.getFileName(),
su.SAVE_VIRTUAL);
// 另存到操作系统的根目录为文件根目录的目录下
// file.saveAs("c:\\temp\\" + myFile.getFileName(),
su.SAVE_PHYSICAL);
}
%>
</body>
</html>
四、文件下载篇
1、下载链接页面download.html
页面源码如下:
<!--
文件名:download.html
作 者:纵横软件制作中心雨亦奇(zhsoft88@sohu.com)
-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>下载</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body>
<a href="jsp/do_download.jsp">点击下载</a>
</body>
</html>
2、下载处理页面do_download.jsp do_download.jsp展示了如何利用jspSmartUpload组件来下载文件,从下面的源码中就可以看到,下载何其简单。
源码如下:
<%@ page contentType="text/html;charset=gb2312"
import="com.jspsmart.upload.*" %><%
// 新建一个SmartUpload对象
SmartUpload su = new SmartUpload();
// 初始化
su.initialize(pageContext);
// 设定contentDisposition为null以禁止浏览器自动打开文件,
//保证点击链接后是下载文件。若不设定,则下载的文件扩展名为
//doc时,浏览器将自动用word打开它。扩展名为pdf时,
//浏览器将用acrobat打开。
su.setContentDisposition(null);
// 下载文件
su.downloadFile("/upload/如何赚取我的第一桶金.doc");
%>
注意,执行下载的页面,在Java脚本范围外(即<% ... %>之外),不要包含HTML代码、空格、回车或换行等字符,有的话将不能正确下载。不信的话,可以在上述源码中%><%之间加入一个换行符,再下载一下,保证出错。因为它影响了返回给浏览器的数据流,导致解析出错。
3、如何下载中文文件
jspSmartUpload虽然能下载文件,但对中文支持不足。若下载的文件名中有汉字,则浏览器在提示另存的文件名时,显示的是一堆乱码,很扫人兴。上面的例子就是这样。(这个问题也是众多下载组件所存在的问题,很少有人解决,搜索不到相关资料,可叹!)
为了给jspSmartUpload组件增加下载中文文件的支持,我对该组件进行了研究,发现对返回给浏览器的另存文件名进行UTF-8编码后,浏览器便能正确显示中文名字了。这是一个令人高兴的发现。于是我对jspSmartUpload组件的SmartUpload类做了升级处理,增加了toUtf8String这个方法,改动部分源码如下:
public void downloadFile(String s, String s1, String s2, int i)
throws ServletException, IOException, SmartUploadException
{
if(s == null)
throw new IllegalArgumentException("File ''" + s +
"'' not found (1040).");
if(s.equals(""))
throw new IllegalArgumentException("File ''" + s +
"'' not found (1040).");
if(!isVirtual(s) && m_denyPhysicalPath)
throw new SecurityException("Physical path is
denied (1035).");
if(isVirtual(s))
s = m_application.getRealPath(s);
java.io.File file = new java.io.File(s);
FileInputStream fileinputstream = new FileInputStream(file);
long l = file.length();
boolean flag = false;
int k = 0;
byte abyte0[] = new byte[i];
if(s1 == null)
m_response.setContentType("application/x-msdownload");
else
if(s1.length() == 0)
m_response.setContentType("application/x-msdownload");
else
m_response.setContentType(s1);
m_response.setContentLength((int)l);
m_contentDisposition = m_contentDisposition != null ?
m_contentDisposition : "attachment;";
if(s2 == null)
m_response.setHeader("Content-Disposition",
m_contentDisposition + " filename=" +
toUtf8String(getFileName(s)));
else
if(s2.length() == 0)
m_response.setHeader("Content-Disposition",
m_contentDisposition);
else
m_response.setHeader("Content-Disposition",
m_contentDisposition + " filename=" + toUtf8String(s2));
while((long)k < l)
{
int j = fileinputstream.read(abyte0, 0, i);
k += j;
m_response.getOutputStream().write(abyte0, 0, j);
}
fileinputstream.close();
}
/**
* 将文件名中的汉字转为UTF8编码的串,以便下载时能正确显示另存的文件名.
* 纵横软件制作中心雨亦奇2003.08.01
* @param s 原文件名
* @return 重新编码后的文件名
*/
public static String toUtf8String(String s) {
StringBuffer sb = new StringBuffer();
for (int i=0;i<s.length();i++) {
char c = s.charAt(i);
if (c >= 0 && c <= 255) {
sb.append(c);
} else {
byte[] b;
try {
b = Character.toString(c).getBytes("utf-8");
} catch (Exception ex) {
System.out.println(ex);
b = new byte[0];
}
for (int j = 0; j < b.length; j++) {
int k = b[j];
if (k < 0) k += 256;
sb.append("%" + Integer.toHexString(k).
toUpperCase());
}
}
}
return sb.toString();
}
注意源码中粗体部分,原jspSmartUpload组件对返回的文件未作任何处理,现在做了编码的转换工作,将文件名转换为UTF-8形式的编码形式。UTF-8编码对英文未作任何处理,对中文则需要转换为%XX的形式。toUtf8String方法中,直接利用Java语言提供的编码转换方法获得汉字字符的UTF-8编码,之后将其转换为%XX的形式。
将源码编译后打包成jspSmartUpload.jar,拷贝到Tomcat的shared/lib目录下(可为所有WEB应用程序所共享),然后重启Tomcat服务器就可以正常下载含有中文名字的文件了。另,toUtf8String方法也可用于转换含有中文的超级链接,以保证链接的有效,因为有的WEB服务器不支持中文链接。
小结:jspSmartUpload组件是应用JSP进行B/S程序开发过程中经常使用的上传下载组件,它使用简单,方便。现在我又为其加上了下载中文名字的文件的支持,真个是如虎添翼,必将赢得更多开发者的青睐。
posted @
2007-07-17 16:06 jadmin 阅读(64) |
评论 (0) |
编辑 收藏
要servlet里面支持中文的方法:
在dopost或者doget的第一句加上:
request.setCharacterEncoding("GB2312");
response.setCharacterEncoding("GB2312");
在jsp里面就把
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" errorPage="" %>
里设置成charset=gb2312
如果这样还不行,就只有在取出中文后(getParameter)强制转换了:
String str=new String(request.getParameter("text").getBytes("ISO-8859-1"),"GB2312");
很不错的方法,因为在java里面,乱码是个大问题,这些一定要铭记!
posted @
2007-07-17 16:02 jadmin 阅读(77) |
评论 (0) |
编辑 收藏
//用命令行参数
import java.io.*;
public class AddArgs
{
public static void main(String args[]) throws IOException
{
int sum;
try
{
sum = Integer.parseInt(args[0])+Integer.parseInt(args[1]);
System.out.println("两参数的和是:"+sum);
}
catch (Exception e)
{
System.out.println("参数出现错误!");
}
}
}
=========================================================
//用system.in
import java.io.*;
public class AddInput
{
public static void main(String args[]) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int x=0,y=0;
try{
System.out.println("请输入第一个整数:");
x=Integer.parseInt(br.readLine());
System.out.println("请输入第二个整数:");
y=Integer.parseInt(br.readLine());
System.out.println("它们的和是:"+(x+y));
}
catch (Exception e)
{
System.out.println("输入有错误,请重新运行!");
}
}
}
=========================================================
//用scanner
import java.io.*;
import java.util.*;
public class AddScanner
{
public static void main(String args[]) throws IOException
{
Scanner sc=new Scanner(System.in);
int x=0,y=0;
try
{
System.out.println("请输入第一个整数:");
x=sc.nextInt();
System.out.println("请输入第二个整数:");
y=sc.nextInt();
System.out.println("它们的和是:"+(x+y));
}
catch (Exception e)
{
System.out.println("输入有错误,请重新运行!");
}
}
}
posted @
2007-07-17 00:03 jadmin 阅读(111) |
评论 (0) |
编辑 收藏
输入流FileInputStream和输出流 FileOutputStream,实现的是对磁盘文件的顺序读写,而且读写要分别创建不同对象。相比之下RandomAccessFile类则可对文件实现随机读写操作。
RandomAccessFile对象的文件位置指针遵循下面的规律:
·新建RandomAccessFile对象的文件位置指针位于文件的开头处;
·每次读写操作之后,文件位置的指针都相应后移到读写的字节数;
·可以通过getFilePointer方法来获得文件位置指针的位置,通过seek方法来设置文件指针的位置。
RandomAccessFile例子:
package net;
import java.io.*;
class RandomAccessFileDemo
{
public static void main(String args[]) throws IOException
{
//以读和写的方式创建RandomAccessFile对象
RandomAccessFile f=new RandomAccessFile("myfile","rw");
System.out.println("File length:"+(f.length())+"B");
System.out.println("File Pointer Position:"+f.getFilePointer());
//下面从文件末尾处开始写数据
f.seek(f.length());
f.writeBoolean(true);
f.writeBoolean(false);
f.writeChar('a');
f.writeChars("Hello!!");
System.out.println("File length:"+(f.length())+"B");
//下面从文件起始处开始读数据
f.seek(0);
System.out.println("kkk::"+f.readBoolean());
System.out.println("kkk::"+f.readBoolean());
while(f.getFilePointer()<f.length())
{
System.out.println(f.readLine());
}
f.close();
}
}
posted @
2007-07-16 22:53 jadmin 阅读(91) |
评论 (0) |
编辑 收藏
最近经常有人问及在上网时,被恶意网站修改注册表的问题,我整理了几种常见的问题及解决办法,供大家参考,希望对大家有所帮助。
1.修改IE的标题栏和IE默认连接首页:HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Internet Explorer\\Main HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\Main在注册表中找到以上两处主键,将其下的“Window Title”主键改为“IE浏览器”等你喜欢的名字,并关闭所有打开的IE浏览器窗口再重新打开就能看到效果。找到串值“Start Page”, 改为自己喜欢的网址即可。
重启以后又会变成了别人的网址的解决办法:HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\Current Version\\Run主键,然后将其下的registry.exe子键删除,然后删除自运行程序c:\\Program Files\\registry.exe,最后从IE选项中重新设置起始页就好了。
改回IE的默认页:HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Internet Explorer\\ Main\\Default_Page_URL“Default_Page_URL”这个子键的键值即起始页的默认页。
2.修改IE的首页:开始,运行,键入msconfig点击“确定”,在弹出的窗口中切换到“启动”选项卡,禁用可疑程序启动项。
3. 系统启动时弹出对话框,点确定才能进去:方法(1):HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Winlogon在注册表中找到此主键,“LegalNoticeCaption”是提示框的标题,“LegalNoticeText”是提示框的文本内容。删除这两个字符串即可。
方法(2):对win2000或winxp:点击“安全与多用户”,再点击左上角的“+”切换窗口后,清除“启动时要显示的标题”和“启动时要显示的信息”两项内容即可。
方法(3):看看run.
4.修改IE鼠标右键菜单里显示网页广告:HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\MenuExt
5. IE首页解锁:[HKEY_CURRENT_USER\\Software\\Policies\\Microsoft\\Internet Explorer\\Control Panel], 一般此键是不存在的, 只存在[HKEY_CURRENT_USER\\Software\\Policies\\Microsoft], 所以后面一截要自己建立, 主键建立完后在Control Panel键下新建一个DWORD值数据, 键名为HOMEPAGE(不分大小写), 键值为0. 此时再打开IE属性时可以发现它改首页设置的部分已经可用了。
IE设置项解锁:[HKEY_CURRENT_USER\\Software\\Policies\\Microsoft\\Internet Explorer\\Control Pan el]"Settings"=dword:1 [HKEY_CURRENT_USER\\Software\\Policies\\Microsoft\\Internet Explorer\\Control Pan el]"Links"=dword:1 [HKEY_CURRENT_USER\\Software\\Policies\\Microsoft\\Internet Explorer\\Control Pan el]"SecAddSites"=dword:1将上面这些DWORD值改为“0”即可恢复功能。
IE的默认首页灰色按扭不可选:HKEY_USERS\\.DEFAULT\\Software\\Policies\\Microsoft\\Internet Explorer\\Control Pan下的DWORD值“homepage”的键值修改为“0” “即可恢复功能。
6. 去掉注册表编辑器被锁定:这是因为KEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System下的DWORD值“DisableRegistryTools”被修改为“1”的缘故,将其键值恢复为“0”即可恢复注册表的使用。
win2000系统:Windows Registry Editor Version 5.00 [Hkey_current_user\\Software\\microsoft\\windows\\currentversion\\Policies\\system] "DisableRegistryTools"=dword:00000000 win98/me系统:REGEDIT4
face=Verdana> [HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System] "DisableRegistryTools"=dword:00000000将以上代码copy到记事本中, 取一个任意名字的。reg文件,比如recover.reg,双击运行可以解除你的锁定状态。注意:“REGEDIT4”一定要大写,并且它的后面一定要空一行,还有,“REGEDIT4”中的“4”和“T”之间一定不能有空格,否则将前功尽弃!
7. IE默认搜索引擎被修改:HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Internet Explorer\\Search\\CustomizeSearch HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Internet Explorer\\Search\\SearchAssistant
8. 查看源文件“菜单被禁用:恶意网页修改了注册表,具体的位置为:(1)在注册表HKEY_CURRENT_USER\\Software\\Policies\\Microsoft\\Internet Explorer下建立子键”Restrictions“,然后在”Restrictions“下面建立两DWORD值:”NoViewSource“和”NoBrowserContextMenu“,并为这两个DWORD值赋值为”1“。
(2)在注册表 HKEY_USERS\\.DEFAULT\\Software\\Policies\\Microsoft\\InternetExplorer\\Restrictions下,将两个DWORD值:“NoViewSource”和“NoBrowserContextMenu”的键值都改为了“1”。第2点中提到的注册表其实相当于第1点中提到的注册表的分支,修改第1点中所说的注册表键值,第2点中注册表键值随之改变。 具体解决办法为:将以下内容另存为后缀名为reg的注册表文件,比方说unlock.reg,双击unlock.reg导入注册表,不用重启电脑 ,重新运行IE就会发现IE的功能恢复正常了。
REGEDIT4
[HKEY_CURRENT_USER\\Software\\Policies\\Microsoft\\Internet Explorer \\Restrictions]“NoViewSource”=dword:00000000 "NoBrowserContextMenu"=dword:00000000 [HKEY_USERS\\.DEFAULT\\Software\\Policies\\Microsoft\\Internet Explorer \\Restrictions]“NoViewSource”=dword:00000000“NoBrowserContextMenu”=dword:00000000
要特别注意的是,在你编制的注册表文件unlock.reg中,“REGEDIT4”一定要大写,并且它的后面一定要空一行,还有,“REGEDIT4”中的“4”和“T”之间一定不能有空格,否则将前功尽弃!许多朋友写注册表文件之所以不成功,就是因为没有注意到上面所说的内容,这回该注意点喽。请注意如果你是Win2000或WinXP用户,请将 “REGEDIT4”改为Windows Registry Editor Version 5.00.
★ 可以在IE中做一些设置以便永远不进恶意站点:
打开IE,点击“工具”→“Internet选项”→“内容”→“分级审查”,点“启用 ”按钮,会调出“分级审查”对话框,然后点击“许可站点”标签,输入不想去的网站网址,如输入:http://***.****.com,按“从不”按钮,再点击“确定”即大功告成!
9. IE不能打开新窗口的解决办法:(1.)点击“开始”→“运行”,在弹出的“运行”对话框中输入“regsvr32 actxprxy.dll”(注意输入时没有引号),然后点击“确定”按钮,接着会出现一个信息对话框“DllRegisterServer in actxprxy.dll succeeded”;在该对话框中点“确定”按钮;(2.)再次点击“开始”→“运行”,在弹出的“运行”对话框中输入“regsvr32 shdocvw.dll”(注意输入时没有引号),然后点击“确定”按钮,接着会出现一个信息对话框“DllRegisterServer in shdocvw.dll succeeded”,在该对话框中点“确定”按钮;(3.)重新启动Windows,
10. 右键功能失效:HKEY_CURRENT_USER\Software\Policies\Microsoft\Internet Explorer\Restrictions,将其DWORD值"NoBrowserContextMenu"的值改为0.
11. 篡改地址栏文字:解决办法:1.地址栏下的文字。在HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\ToolBar下找到键值LinksFolderName,将其中的内容删去即可。
2.地址栏中无用的地址。在HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\TypeURLs中删除无用的键值即可。
12. IE窗口定时弹出:点击"开始-运行-输入msconfig",选择"启动",把里面后缀为hta的都勾掉,重启。
13. 修改IE工具栏:对工具栏按钮点右键选“自定义”,在“当前工具栏按钮”下拉框中选定不需要的按钮后点击“删除”即可。
要去掉多余的地址列表:[HKEY_CURRENT_USERSoftwareWicrosoftInternet ExplorerTypeURLs]主键,将右部窗口中“url1”、“url2”等键值名全部删除即可。
要修复链接栏标题,首先展开[HKEY_CURRENT_USERSoftwareWicrosoftInternet ExplorerToolbar]主键,在右部窗口中对键值名“LinksFolderName”双击,修改其键值为欲显示的信息,或直接将该键值名删除,链接栏的标题将恢复为默认的“链接”字样。
恢复Internet选项卡:在“HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer”下在右边的窗口中找到一个二进制值“NoFolderOptions”,并设值为“00 00 00 00”。
桌面上的图标全部消失:HKEY_CURRENT_USER\\ Software\\Microsoft\\Windows\\ CurrentVersion\\Policies\\Explorer,找到“NoDesktop”值项,右键单击它,选择“修改”(或者干脆删除也可以),把它的值改为0,然后重新启动电脑。
posted @
2007-07-12 03:45 jadmin 阅读(50) |
评论 (0) |
编辑 收藏
这只是个windows系统下不让人注意的操作,有不少朋友为此摸不着头脑,所以今天整理下给大家参考参考。呵呵
IE会记录你上次关闭IE时候的设置.将IE最大化后关闭(关闭所有窗口),再打开就是最大化的了。
或者 在IE的快捷方式(注意!必须是快捷方式!!)上单击鼠标右键,打开属性,在弹出的窗口中有运行方式这一栏,选择最大化,然后确定就可以了。
怎么样?如果还是不行的话,你就用鼠标把窗口拖成最大,然后再关点IE,即重新启动IE。就可以了
posted @
2007-07-03 13:02 jadmin 阅读(71) |
评论 (0) |
编辑 收藏
RoR是Ruby on Rails的缩写。Ruby on Rails是一个用于编写网络应用程序的框架,它基于计算机软件语言Ruby,给程序开发人员提供强大的框架支持。Ruby on Rails包括两部分内容:Ruby语言和Rails框架。
什么是Ruby?
Ruby语言是一种动态语言,它与Python、Smalltalk和Perl这3种编程语言有些类似。Ruby语言起源于日本,它的研发者是日本人松本行弘(Matsumoto Yukihiro)。松本行弘在1993年开始着手Ruby语言的研发工作,他开发Ruby语言的初衷是为了提高编程的效率。1995年12月Matz推出了Ruby的第一个版本Ruby 0.95。
Ruby语言的主要特点如下。
1.纯的面向对象语言
在Ruby中,一切皆是对象。下面举一个例子来更直观地说明Ruby语言的这一特点。
在Java中,求一个数的绝对值的代码如下。
int c = Math.abs(-20);
而在Ruby语言中,一切皆是对象,也就是说“-20”这个数也是一个对象,因此,求一个数绝对值的Ruby代码形式如下。
c = -20.abs
这样的代码编写方式是不是更形象一些呢?
2.解释型脚本语言
Ruby语言是解释型脚本语言,它既有脚本语言强大的字符串处理能力和正则表达式,又不失解释型语言的动态性。一方面,在最初设计Ruby语言时,Ruby的研发者松本行弘考虑到文字处理方面的需要,他借鉴了Perl语言在文字处理方面的成功经验。另一方面,松本行弘将Ruby语言设定为一种解释型语言,Ruby的动态性使得由Ruby语言编写的程序不需要事先编译即可直接运行,这为程序的调试带来了方便。同时,这一特点可以实现开发过程中的快速反馈。
3.其他特点
(1)动态载入。可以在运行时候重定义自己,类也可以在运行时继承或取消继承。
(2)自动内存管理机制。
(3)多精度整数。
(4)迭代器和闭包。
(5)开源项目。有大量活跃的社区支持Ruby语言。
什么是Rails?
虽然Ruby语言有很多优点,但是一直以来,其流行的范围也仅限于日本。直到2004年,Ruby才逐渐被世界上其他地区的人们所认识,那么是什么让Ruby语言走向世界的呢?是Rails。
Rails框架首次提出是在2004年7月,它的研发者是26岁的丹麦人David Heinemeier Hansson。不同于已有复杂的Web 开发框架,Rails是一个更符合实际需要而且更高效的Web开发框架。Rails结合了PHP体系的优点(快速开发)和Java体系的优点(程序规整),因此,Rails在其提出后不长的时间里就受到了业内广泛的关注。
Rails框架主要有如下的6大特点。
1.全栈式的MVC框架
Rails是一个全栈式的MVC框架,换句话说,通过Rails可以实现MVC模式中的各个层次,并使它们无缝地协同运转起来。
在实际开发一个MVC模式的Web应用项目时,如果使用Java开发,需要用到Struts(Model层)、Hibernate(Controller层)和Spring(View层)3个框架,而且需要额外整合3个框架开发出的内容。而使用Ruby语言开发相同的项目时,只需要用到Rails框架就可以完成。
2.约定优于配置
为了说明各个对象之间的关联关系,一般的Web应用开发框架往往采用写入XML配置文件的方法。这种方式虽然可以解决一些问题,但是却带来了管理上的混乱。
Rails对此的态度是约定优于配置,这意味着在Rails中不会出现XML配置文件。Rails使用Web应用多年来积累的各种常见约定(更具体地说是命名规则)来代替XML配置文件,而在Rails内部的映射与发现机制根据这些约定可以实现对象之间的关联。在第1章中,通过Rails的映射与发现机制实现了数据表与Ruby对象之间的关联。
3.更少的代码
使用约定来代替XML配置文件说明Rails本身完成了大量的底层工作,这意味着使用更少的代码来实现应用程序是极有可能的。此外,代码量的缩减也减小了出现bug的可能性,降低了维护程序和升级程序的难度。
4.生成器
Rails使用的实时映射技术和元编程技术,免去了开发人员在开发过程中编写大量样板文件代码的烦恼。在少数需要使用样板文件代码的时候,开发人员可以通过Rails内建的生成器脚本实时创建,而不再是通过手工编写。Rails的这个特点可以使开发人员更专注于系统的逻辑结构,而不必为一些琐碎的细节所烦扰。
5.零周转时间
对已有的Web应用系统进行修改后,其一般需要经过配置、编译、发布、重新设置、测试等一系列步骤才能投入使用,这明显浪费了许多时间。而使用Rails开发Web应用系统,可以通过浏览器即时查看程序运行结果,从而节约了大量的时间。
6.支架系统
Rails的支架系统可以自动为任何相关的数据库表创建一套包含标准CRUD操作和前台视图的系统。通过支架系统,开发人员可以方便快捷地操纵数据库中的数据表。此外,Rails也允许开发人员使用自己设计的代码或视图来替换自动生成的代码和视图。
目前,Rails的最新版本是2005年12月13日发布的v1.0.0。从RoR正式提出到v1.0.0的发布,RoR在一年多的时间里受到了业内人士的广泛关注。RoR受到广泛关注主要有如下两个原因:首先,RoR的开发效率高(部署容易)、功能丰富(支持Ajax等流行应用),有消息称对于相同的Web开发项目,使用RoR开发比使用Java体系架构开发快5~10倍;此外,令人不可思议的高性能是其受到关注的另一个重要原因,根据CSDN上转载的新闻称使用RoR开发出来的项目性能,比基于Struts+Hibernate+Spring的Java应用还要高15%~20%。
RoR当前遇到的主要问题是使用RoR搭建的大型商业应用还很少,究其原因可以概括为两点:第一,从开发能力的角度,RoR是一个基于Ruby语言的轻型Web开发框架,很多开发者对其是否适合大型应用难以把握。第二,本身使用RoR开发的大型商业应用较少,使得后来者持观望态度。
综合分析来看,RoR的发展前景还是很光明的。RoR在短时间内取得了巨大的成就,它打破了Web开发领域的固有观念,方便快捷的开发方式使其被广泛接受。而事实上,现在已有几家跨国公司正在使用RoR开发自己的Web应用程序,并且有多家大型公司在考虑使用RoR进行Web应用开发。
ROR开发环境的配置见如下链接
http://hi.baidu.com/jadmin/blog/item/a0d7f2ef456aca34acafd584.html
posted @
2007-07-03 12:56 jadmin 阅读(77) |
评论 (0) |
编辑 收藏
一:理解多线程
多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。
线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。
多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。
多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题,将在以后探讨。
二:在Java中实现多线程
我们不妨设想,为了创建一个新的线程,我们需要做些什么?很显然,我们必须指明这个线程所要执行的代码,而这就是在Java中实现多线程我们所需要做的一切!
真是神奇!Java是如何做到这一点的?通过类!作为一个完全面向对象的语言,Java提供了类 java.lang.Thread 来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程,我们以后的讨论都将围绕这个类进行。
那么如何提供给 Java 我们要线程执行的代码呢?让我们来看一看 Thread 类。Thread 类最重要的方法是 run() ,它为Thread 类的方法 start() 所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只需要覆盖它!
方法一:继承 Thread 类,覆盖方法 run()
我们在创建的 Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。
下面是一个例子:
public class MyThread extends Thread {
int count= 1, number;
public MyThread(int num) {
number = num;
System.out.println("创建线程 " + number);
}
public void run() {
while(true) {
System.out.println("线程 " + number + ":计数 " + count);
if(++count== 6) return;
}
}
public static void main(String args[]) {
for(int i = 0; i < 5; i++) new MyThread(i+1).start();
}
}
这种方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread 类,这时如果我们又不想建立一个新的类,应该怎么办呢?
我们不妨来探索一种新的方法:我们不创建 Thread 类的子类,而是直接使用它,那么我们只能将我们的方法作为参数传递给 Thread 类的实例,有点类似回调函数。但是 Java 没有指针,我们只能传递一个包含这个方法的类的实例。那么如何限制这个类必须包含这一方法呢?当然是使用接口!(虽然抽象类也可满足,但是需要继承,而我们之所以要采用这种新方法,不就是为了避免继承带来的限制吗?)
Java 提供了接口 java.lang.Runnable 来支持这种方法。
方法二:实现 Runnable 接口
Runnable 接口只有一个方法 run(),我们声明自己的类实现 Runnable 接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。
但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread 类的实例,这一点通过 Thread 类的构造函数
public Thread(Runnable target);来实现。
下面是一个例子:
public class MyThread implements Runnable {
int count= 1, number;
public MyThread(int num) {
number = num;
System.out.println("创建线程 " + number);
}
public void run() {
while(true) {
System.out.println("线程 " + number + ":计数 " + count);
if(++count== 6) return;
}
}
public static void main(String args[]) {
for(int i = 0; i < 5; i++) new Thread(new MyThread(i+1)).start();
}
}
严格地说,创建 Thread 子类的实例也是可行的,但是必须注意的是,该子类必须没有覆盖 Thread 类的 run 方法,否则该线程执行的将是子类的 run 方法,而不是我
们用以实现Runnable 接口的类的 run 方法,对此大家不妨试验一下。
使用 Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,它的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的话,在大多数情况下也许还不如直接用多个类分别继承 Thread 来得紧凑。
综上所述,两种方法各有千秋,大家可以灵活运用。
下面让我们一起来研究一下多线程使用中的一些问题。
三:线程的四种状态
1. 新状态:线程已被创建但尚未执行(start() 尚未被调用)。
2. 可执行状态:线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该线程,从而使得它执行。
3. 死亡状态:正常情况下 run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。
4. 阻塞状态:线程不会被分配 CPU 时间,无法执行。
四:线程的优先级
线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得 CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间,优先级高的线程有更大的机会获得 CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。
你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。
五:线程的同步
由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问。
由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。
1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方
法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。
2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
synchronized(syncObject) {
//允许访问控制的代码
}
synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
六:线程的阻塞
为了解决对共享存储区的访问冲突,Java 引入了同步机制,现在让我们来考察多个线程对共享资源的访问,显然同步机制已经不够了,因为在任意时刻所要求的资源不一定已经准备好了被访问,反过来,同一时刻准备好了的资源也可能不止一个。为了解决这种情况下的访问控制问题,Java 引入了对阻塞机制的支持。
阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。
1. sleep() 方法:sleep() 允许 指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。
典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。
2. suspend() 和 resume() 方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。典型地,suspend() 和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。
3. yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。
4. wait() 和 notify() 方法:两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许 指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。
初看起来它们与 suspend() 和 resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。
上述的核心区别导致了一系列的细节上的区别。
首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。
其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。
wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block 和wakeup 原语(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。
关于 wait() 和 notify() 方法最后再说明两点:
第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。
第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。
谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。
以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() 和 notify() 方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。
七:守护线程
守护线程是一类特殊的线程,它和普通线程的区别在于它并不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止,反之,只要有一个非守护线程在运行,应用程序就不会终止。守护线程一般被用于在后台为其它线程提供服务。
可以通过调用方法 isDaemon() 来判断一个线程是否是守护线程,也可以调用方法 setDaemon() 来将一个线程设为守护线程。
八:线程组
线程组是一个 Java 特有的概念,在 Java 中,线程组是类ThreadGroup 的对象,每个线程都隶属于唯一一个线程组,这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。你可以通过调用包含 ThreadGroup 类型参数的 Thread 类构造函数来指定线程属的线程组,若没有指定,则线程缺省地隶属于名为 system 的系统线程组。
在 Java 中,除了预建的系统线程组外,所有线程组都必须显式创建。
在 Java 中,除系统线程组外的每个线程组又隶属于另一个线程组,你可以在创建线程组时指定其所隶属的线程组,若没有指定,则缺省地隶属于系统线程组。这样,所有线程组组成了一棵以系统线程组为根的树。
Java 允许我们对一个线程组中的所有线程同时进行操作,比如我们可以通过调用线程组的相应方法来设置其中所有线程的优先级,也可以启动或阻塞其中的所有线程。
Java 的线程组机制的另一个重要作用是线程安全。线程组机制允许我们通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处理,还可以通过线程组的分层结构来支持不对等安全措施的采用。Java 的 ThreadGroup 类提供了大量的方法来方便我们对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。
九:总结
在这一讲中,我们一起学习了 Java 多线程编程的方方面面,包括创建线程,以及对多个线程进行调度、管理。我们深刻认识到了多线程编程的复杂性,以及线程切换开销带来的多线程程序的低效性,这也促使我们认真地思考一个问题:我们是否需要多线程?何时需要多线程?
多线程的核心在于多个代码块并发执行,本质特点在于各代码块之间的代码是乱序执行的。我们的程序是否需要多线程,就是要看这是否也是它的内在特点。
假如我们的程序根本不要求多个代码块并发执行,那自然不需要使用多线程;假如我们的程序虽然要求多个代码块并发执行,但是却不要求乱序,则我们完全可以用一个循环来简单高效地实现,也不需要使用多线程;只有当它完全符合多线程的特点时,多线程机制对线程间通信和线程管理的强大支持才能有用武之地,这时使用多线程才是值得的。
posted @
2007-07-01 03:40 jadmin 阅读(75) |
评论 (0) |
编辑 收藏