??xml version="1.0" encoding="utf-8" standalone="yes"?> Commons ProperlgQ?/p> Commons SandboxlgQ?/p> Commons之字W串操作 以下是StringUtils的各用?br />1.I字W串?/strong> System.out.println( "test blank? " + StringUtils.isBlank( test ) ); System.out.println( "test1 trimToNull: " + StringUtils.trimToNull( test1 ) ); 输出如下: 注意Q函数StringUtils.trim(testString)?br />StringUtils.trimToNull(testString)功能cMQ但testStringq白字W?br />(whitespace)l成时返回零长度字符丌Ӏ?/p> System.out.println( StringUtils.abbreviate( test, 15 ) ); 4.劈分字符?/strong> String[] array1 = StringUtils.split( input, " ,.|"); 5.查找嵌套字符?/strong> 其他函数:StringUtils.center( testString, count,repeatString ); 9.判断字符串内容的cd 例程: 10.取得某字W串在另一字符串中出现的次?br />使用函数:StringUtils.countMatches(testString,seqString) 11.部分截取字符?/strong> 函数介绍Q上面应该都讲明白了吧?br />例程Q?br /> String formatted = " 25 * (30,40) [50,60] | 30";
]]>
Commons ProperQ提供了设计良好可重用的javalgQƈ都经q了q泛、严格的试?br />Commons SandboxQ处于实验、测试阶D늚lg?br />Commons DormantQ处于停滞状态,从Sandbox退出的Q不z跃的组Ӟ谨慎使用?
Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-org.apache.commons.beanutils.PropertyUtils;
copyProperties(a,b);//把相同类型b的属性赋值给a
Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-转蝲Qorg.apache.commons?/h4>
org.apache.commons包的下蝲面?
http://jakarta.apache.org/site/downloads/downloads_commons-lang.cgi
其中源码大家可以借鉴一下,我觉得很有参考h|其是有些函数在不用正则表达式下取得的效果?br />
取得commons-lang-2.1.jar后加入自己工E的lib目录可以了.如果用户不允怋用commonsQ那末打开其源码把具体函数加入自己的代码也可以Q当焉要尊重h家的知识产权?br />
以下代码l过试,试环境(WinXp+Eclipse3.1+JDK1.5+commons-lang-2.1)Q我在有些地方修改了一下?br />
Jakarta Commons Cookbook?1—Manipulating Text
要利用Jakarta Commons来进行字W串操作,首先需要加载需要用到的?
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
使用函数: StringUtils.isBlank(testString)
函数介绍: 当testString为空,长度为零或者仅q白字W?whitespace)l成?q回True;否则q回False
例程:
String test = "";
String test2 = "\n\n\t";
String test3 = null;
String test4 = "Test";
System.out.println( "test2 blank? " + StringUtils.isBlank( test2 ) );
System.out.println( "test3 blank? " + StringUtils.isBlank( test3 ) );
System.out.println( "test4 blank? " + StringUtils.isBlank( test4 ) );
输出如下:
test blank? true
test2 blank? true
test3 blank? true
test4 blank? False
函数StringUtils.isNotBlank(testString)的功能与StringUtils.isBlank(testString)相反.
2.清除I白字符
使用函数: StringUtils.trimToNull(testString)
函数介绍:清除掉testString首尾的空白字W?如果仅testString全由I白字符
(whitespace)l成则返回null
例程:
String test1 = "\t";
String test2 = " A Test ";
String test3 = null;
System.out.println( "test2 trimToNull: " + StringUtils.trimToNull( test2 ) );
System.out.println( "test3 trimToNull: " + StringUtils.trimToNull( test3 ) );
test1 trimToNull: null
test2 trimToNull: A Test
test3 trimToNull: null
3.取得字符串的~写
使用函数: StringUtils.abbreviate(testString,width)和StringUtils.abbreviate(testString,offsetQwidth)
函数介绍:在给定的width内取得testString的羃?当testString的长度小于width则返回原字符?
例程:
String test = "This is a test of the abbreviation.";
String test2 = "Test";
System.out.println( StringUtils.abbreviate( test, 5,15 ) );
System.out.println( StringUtils.abbreviate( test2, 10 ) );
输出如下:
This is a te...
...is a test...
Test
使用函数: StringUtils.split(testString,splitChars,arrayLength)
函数介绍:splitChars中可以包含一pd的字W串来劈分testString,q可以设定得
到数l的长度.注意讑֮长度arrayLength和劈分字W串间有抵触关系,一般情况下
不要讑֮长度.
例程:
String input = "A b,c.d|e";
String input2 = "Pharmacy, basketball funky";
String[] array2 = StringUtils.split( input2, " ,", 2 );
System.out.println( ArrayUtils.toString( array1 ) );
System.out.println( ArrayUtils.toString( array2 ) );
输出如下:
{A,b,c,d,e}
{Pharmacy,basketball funky}
使用函数:StringUtils.substringBetween(testString,header,tail)
函数介绍Q在testString中取得header和tail之间的字W串。不存在则返回空
例程Q?br /> String htmlContent = "ABC1234ABC4567";
System.out.println(StringUtils.substringBetween(htmlContent, "1234", "4567"));
System.out.println(StringUtils.substringBetween(htmlContent, "12345", "4567"));
输出如下Q?br /> ABC
null
6.去除N换行W?/strong>
使用函数:StringUtils.chomp(testString)
函数介绍:去除testStringN的换行符
例程:
String input = "Hello\n";
System.out.println( StringUtils.chomp( input ));
String input2 = "Another test\r\n";
System.out.println( StringUtils.chomp( input2 ));
输出如下:
Hello
Another test
7.重复字符?/strong>
使用函数:StringUtils.repeat(repeatString,count)
函数介绍:得到repeatString重复countơ后的字W串
例程:
System.out.println( StringUtils.repeat( "*", 10));
System.out.println( StringUtils.repeat( "China ", 5));
输出如下:
**********
China China China China China
函数介绍:把testString插入repeatString重复多次后的字符串中?得到字符?br />的总长为count
例程:
System.out.println( StringUtils.center( "China", 11,"*"));
输出如下:
***China***
8.颠倒字W串
使用函数:StringUtils.reverse(testString)
函数介绍:得到testString中字W颠倒后的字W串
例程:
System.out.println( StringUtils.reverse("ABCDE"));
输出如下:
EDCBA
函数介绍:
StringUtils.isNumeric( testString ) :如果testString全由数字l成q回True
StringUtils.isAlpha( testString ) :如果testString全由字母l成q回True
StringUtils.isAlphanumeric( testString ) :如果testString全由数字或数字组
成返回True
StringUtils.isAlphaspace( testString ) :如果testString全由字母或空格组
成返回True
String state = "Virginia";
System.out.println( "Is state number? " + StringUtils.isNumeric(
state ) );
System.out.println( "Is state alpha? " + StringUtils.isAlpha( state )
);
System.out.println( "Is state alphanumeric? " +StringUtils.isAlphanumeric( state ) );
System.out.println( "Is state alphaspace? " + StringUtils.isAlphaSpace( state ) );
输出如下:
Is state number? false
Is state alpha? true
Is state alphanumeric? true
Is state alphaspace? true
函数介绍:取得seqString在testString中出现的ơ数,未发现则q回?br />例程:
System.out.println(StringUtils.countMatches( "Chinese People", "e"
));
输出:
4
使用函数:
StringUtils.substringBetween(testString,fromString,toString ):取得两字W?br />之间的字W串
StringUtils.substringAfter( ):取得指定字符串后的字W串
StringUtils.substringBefore( )Q取得指定字W串之前的字W串
StringUtils.substringBeforeLast( )Q取得最后一个指定字W串之前的字W串
StringUtils.substringAfterLast( )Q取得最后一个指定字W串之后的字W串
System.out.print("N0: " + StringUtils.substringBeforeLast( formatted, "*" ) );
System.out.print(", N1: " + StringUtils.substringBetween( formatted, "(", "," ) );
System.out.print(", N2: " + StringUtils.substringBetween( formatted, ",", ")" ) );
System.out.print(", N3: " + StringUtils.substringBetween( formatted, "[", "," ) );
System.out.print(", N4: " + StringUtils.substringBetween( formatted, ",", "]" ) );
System.out.print(", N5: " + StringUtils.substringAfterLast( formatted, "|" ) );
输出如下Q?br /> N0: 25 , N1: 30, N2: 40, N3: 50, N4: 40) [50,60, N5: 30
]]>
Jakarta POI
?span lang="EN-US">apache的子目Q目标是处理ole2对象。它提供了一l操U?span lang="EN-US">Windows文档?span lang="EN-US">Java API
目前比较成熟的是HSSF接口Q处?span lang="EN-US">MS ExcelQ?span lang="EN-US">97-2002Q对象。它不象我们仅仅是用csv生成的没有格式的可以?span lang="EN-US">Excel转换的东西,而是真正?span lang="EN-US">Excel对象Q你可以控制一些属性如sheet,cell{等?span lang="EN-US">
二.HSSF概况
HSSF
?span lang="EN-US">Horrible SpreadSheet Format的羃写,也即“讨厌的电子表格格式”?也许HSSF的名字有ҎE,本质而言它是一个非怸肃、正规的API。通过HSSFQ你可以用纯Java代码来读取、写入、修?span lang="EN-US">Excel文g?span lang="EN-US">
HSSF
取操作提供了两类APIQ?span lang="EN-US">usermodel?span lang="EN-US">eventusermodelQ即“用h型”和“事?span lang="EN-US">-用户模型”。前者很好理解,后者比较抽象,但操作效率要高得多?/span>
三.开始编?span lang="EN-US">
1
Q?/span>
准备工作
要求:JDK 1.4+POI开发包
可以?/span>
http://www.apache.org/dyn/closer.cgi/jakarta/poi/
最新的POI工具?span lang="EN-US">
2
Q?/span>
EXCEL
l构
HSSFWorkbook excell
文档对象介绍
HSSFSheet excell的表?span lang="EN-US">
HSSFRow excell的行
HSSFCell excell的格子单?span lang="EN-US">
HSSFFont excell字体
HSSFName 名称
HSSFDataFormat 日期格式
?span lang="EN-US">poi1.7中才有以?span lang="EN-US">2:
HSSFHeader sheet?span lang="EN-US">
HSSFFooter sheet?span lang="EN-US">
和这个样?span lang="EN-US">
HSSFCellStyle cell样式
辅助操作包括
HSSFDateUtil 日期
HSSFPrintSetup 打印
HSSFErrorConstants 错误信息?/span>
3
Q具体用法实?/span>
Q采?/span>
usermodel
Q?/span>
如何?span lang="EN-US">Excel
dExcel文gӞ首先生成一?span lang="EN-US">POIFSFileSystem对象Q由POIFSFileSystem对象构造一?span lang="EN-US">HSSFWorkbookQ该HSSFWorkbook对象׃表了Excel文档。下面代码读取上面生成的Excel文g写入的消息字Ԍ
try{
POIFSFileSystem fs=new POIFSFileSystem(new FileInputStream("d:/workbook.xls"));
HSSFWorkbook wb = new HSSFWorkbook(fs);
HSSFSheet sheet = wb.getSheetAt(0);
HSSFRow row = sheet.getRow(0);
HSSFCell cell = row.getCell((short) 0);
String msg = cell.getStringCellValue();
System.out.println(msg);
}catch(Exception e){
e.printStackTrace();
}
如何?span lang="EN-US">excelQ?span lang="EN-US">
?span lang="EN-US">excel的第一个表单第一行的W一个单元格的值写成?span lang="EN-US">a test”?span lang="EN-US">
POIFSFileSystem fs =new POIFSFileSystem(new FileInputStream("workbook.xls"));
HSSFWorkbook wb = new HSSFWorkbook(fs);
HSSFSheet sheet = wb.getSheetAt(0);
HSSFRow row = sheet.getRow(0);
HSSFCell cell = row.getCell((short)0);
cell.setCellValue("a test");
// Write the output to a file
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
4
Q?/span>
可参考文?/span>
POI
主页Q?span lang="EN-US">http://jakarta.apache.org/poi/Q?span lang="EN-US">
初学者如何快速上手?span lang="EN-US">POI HSSF
http://jakarta.apache.org/poi/hssf/quick-guide.html
?span lang="EN-US">
里面有很多例子代码,可以很方便上手?span lang="EN-US">
5、中文ؕ码问?br />
//把List expItems,写到EXCEL文g?br /> FileOutputStream fileOut = new FileOutputStream(filePath);
HSSFWorkbook wb = new HSSFWorkbook();// HSSFWorkbook
HSSFSheet sheet = wb.createSheet();//
wb.setSheetName(0,“实验项目列表-Q中文测试?HSSFWorkbook.ENCODING_UTF_16);
HSSFRow row = sheet.createRow((short) 0);
HSSFCell cell;
Iterator iter = expItems.iterator();
int i = 0;
while (iter.hasNext()) {
cell = row.createCell((short) i);
cell.setEncoding(HSSFCell.ENCODING_UTF_16);
cell.setCellValue(iter.next().toString());
i++;
}
wb.write(fileOut);
fileOut.close();
}
四.使用心得
POI HSSF
?span lang="EN-US">usermodel包把Excel文g映射成我们熟悉的l构Q诸?span lang="EN-US">Workbook?span lang="EN-US">Sheet?span lang="EN-US">Row?span lang="EN-US">Cell{,它把整个l构以一l对象的形式保存在内存之中,便于理解Q操作方便,基本上能够满x们的要求Q所以说q个一个不错的选择?/span>
Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?br />前面已经讲过利用POIdExcelQ下面是一个用POI向Excel中插入图片的例子?br />
官方文档Q?
Images are part of the drawing support. To add an image just call createPicture() on the drawing patriarch. At the time of writing the following types are supported:
PNG
JPG
DIB
It is not currently possible to read existing images and it should be noted that any existing drawings may be erased once you add a image to a sheet.
// Create the drawing patriarch. This is the top level container for
// all shapes. This will clear out any existing shapes for that sheet.
通过HSSFPatriarchccreatePictureҎ的在指定的wb中的sheet创徏囄,它接受二个参敎ͼW一个是HSSFClientAnchorQ设定图片的大小?br />
package com.poi.hssf.test;
import java.io.FileOutputStream;
import java.io.File;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.*;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFPatriarch;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;;
public class TestPOI {
public static void main(String[] args) {
FileOutputStream fileOut = null;
BufferedImage bufferImg =null;
BufferedImage bufferImg1 = null;
try{
//先把读进来的囄攑ֈ一个ByteArrayOutputStream中,以便产生ByteArray
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
ByteArrayOutputStream byteArrayOut1 = new ByteArrayOutputStream();
bufferImg = ImageIO.read(new File("d:/PieChart.jpg"));
bufferImg1 = ImageIO.read(new File("d:/fruitBarChart.jpg"));
ImageIO.write(bufferImg,"jpg",byteArrayOut);
ImageIO.write(bufferImg1,"jpg",byteArrayOut1);
//创徏一个工作薄
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet("new sheet");
//HSSFRow row = sheet1.createRow(2);
HSSFPatriarch patriarch = sheet1.createDrawingPatriarch();
HSSFClientAnchor anchor = new HSSFClientAnchor(0,0,512,255,(short) 1,1,(short)10,20);
HSSFClientAnchor anchor1 = new HSSFClientAnchor(0,0,512,255,(short) 2,30,(short)10,60);
anchor1.setAnchorType(2);
//插入囄
patriarch.createPicture(anchor , wb.addPicture(byteArrayOut.toByteArray(),HSSFWorkbook.PICTURE_TYPE_JPEG));
patriarch.createPicture(anchor1 , wb.addPicture(byteArrayOut1.toByteArray(),HSSFWorkbook.PICTURE_TYPE_JPEG));
fileOut = new FileOutputStream("d:/workbook.xls");
//写入excel文g
wb.write(fileOut);
fileOut.close();
}catch(IOException io){
io.printStackTrace();
System.out.println("io erorr : "+ io.getMessage());
} finally
{
if (fileOut != null)
{
try {
fileOut.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
1.下蝲
FCKeditor.java 2.3 (FCKeditot for java)
FCKeditor 2.2 (FCKeditor基本文g)
2.建立目:tomcat/webapps/FCKeditor
3.FCKeditor.java 2.3解压后,把其中的web目录下的WEB-INF目录copy到FCKeditor?里面有commons-fileupload.jar, FCKeditor-2.3.jar,web.xml{几个文?, 把其中的src目录下的FCKeditor.tld文gcopy到FCKeitor/WEB-INF/?
4.修改web.xml:
把SimpleUploader中的配置属性enabled定义为true(开启文件上传功能)
d标签定义:
<taglib>
<taglib-uri>/FCKeditor</taglib-uri>
<taglib-location>/WEB-INF/FCKeditor.tld</taglib-location>
</taglib>
5.解压FCKeditor2.2后,把目?editor和fckconfig.js, fckeditor.js, fckstyles.xml, fcktemplates.xml四个文gcopy?FCKeditor?
删除目录/editor/_source,
删除/editor/filemanager/browser/default/connectors/下的所有文?
删除/editor/filemanager/upload/下的所有文?
删除/editor/lang/下的除了fcklanguagemanager.js, en.js, zh.js, zh-cn.js四个文g的所有文?
6.打开/FCKeditor/fckconfig.js
修改 FCKConfig.DefaultLanguage = 'zh-cn' ;
把FCKConfig.LinkBrowserURL{的值替换成以下内容Q?
FCKConfig.LinkBrowserURL =
FCKConfig.BasePath + "filemanager/browser/default/browser.html?Connector=connectors/jsp/connector" ;
FCKConfig.ImageBrowserURL =
FCKConfig.BasePath + "filemanager/browser/default/browser.html?Type=Image&Connector=connectors/jsp/connector" ;
FCKConfig.FlashBrowserURL =
FCKConfig.BasePath + "filemanager/browser/default/browser.html?Type=Flash&Connector=connectors/jsp/connector" ;
FCKConfig.LinkUploadURL = FCKConfig.BasePath + 'filemanager/upload/simpleuploader?Type=File' ;
FCKConfig.FlashUploadURL = FCKConfig.BasePath + 'filemanager/upload/simpleuploader?Type=Flash' ;
FCKConfig.ImageUploadURL = FCKConfig.BasePath + 'filemanager/upload/simpleuploader?Type=Image' ;
7.d文g /FCKeditor/test.jsp:
<%@ page language="java" import="com.fredck.FCKeditor.*" %>
<%@ taglib uri="/FCKeditor" prefix="FCK" %>
<script type="text/javascript" src="/FCKeditor/fckeditor.js"></script>
<%--
三种Ҏ调用FCKeditor
1.FCKeditor自定义标{?(必须加头文g <%@ taglib uri="/FCKeditor" prefix="FCK" %> )
2.script脚本语言调用 (必须引用 脚本文g <script type="text/javascript" src="/FCKeditor/fckeditor.js"></script> )
3.FCKeditor API 调用 (必须加头文g <%@ page language="java" import="com.fredck.FCKeditor.*" %> )
--%>
<%--
<form action="show.jsp" method="post" target="_blank">
<FCK:editor id="content" basePath="/FCKeditor/" width="700" height="500" skinPath="/FCKeditor/editor/skins/silver/"
toolbarSet = "Default" >
input
</FCK:editor>
<input type="submit" value="Submit">
</form>
--%>
<form action="show.jsp" method="post" target="_blank">
<table border="0" width="700"><tr><td>
<textarea id="content" name="content" style="WIDTH: 100%; HEIGHT: 400px">input</textarea>
<script type="text/javascript">
var oFCKeditor = new FCKeditor('content') ;
oFCKeditor.BasePath = "/FCKeditor/" ;
oFCKeditor.Height = 400;
oFCKeditor.ToolbarSet = "Default" ;
oFCKeditor.ReplaceTextarea();
</script>
<input type="submit" value="Submit">
</td></tr></table>
</form>
<%--
<form action="show.jsp" method="post" target="_blank">
<%
FCKeditor oFCKeditor ;
oFCKeditor = new FCKeditor( request, "content" ) ;
oFCKeditor.setBasePath( "/FCKeditor/" ) ;
oFCKeditor.setValue( "input" );
out.println( oFCKeditor.create() ) ;
%>
<br>
<input type="submit" value="Submit">
</form>
--%>
d文g/FCKeditor/show.jsp:
<%
String content = request.getParameter("content");
out.print(content);
%>
8.览 http://localhost:8080/FCKeditor/test.jsp
ok!
9.上传遇到错误: internal server error 500,
直接引用servlet(com.fredck.FCKeditor.connector.ConnectorServlet)也遇到错? "Provider org.apache.xalan.processor.TransformerFactoryImpl not found",
拯xalan.jar到lib目录可以了
?000q的时候,我的个h|站“爱情工作室”就是用我自己的写的论坛,当时是用PHPQ功能上比较单,主要是ؓ了网站用,后来我想把社坛单独拿出来Qƈ且爱情工作室|站也只剩下了社一部分Q所以就开始用PHP按照|易Cl构写了BBS-CS PHP 1.0的版本,后来q个版本一直没有更斎ͼ但JSP版BBS-CS 1.0?.0的数据结构都是从PHP版本发展来的Q?000q底我开始用JAVAQ?001q就写了BBS-CS1.0Q后来出?.0Q?.0?.0的安装都不是很方便,q且SQL都是在JSP中,l构不好Q?002q我写了BBS-CS3.0Q此时数据库操作已经用JavaBean来封装,但是3.0的性能非常不好Q?003q初我写?.0Q?.0的结构已l发生了完全的变化,主要是适应多种数据库和数据库访问均衡处理,性能大大增强Qƈ且BBS-CS定名为“天乙社区”,今年下半q我写了5.0Q?.0采用了Struts的框Ӟ国际化内核,同时支持集群q行Q全文检索等{?
有h可能会拿BBS-CS和Jive来比较,觉得BBS-CS是小儿科Q但我想BBS-CS的设计模式虽然没有Jive那么复杂Q但是在国际化、实用性、性能{方面ƈ不差?
一、数据库设计
BBS-CS?.0开始,数据库结构发生了Ҏ的变化,主要是对数据表作了负载均衡的处理Q即一个功能不是用一张表Q而是使用多张同样l构的表Q比如存攑ָ子的表有20张,q样做的目的是ؓ了将数据库的查询压力分散C同表中,其实道理很简单,如果一张表中存?00万条记录Q和100张表Q每个表存放1万条记录Q查询效率是完全不一L。具体程序中Q比如一个版区的帖子往那个表中插,是由法来实现的Q算法会保证某个版区的帖子只会往某一个表中插入?
二、分处?
BBS-CS4.0?.0都是支持多数据库的,现在支持的数据库有Mysql、Oracle和SQL ServerQ在分页处理上确实要׃些功夫,以前我看q很多关于分늚技术资料,处理分页的方法主要是有几个:1、用JDBC的本w特性,数据全部取出,然后Ҏ늠Q每记录数Q对查询出的数据集的数据滚动Q这个方法比较简单、通用Q但是性能极差Q如果一个百万的数据表Q将耗费巨大的系l资源,甚至宕机Q?、和W一U比较类|但不是查出所有数据,只查出数据的主键Q对主键滚动Q然后根据主键再ơ查扄对应的数据,产生集合Q这U方法也是EJB中实体Bean查询数据的方法,但依然存在性能问题Q因为数据过多的时候主键数据集也很大,而来用主键插找数据的是后又进行了多次数据库的执行操作Q性能降低。在多种分页Ҏ比较之后QBBS-CSq是军_采用数据自nҎ的SQL语句来进行分,比如Mysql使用limitQOracle使用rownumQSQL Server使用TopQ分늱是一个抽象类Q每U数据库的分实现方法都ȝ承这个抽象类Q然后用一个工厂方法根据系l配|文件来产生分页cȝ实例Q采用这L分页ҎQ能都达到最佳的分页查询效率?
三、系l配|文?
BBS-CS5.0的系l配|文件用了一个XML文gQ在4.0的时候,pȝ的配|文件是采用properties文g来解冻I后来我觉得properties的文件还是有很大的局限性,比如Q不方便q行有数据库l构的数据进行配|,后来觉得XML文g是一个绝佳的ҎQ难怪现在很多品的配置文g都是XML?)Q对于XML文g的解析,BBS-CS采用了jdomQjdom是java+domQ详l资?a >http://www.jdom.orgQ,一个很方便的处理XML的Y件包Q在BBS-CSU对jdom的运用也不复杂,主要是用来解析bbscs.xmlq个配置文g。系l解析bbscs.xml文g后,这些系l信息保存在静态变量中Q以供系l其他程序随时调用?
四、国际化与资源文?
JAVA本n是国际化的内部采用了Unicode的编码格式,而Struts的框Ӟ更方便了国际化的应用QStruts会根据浏览器的LocaleQ自动调取相应的properties资源文gQ在BBS-CS是定义的app.peoperties文gQBBS-CS只定义了中文的资源包Q即app_zh_CN.properties文gQ当然只要将q个文g译成日文、韩文等{其他国家文字,卛_实现BBS-CS多语a览。当然只有浏览多语言是不行,数据的保存也必须是通用的格式,BBS-CS采用了UTF-8的编码格式的保存数据Q以解决多语a的问题,关于Unicode、UTF-8~码格式的资料、原理大家可以在|上查找资料Q我在这里不多做讲解了?
五、编码问?
在(四)中讲了国际化问题Q但是如果不作处理,对中文显C处理都会有问题Q要对app_zh_CN.propertieq行native2ascii的{换,q个命o是在jdk/bin下的Q之后我们要做一个Servlet的过滤器Q对所有的request对象q行~码的{换,com.laoer.bbscs.servlet.EncodingFilter是BBS-CS的过滤器Q它对request的编码格式进行了处理Q定义ؓ了UTF-8的编码格式。以前网上在看到q的一些文章上_采用Servletqo器会有性能的下降,在我使用的感觉上来说Q问题倒不是很大?
六、集与Session处理
BBS-CS5.0设计的时候考虑C集群的应用,因ؓ随着pȝ讉K量和数据的不断增长,单台服务器是很难支撑的,所以要求系l能够用多台服务器来q行Q跨服务器的Session是一个很大的问题Q大安知道Session是生存与一个应用中的,跨应用,Session必然不能׃nQ现在JSP容器Q包括Weblogic、Resin、Tomcat都可以进行集应用,但是配制h可能比较复杂Q所以BBS-CS实现了自q集群q用Ҏ。实现的原理比较单,主要是涉及了|络~程Q在JAVA中,对象只要implements了java.io.Serializableq个接口Q即可实C行化Q便可以在网l中传输QBBS-CS是把需要在|络中传输的Session对象q行串行化,在多个服务器之间q行通讯Q以辑ֈ服务器之间的Session同步Q同时对于几台服务器的访问是负蝲均衡的?
Session实现QBBS-CS在单服务器配制下是采用了应用的SessionQ而在机群方式下,是徏立了自己的Session列表QSession是一个静态的HashTableQ每个用户在登陆的时候会产生一个随机的sidQ这个sid是HashTable的keyQ而value则是一个HasMapQ保存需要的对象。系l在q行期间会根据配|文件的Session时旉定时对超时的Sessionq行清理?
七、静态变量与U程q用
Z减少Ҏ据库的访问,pȝ很多地方使用了静态变量来储存数据Q比如,版区列表和信息是pȝ需要频J访问的Q如果每ơ都从数据库中读取,消耗是很大的,BBS-CS采用的方法是Q一ơ性将数据库读出,攑֜一个静态的列表里(HashTableQ,以后再访问则从静态列表中取,而不是从数据库中取,如果版区信息发生变化Q只要刷新这个静态变量就行了?
通常在B/Sl构下,pȝ的定时操作是比较困难的,因ؓB/Sl构的Y件是览器事仉动的模型Q而不是服务器事g驱动Q在Unix/Linux下,通常是写一个脚本,然后用Crontab来跑Q在win下可能就要用VB/VC/Delphi来写一个定时程序了。BBS-CS必须要做到跨品台和通用性,而线E和Sevlet提供很好的方法,一个web应用Q可以在启动的时候按序的执行ServletE序Q在web.xml文g中定义)Q在Servlet中就可以启动一个线E,q个U程定时执行、休眠,便可以达到定时运行程序的目的Q在BBS-CS中,清除时Session、游客等{都是采用了U程定时执行的方式?/p>
建立索引
ZҎ档进行烦引,Lucene 提供了五个基的类Q他们分别是 Document, Field, IndexWriter, Analyzer, Directory。下面我们分别介l一下这五个cȝ用途:
Document
Document 是用来描q文档的Q这里的文档可以指一?HTML 面Q一电子邮Ӟ或者是一个文本文件。一?Document 对象由多?Field 对象l成的。可以把一?Document 对象惌成数据库中的一个记录,而每?Field 对象是记录的一个字Dc?
Field
Field 对象是用来描qC个文档的某个属性的Q比如一电子邮件的标题和内容可以用两个 Field 对象分别描述?
Analyzer
? 一个文档被索引之前Q首先需要对文档内容q行分词处理Q这部分工作是?Analyzer 来做的。Analyzer cL一个抽象类Q它有多个实现。针对不同的语言和应用需要选择适合?Analyzer。Analyzer 把分词后的内容交l? IndexWriter 来徏立烦引?
IndexWriter
IndexWriter ?Lucene 用来创徏索引的一个核心的c,他的作用是把一个个?Document 对象加到索引中来?
Directory
q个cM表了 Lucene 的烦引的存储的位|,q是一个抽象类Q它目前有两个实玎ͼW一个是 FSDirectoryQ它表示一个存储在文gpȝ中的索引的位|。第二个?RAMDirectoryQ它表示一个存储在内存当中的烦引的位置?
熟悉了徏立烦引所需要的q些cdQ我们就开始对某个目录下面的文本文件徏立烦引了Q清?l出了对某个目录下的文本文g建立索引的源代码?
清单 1. Ҏ本文件徏立烦?
package TestLucene;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.util.Date;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
/**
* This class demonstrate the process of creating index with Lucene
* for text files
*/
public class TxtFileIndexer {
public static void main(String[] args) throws Exception{
//indexDir is the directory that hosts Lucene's index files
File indexDir = new File("D:\\luceneIndex");
//dataDir is the directory that hosts the text files that to be indexed
File dataDir = new File("D:\\luceneData");
Analyzer luceneAnalyzer = new StandardAnalyzer();
File[] dataFiles = dataDir.listFiles();
IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
long startTime = new Date().getTime();
for(int i = 0; i < dataFiles.length; i++){
if(dataFiles[i].isFile() && dataFiles[i].getName().endsWith(".txt")){
System.out.println(
"Indexing file " + dataFiles[i].getCanonicalPath());
Document document = new Document();
Reader txtReader = new FileReader(dataFiles[i]);
document.add(
Field.Text("path",dataFiles[i].getCanonicalPath()));
document.add(Field.Text("contents",txtReader));
indexWriter.addDocument(document);
}
}
indexWriter.optimize();
indexWriter.close();
long endTime = new Date().getTime();
System.out.println("It takes " + (endTime - startTime)
+ " milliseconds to create index for
the files in directory "
+ dataDir.getPath());
}
}
? 清单1中,我们注意到类 IndexWriter 的构造函数需要三个参敎ͼW一个参数指定了所创徏的烦引要存放的位|,他可以是一?File 对象Q也可以是一?FSDirectory 对象或?RAMDirectory 对象。第二个参数指定?Analyzer cȝ一个实玎ͼ也就是指定这个烦引是用哪个分词器Ҏ挡内容进行分词。第三个参数是一个布型的变量,如果?true 的话׃表创Z个新的烦引,?false 的话׃表在原来索引的基上进行操作。接着E序遍历了目录下面的所有文本文档,qؓ每一个文本文档创Z一?Document 对象。然后把文本文档的两个属性:路径和内容加入到了两?Field 对象中,接着在把q两?Field 对象加入?Document 对象中,最后把q个文档?IndexWriter cȝ add Ҏ加入到烦引中厅R这h们便完成了烦引的创徏。接下来我们q入在徏立好的烦引上q行搜烦的部分?
搜烦文档
? 用Luceneq行搜烦像建立索引一样也是非常方便的。在上面一部分中,我们已经Z个目录下的文本文档徏立好了烦引,现在我们p在这个烦引上q行? 索以扑ֈ包含某个关键词或短语的文档。Lucene提供了几个基的类来完成这个过E,它们分别是呢IndexSearcher, Term, Query, TermQuery, Hits. 下面我们分别介绍q几个类的功能?
Query
q是一个抽象类Q他有多个实玎ͼ比如TermQuery, BooleanQuery, PrefixQuery. q个cȝ目的是把用户输入的查询字W串装成Lucene能够识别的Query?
Term
Term 是搜索的基本单位Q一个Term对象有两个Stringcd的域l成。生成一个Term对象可以有如下一条语句来完成QTerm term = new Term(“fieldName?”queryWord?; 其中W一个参C表了要在文档的哪一个Field上进行查找,W二个参C表了要查询的关键词?
TermQuery
TermQuery 是抽象类Query的一个子c,它同时也是Lucene支持的最为基本的一个查询类。生成一个TermQuery对象由如下语句完成: TermQuery termQuery = new TermQuery(new Term(“fieldName?”queryWord?); 它的构造函数只接受一个参敎ͼ那就是一个Term对象?
IndexSearcher
IndexSearcher是用来在建立好的索引上进行搜索的。它只能以只ȝ方式打开一个烦引,所以可以有多个IndexSearcher的实例在一个烦引上q行操作?
Hits
Hits是用来保存搜索的l果的?
介绍完这些搜索所必须的类之后Q我们就开始在之前所建立的烦引上q行搜烦了,清单2l出了完成搜索功能所需要的代码?
清单2 Q在建立好的索引上进行搜?
package TestLucene;
import java.io.File;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.FSDirectory;
/**
* This class is used to demonstrate the
* process of searching on an existing
* Lucene index
*
*/
public class TxtFileSearcher {
public static void main(String[] args) throws Exception{
String queryStr = "lucene";
//This is the directory that hosts the Lucene index
File indexDir = new File("D:\\luceneIndex");
FSDirectory directory = FSDirectory.getDirectory(indexDir,false);
IndexSearcher searcher = new IndexSearcher(directory);
if(!indexDir.exists()){
System.out.println("The Lucene index is not exist");
return;
}
Term term = new Term("contents",queryStr.toLowerCase());
TermQuery luceneQuery = new TermQuery(term);
Hits hits = searcher.search(luceneQuery);
for(int i = 0; i < hits.length(); i++){
Document document = hits.doc(i);
System.out.println("File: " + document.get("path"));
}
}
}
?
清单2中,cIndexSearcher的构造函数接受一个类型ؓDirectory的对象,Directory是一个抽象类Q它目前有两个子c:
FSDirctory和RAMDirectory.
我们的程序中传入了一个FSDirctory对象作ؓ其参敎ͼ代表了一个存储在盘上的索引的位|。构造函数执行完成后Q代表了q个
IndexSearcher以只ȝ方式打开了一个烦引。然后我们程序构造了一个Term对象Q通过q个Term对象Q我们指定了要在文档的内容中搜烦?
含关键词”lucene”的文档。接着利用q个Term对象构造出TermQuery对象q把q个TermQuery对象传入?
IndexSearcher的searchҎ中进行查询,q回的结果保存在Hits对象中。最后我们用了一个@环语句把搜烦到的文档的\径都打印了出
来。好了,我们的搜索应用程序已l开发完毕,怎么P利用Lucene开发搜索应用程序是不是很简单?
Ҏ 切词 索引 存储 用?
Field.Text(String name, String value) Yes Yes Yes 切分词烦引ƈ存储Q比如:标题Q内容字D?
Field.Text(String name, Reader value) Yes Yes No 切分词烦引不存储Q比如:META信息Q?
不用于返回显C,但需要进行检索内?
Field.Keyword(String name, String value) No Yes Yes 不切分烦引ƈ存储Q比如:日期字段
Field.UnIndexed(String name, String value) No No Yes 不烦引,只存储,比如Q文件\?
Field.UnStored(String name, String value) Yes Yes No 只全文烦引,不存?
好好看看q篇文章Q很不错?
----------------------------------------------------------------------------------------
Q李宇翻译,来自Lucene的帮助文档)
l论
Lucene提供了方便您创徏自徏查询的APIQ也通过QueryParser提供了强大的查询语言?
本文讲述Lucene的查询语句解析器支持的语法,Lucene的查询语句解析器是用JavaCC工具生成的词法解析器Q它查询字串解析ؓLucene Query对象?
(TermQ?
一条搜索语句被拆分Z些项QtermQ和操作W(operatorQ。项有两U类型:单独和短语?
单独就是一个单独的单词Q例?test" Q?"hello"?
短语是一l被双引号包围的单词Q例?hello dolly"?
多个可以用布尔操作W连接v来Ş成复杂的查询语句Q接下来您就会看刎ͼ?
注意QAnalyzer建立索引时用的解析器和解析单独和短语时的解析器相同,因此选择一个不会受查询语句q扰的Analyzer非常重要?
域(FieldQ?
Lucene支持域。您可以指定在某一个域中搜索,或者就使用默认域。域名及默认域是具体索引器实现决定的?
您可以这h索域Q域?":"+搜烦的项名?
举个例子Q假设某一个Lucene索引包含两个域,title和textQtext是默认域。如果您x找标题ؓ"The Right Way"且含?don't go this way"的文章,您可以输入:
title:"The Right Way" AND text:go
或?
title:"Do it right" AND right
因ؓtext是默认域Q所以这个域名可以不行?
注意Q域名只对紧接于其后的项生效Q所?
title:Do it right
只有"Do"属于title域?it"?right"仍将在默认域中搜索(q里是text域)?
修饰符QTerm ModifiersQ?
Lucene支持修饰符以支持更宽范围的搜烦选项?
用通配W搜?
Lucene支持单个与多个字W的通配搜烦?
使用W号"?"表示单个L字符的通配?
使用W号"*"表示多个L字符的通配?
单个L字符匚w的是所有可能单个字W。例如,搜烦"text或?test"Q可以这P
te?t
多个L字符匚w的是0个及更多个可能字W。例如,搜烦test, tests 或?testerQ可以这P
test*
您也可以在字W窜中间使用多个L字符通配W?
te*t
注意Q您不能在搜索的开始?或?W号?
模糊查询
Lucene支持ZLevenshtein Distance与Edit Distance法的模p搜索。要使用模糊搜烦只需要在单独的最后加上符?~"。例如搜索拼写类g"roam"的项q样写:
roam~
q次搜烦找到Ş如foam和roams的单词?
注意Q用模p查询将自动得到增量因子Qboost factorQؓ0.2的搜索结?
邻近搜烦(Proximity Searches)
Luceneq支持查扄隔一定距ȝ单词。邻q搜索是在短语最后加上符?~"。例如在文档中搜索相?0个单词的"apache"?jakarta"Q这样写Q?
"jakarta apache"~10
Boosting a Term
Lucene provides the relevance level of matching documents based on the terms found. To boost a term use the caret, "^", symbol with a boost factor (a number) at the end of the term you are searching. The higher the boost factor, the more relevant the term will be.
Lucene可以讄在搜索时匚w的怼度。在的最后加上符?^"紧接一个数字(增量|Q表C搜索时的相似度。增量D高,搜烦到的相兛_好?
Boosting allows you to control the relevance of a document by boosting its term. For example, if you are searching for jakarta apache and you want the term "jakarta" to be more relevant boost it using the ^ symbol along with the boost factor next to the term. You would type:
通过增量一个项可以控制搜烦文档时的相关度。例如如果您要搜索jakarta apacheQ同时您惌"jakarta"的相兛_更加好,那么在其后加?^"W号和增量|也就是您输入Q?
jakarta^4 apache
This will make documents with the term jakarta appear more relevant. You can also boost Phrase Terms as in the example:
q将使得生成的doucment可能与jakarta相关度高。您也可以增量短语,象以下这个例子一P
"jakarta apache"^4 "jakarta lucene"
By default, the boost factor is 1. Although, the boost factor must be positive, it can be less than 1 (i.e. .2)
默认情况下,增量值是1。增量g可以于1Q例?.2Q,但必L有效的?
布尔操作W?
布尔操作W可项通过逻辑操作q接h。Lucene支持AND, "+", OR, NOT ?"-"q些操作W。(注意Q布操作符必须全部大写Q?
OR
OR操作W是默认的连接操作符。这意味着如果两个之间没有布操作符Q就是用OR操作W。OR操作W连接两个项Q意味着查找含有L的文档。这与集合ƈq算相同。符号||可以代替W号OR?
搜烦含有"jakarta apache" 或?"jakarta"的文档,可以使用q样的查询:
"jakarta apache" jakarta
或?
"jakarta apache" OR jakarta
AND
AND操作W匹配的是两同时出现的文档。这个与集合交操作相{。符?amp;&可以代替W号AND?
搜烦同时含有"jakarta apache" ?"jakarta lucene"的文档,使用查询Q?
"jakarta apache" AND "jakarta lucene"
+
"+"操作W或者称为存在操作符Q要求符?+"后的必d文档相应的域中存在?
搜烦必须含有"jakarta"Q可能含?lucene"的文档,使用查询Q?
+jakarta apache
NOT
NOT操作W排除那些含有NOTW号后面的文档。这和集合的差运相同。符P可以代替W号NOT?
搜烦含有"jakarta apache"Q但是不含有"jakarta lucene"的文档,使用查询Q?
"jakarta apache" NOT "jakarta lucene"
注意QNOT操作W不能单独与用构成查询。例如,以下的查询查不到Ml果Q?
NOT "jakarta apache"
-
"-"操作W或者禁止操作符排除含有"-"后面的相似项的文档?
搜烦含有"jakarta apache"Q但不是"jakarta lucene"Q用查询:
"jakarta apache" -"jakarta lucene"
分组QGroupingQ?
Lucene支持使用圆括hl合字句形成子查询。这对于x制查询布逻辑的h十分有用?
搜烦含有"jakarta"或?apache"Q同时含?website"的文档,使用查询Q?
(jakarta OR apache) AND website
q样消除了歧义Q保证website必须存在Qjakarta和apache中之一也存在?
转义Ҏ字符QEscaping Special CharactersQ?
Lucene支持转义Ҏ字符Q因为特D字W是查询语法用到的。现在,Ҏ字符包括
+ - && || ! ( ) { } [ ] ^ " ~ * ? : \
转义Ҏ字符只需在字W前加上W号\,例如搜烦(1+1):2Q用查?
\(1\+1\)\:2
---------------------------------------
索引文g格式
本文定义了LuceneQ版?.3Q用到的索引文g的格式?
Jakarta Lucene是用Java写成的,同时有很多团体正在默默的用其他的E序语言来改写它。如果这些新的版本想和Jakarta Lucene兼容Q就需要一个与具体语言无关的Lucene索引文g格式。本文正是试图提供一个完整的与语a无关的Jakarta Lucene 1.3索引文g格式的规格定义?
随着Lucene不断发展Q本文也应该更新。不同语a写成的Lucene实现版本应当力遵守文g格式Q也必须产生本文的新版本?
本文同时提供兼容性批注,描述文g格式上与前一版本不同的地斏V?
定义
Lucene中最基础的概忉|索引QindexQ,文档QdocumentQ,域(fieldQ和(termQ?
索引包含了一个文档的序列?
· 文档是一些域的序列?
· 域是一些项的序列?
· 就是一个字丌Ӏ?
存在于不同域中的同一个字串被认ؓ是不同的V因此项实际是用一对字串表C的Q第一个字串是域名Q第二个是域中的字串?
倒排索引
Z使得Z的搜烦更有效率Q烦引中Ҏ静态存储的。Lucene的烦引属于烦引方式中的倒排索引Q因为对于一个项q种索引可以列出包含它的文档。这刚好是文档与自然联pȝ倒置?
域的cd
Lucene中,域的文本可能以逐字的非倒排的方式存储在索引中。而倒排q的域称索引q了。域也可能同时被存储和被索引?
域的文本可能被分解许多项目而被索引Q或者就被用作一个项目而被索引。大多数的域是被分解q的Q但是有些时候某些标识符域被当做一个项目烦引是很有用的?
D(SegmentQ?
Lucene索引可能由多个子索引l成Q这些子索引成ؓDc每一D都是完整独立的索引Q能被搜索。烦引是q样作成的:
1. 为新加入的文档创建新Dc?
2. 合ƈ已经存在的段?
搜烦旉要涉及到多个D和/或者多个烦引,每一个烦引又可能׃些段l成?
文档PDocument NumberQ?
内部的来_Lucene用一个整形(intergerQ的文档h指示文档。第一个被加入到烦引中的文档就?P序加入的文档将得到一个由前一个号码递增而来的号码?
注意文档h可能改变的,所以在Lucene外部存储q些L时必d心。特别的Q号码的改变的情况如下:
· 只有D内的号码是相同的,不同D之间不同,因而在一个比D广泛的上下文环境中使用q些LӞ必L变它们。标准的技术是Ҏ每一D号码多ؓ每一D分 配一个段受将D内文档可{换到D外Ӟ加上D号。将某段外的文档可{换到D内ӞҎ每段中可能的转换后号码范围来判断文档属于那一D,q减调这一D늚 D号。例如有两个?个文档的D合qӞ那么W一D늚D号是0Q第二段D号5。第二段中的W三个文档,在段外的L是8?
· 文档删除后,q箋的号码就出现了间断。这可以通过合ƈ索引来解冻ID合q时删除的文档相应也删掉了,新合q而成的段q没有号码间断?
l论
索引D늻护着以下的信息:
· 域集合。包含了索引中用到的所有的域?
· 域值存储表。每一个文档都含有一个“属性-值”对的列表,属性即为域名。这个列表用来存储文档的一些附加信息,如标题,url或者访问数据库的一个ID。在搜烦时存储域的集合可以被q回。这个表以文档号标识?
· 字典。这个字典含有所有文档的所有域中用过的的,同时含有使用q它的文档的文档P以及指向使用频数信息和位|信息的指针?
· wC息。对于项字典中的每个,q些信息包含含有q个的文档的LQ以及每个文档中使用的次数?
· 位|信息。对于项字典中的每个,都存有在每个文档中出现的各个位置?
· Normalization factors. For each field in each document, a value is stored that is multiplied into the score for hits on that field. 标准化因子。对于文档中的每一个域Q存有一个|用来以后乘以q个q个域的命中敎ͼhitsQ?
· 被删除的文档信息。这是一个可选文Ӟ用来表明那些文档已经删除了?
接下来的各部分部分详l描q这些信息?
文g的命名(File NamingQ?
同属于一个段的文件拥有相同的文g名,不同的扩展名。扩展名׃下讨论的各种文g格式定?
一般来_一个烦引存放一个目录,其所有段都存攑֜q个目录里,管我们不要求您q样做?
基本数据cdQPrimitive TypesQ?
Byte
最基本的数据类型就是字节(byteQ?位)。文件就是按字节序讉K的。其它的一些数据类型也定义为字节的序列Q文件的格式h字节意义上的独立性?
UInt32
32位无W号整数Q由四个字节l成Q高位优先?
UInt32 --> <Byte>4
Uint64
64位无W号整数Q由八字节组成,高位优先?
UInt64 --> <Byte>8
VInt
可变长的正整数类型,每字节的最高位表明q剩多少字节。每字节的低七位表明整数的倹{因此单字节的g0?27Q两字节g128?6,383Q等{?
VInt ~码CZ
Value
First byte
Second byte
Third byte
0
00000000
1
00000001
2
00000010
...
127
01111111
128
10000000
00000001
129
10000001
00000001
130
10000010
00000001
...
16,383
11111111
01111111
16,384
10000000
10000000
00000001
16,385
10000001
10000000
00000001
...
q种~码提供了一U在高效率解码时压羃数据的方法?
Chars
Lucene输出UNICODE字符序列Q用标准UTF-8~码?
String
Lucene输出由VINT和字W串l成的字ԌVINT表示字串长,字符串紧接其后?
String --> VInt, Chars
索引包含的文ӞPer-Index FilesQ?
q部分介l每个烦引包含的文g?
Segments文g
索引中活动的D存储在Segments文g中。每个烦引只能含有一个这L文gQ名?segments".q个文g依次列出每个D늚名字和每个段的大?
Segments --> SegCount, <SegName, SegSize>SegCount
SegCount, SegSize --> UInt32
SegName --> String
SegName表示该segment的名字,同时作ؓ索引其他文g的前~?
SegSize是段索引中含有的文档数?
Lock文g
有一些文件用来表C另一个进E在使用索引?
· 如果存在"commit.lock"文gQ表C有q程在写"segments"文g和删除无用的D늃引文Ӟ或者表C有q程在读"segments"文g 和打开某些D늚文g。在一个进E在d"segments"文gD信息后Q还没来得及打开所有该D늚文g前,q个Lock文g可以防止另一个进E删除这? 文g?
· 如果存在"index.lock"文gQ表C有q程在向索引中加入文档,或者是从烦引中删除文档。这个文仉止很多文件同时修改一个烦引?
Deleteable文g
名ؓ"deletetable"的文件包含了索引不再使用的文件的名字Q这些文件可能ƈ没有被实际的删除。这U情况只存在与Win32q_下,因ؓWin32下文件仍打开时ƈ不能删除?
Deleteable --> DelableCount, <DelableName>DelableCount
DelableCount --> UInt32
DelableName --> String
D包含的文gQPer-Segment FilesQ?
剩下的文件是每段中包含的文gQ因此由后缀来区分?
域(FieldQ?
域集合信息(Field InfoQ?
所有域名都存储在这个文件的域集合信息中Q这个文件以后缀.fnml尾?
FieldInfos (.fnm) --> FieldsCount, <FieldName, FieldBits>FieldsCount
FieldsCount --> VInt
FieldName --> String
FieldBits --> Byte
目前情况下,FieldBits只有使用低位Q对于已索引的域gؓ1Q对未烦引的域gؓ0?
文g中的域根据它们的ơ序~号。因此域0是文件中的第一个域Q域1是接下来的,{等。这个和文档L~号方式相同?
域值存储表QStored FieldsQ?
域值存储表使用两个文g表示Q?
1. 域烦引(.fdx文gQ?
如下Q对于每个文档这个文件包含指向域值的指针Q?
FieldIndex (.fdx) --> <FieldValuesPosition>SegSize
FieldValuesPosition --> Uint64
FieldValuesPosition 指示的是某一文档的某域的域值在域值文件中的位|。因为域值文件含有定长的数据信息Q因而很Ҏ随机讉K。在域值文件中Q文档n的域g息就存在n*8? |处QThe position of document n's field data is the Uint64 at n*8 in this file.Q?
2. 域|.fdt文gQ?
如下Q每个文档的域g息包含:
FieldData (.fdt) --> <DocFieldData>SegSize
DocFieldData --> FieldCount, <FieldNum, Bits, Value>FieldCount
FieldCount --> VInt
FieldNum --> VInt
Bits --> Byte
Value --> String
目前情况下,Bits只有低位被用,gؓ1表示域名被分解过Qgؓ0表示未分解过?
字典(Term DictionaryQ?
字典用以下两个文g表示Q?
1. 信息(.tis文gQ?
TermInfoFile (.tis)--> TermCount, TermInfos
TermCount --> UInt32
TermInfos --> <TermInfo>TermCount
TermInfo --> <Term, DocFreq, FreqDelta, ProxDelta>
Term --> <PrefixLength, Suffix, FieldNum>
Suffix --> String
PrefixLength, DocFreq, FreqDelta, ProxDelta
--> VInt
信息按Ҏ序。项信息排序时先按项所属的域的文字序排序Q然后按照项的字串的文字序排序?
的字前~往往是共同的Q与字的后缀l成字。PrefixLength变量是表示与前一相同的前缀的字数。因此,如果前一个项的字?bone"Q后一个是"boy"的话QPrefixLengthgؓ2QSuffixgؓ"y"?
FieldNum指明了项属于的域P而域名存储在.fdt文g中?
DocFreg表示的是含有该项的文档的数量?
FreqDelta指明了项所属TermFreq变量?frq文g中的位置。详l的_是指相对于前一个项的数据的位置偏移量(或者是0Q表C文件中W一个项Q?
ProxDelta指明了项所属的TermPosition变量?prx文g中的位置。详l的_是指相对于前一个项的数据的位置偏移量(或者是0Q表C文件中W一个项Q?
2. 信息烦引(.tii文gQ?
每个信息烦引文件包?tis文g中的128个条目,依照条目?tis文g中的序。这栯计是Z一ơ将索引信息d内存能,然后使用它来随机的访?tis文g?
q个文g的结构和.tis文g非常cMQ只在每个条目记录上增加了一个变量IndexDelta?
TermInfoIndex (.tii)--> IndexTermCount, TermIndices
IndexTermCount --> UInt32
TermIndices --> <TermInfo, IndexDelta>IndexTermCount
IndexDelta --> VInt
IndexDelta表示该项的TermInfo变量值在.tis文g中的位置。详l的Ԍ是指相对于前一个条目的偏移量(或者是0Q对于文件中W一个项Q?
w敎ͼFrequenciesQ?
.frq文g包含每一的文档的列表,q有该项在对应文档中出现的频数?
FreqFile (.frq) --> <TermFreqs>TermCount
TermFreqs --> <TermFreq>DocFreq
TermFreq --> DocDelta, Freq?
DocDelta,Freq --> VInt
TermFreqs序列按照Ҏ排序Q依据于.tis文g中的,即项是隐含存在的Q?
TermFreq元组按照文档号升序排列?
DocDelta 军_了文档号和频数。详l的_DocDelta/2表示相对于前一文档L偏移量(或者是0Q表C是TermFreqs里面的第一)。当 DocDelta是奇数时表示在该文档中频Cؓ1Q当DocDelta是偶数时Q另一个VIntQFreqQ就表示在该文档中出现的频数?
例如Q假设某一在文档7中出Cơ,在文?1中出C3ơ,在TermFreqs中就存在如下的VInts序列Q?
15, 22, 3
位|(PositionQ?
.prx文g包含了某文档中某出现的位置信息的列表?
ProxFile (.prx) --> <TermPositions>TermCount
TermPositions --> <Positions>DocFreq
Positions --> <PositionDelta>Freq
PositionDelta --> VInt
TermPositions按照Ҏ排序Q依据于.tis文g中的,即项是隐含存在的Q?
Positions元组按照文档号升序排列?
PositionDelta是相对于前一个出C|的偏移位置Q或者ؓ0Q表C是第一ơ在q个文档中出玎ͼ?
例如Q假设某一在某文档第4出玎ͼ在另一个文档中W?和W?出玎ͼ存在如下的VInt序列Q?
4, 5, 4
标准化因子(Normalization FactorQ?
.nrm文g包含了每个文档的标准化因子,标准化因子用来以后乘以这个这个域的命中数?
Norms (.nrm) --> <Byte>SegSize
每个字节记录一个QҎ。位0-2包含?位的数部分Q位3-8包含?位的指数部分?
按如下规则可这些字节{换ؓIEEE标准单精度QҎQ?
1. 如果该字节是0Q就是Q?Q?
2. 否则Q设|新点数的标志位ؓ0Q?
3. 字节中的指数加?8后作为新的QҎ的指敎ͼ
4. 字节中的尾数映到新QҎ数的高3位;q且
5. 讄新QҎ数的低21位ؓ0?
被删除的文档QDeleted DocumentQ?
.del文g是可选的Q只有在某段中存在删除操作后才存在:
Deletions (.del) --> ByteCount,BitCount,Bits
ByteSize,BitCount --> Uint32
Bits --> <Byte>ByteCount
ByteCount表示的是Bits列表中Byte的数量。典型的Q它{于QSegSize/8Q?1?
BitCount表示Bits列表中多个已经被设|过了?
Bits列表包含了一些位QbitQ,序表示一个文档。当对应于文档号的位被设|了Q就标志着q个文档已经被删除了。位的顺序是从低到高。因此,如果Bits包含两个字节Q?x00?x02Q那么表C文?已经删除了?
局限性(LimitationsQ?
在以上的文g格式中,好几处都有限刉和文档的最大个Cؓ32位数的极限,xq于40ѝ今天看来,q不会造成问题Q但是,长远的看Q可能造成问题。因此,q些极限应该或者换为UInt64cd的|或者更好的Q换为VIntcd的|VInt值没有上限)?
有两处地方的代码要求必须是定长的|他们是:
1. FieldValuesPosition变量Q存储于域烦引文件中Q?fdx文gQ。它已经是一个UInt64型,所以不会有问题?
2. TermCount变量Q存储于信息文件中Q?tis文gQ。这是最后输出到文g中的Q但是最先被dQ因此是存储于文件的最前端 。烦引代码先在这里写入一?|然后在其他文件输出完毕后覆盖q个倹{所以无论它存储在什么地方,它都必须是一个定长的|它应该被变成UInt64 型?
除此之外Q所有的UInt值都可以换成VInt型以L限制?/p>
jspSmartUpload是由www.jspsmart.com|站开发的一个可免费使用的全功能的文件上传下载组Ӟ适于嵌入执行上传下蝲操作的JSP文g中。该lg有以下几个特点:
1、用简单。在JSP文g中仅仅书写三五行JAVA代码可以搞定文件的上传或下载,方便?
2、能全程控制上传。利用jspSmartUploadlg提供的对象及其操作方法,可以获得全部上传文g的信息(包括文g名,大小Q类型,扩展名,文g数据{)Q方便存取?
3、能对上传的文g在大、类型等斚w做出限制。如此可以o掉不W合要求的文件?
4、下载灵zR仅写两行代码,p把Web服务器变成文件服务器。不文件在Web服务器的目录下或在其它Q何目录下Q都可以利用jspSmartUploadq行下蝲?
5、能文件上传到数据库中Q也能将数据库中的数据下载下来。这U功能针对的是MYSQL数据库,因ؓ不具有通用性,所以本文不准备举例介绍q种用法?
jspSmartUploadlg可以?a >www.jspsmart.com|站上自׃载,压羃包的名字是jspSmartUpload.zip。下载后Q用WinZip或WinRAR其解压到Tomcat的webapps目录下(本文以Tomcat服务器ؓ例进行介l)。解压后Q将webapps/jspsmartupload目录下的子目录Web-inf名字改ؓ全大写的WEB-INFQ这样一改jspSmartUploadcL能用。因为TomcatҎ件名大小写敏感,它要求Web应用E序相关的类所在目录ؓWEB-INFQ且必须是大写。接着重新启动TomcatQ这样就可以在JSP文g中用jspSmartUploadlg了?
注意Q按上述Ҏ安装后,只有webapps/jspsmartupload目录下的E序可以使用jspSmartUploadlgQ如果想让Tomcat服务器的所有Web应用E序都能用它Q必d如下工作Q?
1Q进入命令行状态,目录切换到Tomcat的webapps/jspsmartupload/WEB-INF目录下?
2Q运行JAR打包命oQjar cvf jspSmartUpload.jar com
Q也可以打开资源理器,切换到当前目录,用WinZipcom目录下的所有文件压~成jspSmartUpload.zipQ然后将jspSmartUpload.zip换名为jspSmartUpload.jar文g卛_。)
3Q将jspSmartUpload.jar拯到Tomcat的shared/lib目录下?
二、相关类说明?
?Filec?
q个cd装了一个上传文件的所有信息。通过它,可以得到上传文g的文件名、文件大、扩展名、文件数据等信息?
FilecM要提供以下方法:
1、saveAs作用Q将文g换名另存?
原型Q?
public void saveAs(java.lang.String destFilePathName)
?
public void saveAs(java.lang.String destFilePathName, int optionSaveAs)
其中QdestFilePathName是另存的文g名,optionSaveAs是另存的选项Q该选项有三个|分别是SAVEAS_PHYSICAL,SAVEAS_VIRTUALQSAVEAS_AUTO。SAVEAS_PHYSICAL表明以操作系l的根目录ؓ文g根目录另存文ӞSAVEAS_VIRTUAL表明以Web应用E序的根目录为文件根目录另存文gQSAVEAS_AUTO则表Clg军_Q当Web应用E序的根目录存在另存文g的目录时Q它会选择SAVEAS_VIRTUALQ否则会选择SAVEAS_PHYSICAL?
例如QsaveAs("/upload/sample.zip",SAVEAS_PHYSICAL)执行后若Web服务器安装在C盘,则另存的文g名实际是c:\upload\sample.zip。而saveAs("/upload/sample.zip",SAVEAS_VIRTUAL)执行后若Web应用E序的根目录是webapps/jspsmartuploadQ则另存的文件名实际是webapps/jspsmartupload/upload/sample.zip。saveAs("/upload/sample.zip",SAVEAS_AUTO)执行时若Web应用E序根目录下存在upload目录Q则其效果同saveAs("/upload/sample.zip",SAVEAS_VIRTUAL)Q否则同saveAs("/upload/sample.zip",SAVEAS_PHYSICAL)?
Q对于WebE序的开发来_最好用SAVEAS_VIRTUALQ以便移植?
2、isMissing
作用Q这个方法用于判断用h否选择了文Ӟ也即对应的表单项是否有倹{选择了文件时Q它q回false。未选文件时Q它q回true?
原型Qpublic boolean isMissing()
3、getFieldName
作用Q取HTML表单中对应于此上传文件的表单的名字?
原型Qpublic String getFieldName()
4、getFileName
作用Q取文g名(不含目录信息Q?
原型Qpublic String getFileName()
5、getFilePathName
作用Q取文g全名Q带目录Q?
原型Qpublic String getFilePathName
6、getFileExt
作用Q取文g扩展名(后缀Q?
原型Qpublic String getFileExt()
7、getSize
作用Q取文g长度Q以字节计)
原型Qpublic int getSize()
8、getBinaryData
作用Q取文g数据中指定位Ud的一个字节,用于文件等处理?
原型Qpublic byte getBinaryData(int index)。其中,index表示位移Q其值在0到getSize()-1之间?
?Filesc?
q个c表C所有上传文件的集合Q通过它可以得C传文件的数目、大等信息。有以下ҎQ?
1、getCount
作用Q取得上传文件的数目?
原型Qpublic int getCount()
2、getFile
作用Q取得指定位Ud的文件对象FileQ这是com.jspsmart.upload.FileQ不是java.io.FileQ注意区分)?
原型Qpublic File getFile(int index)。其中,index为指定位U,其值在0到getCount()-1之间?
3、getSize
作用Q取得上传文件的总长度,可用于限制一ơ性上传的数据量大?
原型Qpublic long getSize()
4、getCollection
作用Q将所有上传文件对象以Collection的Ş式返回,以便其它应用E序引用Q浏览上传文件信息?
原型Qpublic Collection getCollection()
5、getEnumeration
作用Q将所有上传文件对象以EnumerationQ枚举)的Ş式返回,以便其它应用E序览上传文g信息?
原型Qpublic Enumeration getEnumeration()
?Requestc?
q个cȝ功能{同于JSP内置的对象request。只所以提供这个类Q是因ؓ对于文g上传表单Q通过request对象无法获得表单的|必须通过jspSmartUploadlg提供的Request对象来获取。该cL供如下方法:
1、getParameter
作用Q获取指定参C倹{当参数不存在时Q返回gؓnull?
原型Qpublic String getParameter(String name)。其中,name为参数的名字?
2、getParameterValues
作用Q当一个参数可以有多个值时Q用此方法来取其倹{它q回的是一个字W串数组。当参数不存在时Q返回gؓnull?
原型Qpublic String[] getParameterValues(String name)。其中,name为参数的名字?
3、getParameterNames
作用Q取得Request对象中所有参数的名字Q用于遍历所有参数。它q回的是一个枚丑֞的对象?
原型Qpublic Enumeration getParameterNames()
?SmartUploadc这个类完成上传下蝲工作?
AQ上传与下蝲q的方法:
只有一个:initialize?
作用Q执行上传下载的初始化工作,必须W一个执行?
原型Q有多个Q主要用下面这个:
public final void initialize(javax.servlet.jsp.PageContext pageContext)
其中QpageContext为JSP面内置对象Q页面上下文Q?
BQ上传文件用的ҎQ?
1、upload
作用Q上传文件数据。对于上传操作,W一步执行initializeҎQ第二步p执行q个Ҏ?
原型Qpublic void upload()
2、save
作用Q将全部上传文g保存到指定目录下Qƈq回保存的文件个数?
原型Qpublic int save(String destPathName)
和public int save(String destPathName,int option)
其中QdestPathName为文件保存目录,optionZ存选项Q它有三个|分别是SAVE_PHYSICAL,SAVE_VIRTUAL和SAVE_AUTO。(同FilecȝsaveAsҎ的选项之值类|SAVE_PHYSICAL指示lg文件保存到以操作系l根目录为文件根目录的目录下QSAVE_VIRTUAL指示lg文件保存到以Web应用E序根目录ؓ文g根目录的目录下,而SAVE_AUTO则表C由lg自动选择?
注:save(destPathName)作用{同于save(destPathName,SAVE_AUTO)?
3、getSize
作用Q取上传文g数据的总长?
原型Qpublic int getSize()
4、getFiles
作用Q取全部上传文gQ以Files对象形式q回Q可以利用Filescȝ操作Ҏ来获得上传文件的数目{信息?
原型Qpublic Files getFiles()
5、getRequest
作用Q取得Request对象Q以便由此对象获得上传表单参C倹{?
原型Qpublic Request getRequest()
6、setAllowedFilesList
作用Q设定允怸传带有指定扩展名的文Ӟ当上传过E中有文件名不允许时Q组件将抛出异常?
原型Qpublic void setAllowedFilesList(String allowedFilesList)
其中QallowedFilesList为允怸传的文g扩展名列表,各个扩展名之间以逗号分隔。如果想允许上传那些没有扩展名的文gQ可以用两个逗号表示。例如:setAllowedFilesList("doc,txt,,")允怸传带doc和txt扩展名的文g以及没有扩展名的文g?
7、setDeniedFilesList
作用Q用于限制上传那些带有指定扩展名的文件。若有文件扩展名被限Ӟ则上传时lg抛出异常?
原型Qpublic void setDeniedFilesList(String deniedFilesList)
其中QdeniedFilesList为禁止上传的文g扩展名列表,各个扩展名之间以逗号分隔。如果想止上传那些没有扩展名的文gQ可以用两个逗号来表C。例如:setDeniedFilesList("exe,bat,,")禁止上传带exe和bat扩展名的文g以及没有扩展名的文g?
8、setMaxFileSize
作用Q设定每个文件允怸传的最大长度?
原型Qpublic void setMaxFileSize(long maxFileSize)
其中QmaxFileSizeZؓ每个文g允许上传的最大长度,当文件超出此长度Ӟ不被上传?
9、setTotalMaxFileSize
作用Q设定允怸传的文g的总长度,用于限制一ơ性上传的数据量大?
原型Qpublic void setTotalMaxFileSize(long totalMaxFileSize)
其中QtotalMaxFileSize为允怸传的文g的总长度?
CQ下载文件常用的Ҏ
1、setContentDisposition
作用Q将数据q加到MIME文g头的CONTENT-DISPOSITION域。jspSmartUploadlg会在q回下蝲的信息时自动填写MIME文g头的CONTENT-DISPOSITION域,如果用户需要添加额外信息,L此方法?
原型Qpublic void setContentDisposition(String contentDisposition)
其中QcontentDispositiond的数据。如果contentDisposition为nullQ则lg自动添?attachment;"Q以表明下载的文g作ؓ附gQ结果是IE览器将会提C另存文Ӟ而不是自动打开q个文gQIE览器一般根据下载的文g扩展名决定执行什么操作,扩展名ؓdoc的将用wordE序打开Q扩展名为pdf的将用acrobatE序打开Q等{)?
2、downloadFile
作用Q下载文件?
原型Q共有以下三个原型可用,W一个最常用Q后两个用于Ҏ情况下的文g下蝲Q如更改内容cdQ更改另存的文g名)?
?public void downloadFile(String sourceFilePathName)
其中QsourceFilePathName下蝲的文件名Q带目录的文件全名)
?public void downloadFile(String sourceFilePathName,String contentType)
其中QsourceFilePathName下蝲的文件名Q带目录的文件全名),contentType为内容类型(MIME格式的文件类型信息,可被览器识别)?
?public void downloadFile(String sourceFilePathName,String contentType,String destFileName)
其中QsourceFilePathName下蝲的文件名Q带目录的文件全名),contentType为内容类型(MIME格式的文件类型信息,可被览器识别),destFileNameZ载后默认的另存文件名?
三、文件上传篇
?表单要求
对于上传文g的FORM表单Q有两个要求Q?
1、METHOD应用POSTQ即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
本页面提供表单,让用户选择要上传的文gQ点?上传"按钮执行上传操作?
面源码如下Q?
<!--
文g名:upload.html
作?者:U|软g制作中心雨亦?zhsoft88@sohu.com)
-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>文g上传</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body>
<p> </p>
<p align="center">上传文g选择</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
本页面执行文件上传操作。页面源码中详细介绍了上传方法的用法Q在此不赘述了?
面源码如下Q?
<%--
文g名:do_upload.jsp
作?者:U|软g制作中心雨亦?zhsoft88@sohu.com)
--%>
<%@ page contentType="text/html; charset=gb2312" language="java"
import="java.util.*,com.jspsmart.upload.*" errorPage="" %>
<html>
<head>
<title>文g上传处理面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body>
<%
// 新徏一个SmartUpload对象
SmartUpload su = new SmartUpload();
// 上传初始?br /> su.initialize(pageContext);
// 讑֮上传限制
// 1.限制每个上传文g的最大长度?br /> // su.setMaxFileSize(10000);
// 2.限制M传数据的长度?br /> // su.setTotalMaxFileSize(20000);
// 3.讑֮允许上传的文Ӟ通过扩展名限Ӟ,仅允许doc,txt文g?br /> // su.setAllowedFilesList("doc,txt");
// 4.讑֮止上传的文Ӟ通过扩展名限Ӟ,止上传带有exe,bat,
jsp,htm,html扩展名的文g和没有扩展名的文件?br /> // su.setDeniedFilesList("exe,bat,jsp,htm,html,,");
// 上传文g
su.upload();
// 上传文件全部保存到指定目录
int count = su.save("/upload");
out.println(count+"个文件上传成功!<br>");
// 利用Request对象获取参数之?br /> out.println("TEST="+su.getRequest().getParameter("TEST")
+"<BR><BR>");
// 逐一提取上传文g信息Q同时可保存文g?br /> for (int i=0;i<su.getFiles().getCount();i++)
{
com.jspsmart.upload.File file = su.getFiles().getFile(i);
// 若文件不存在则l?br /> if (file.isMissing()) continue;
// 昄当前文g信息
out.println("<TABLE BORDER=1>");
out.println("<TR><TD>表单名QFieldNameQ?lt;/TD><TD>"
+ file.getFieldName() + "</TD></TR>");
out.println("<TR><TD>文g长度QSizeQ?lt;/TD><TD>" +
file.getSize() + "</TD></TR>");
out.println("<TR><TD>文g名(FileNameQ?lt;/TD><TD>"
+ file.getFileName() + "</TD></TR>");
out.println("<TR><TD>文g扩展名(FileExtQ?lt;/TD><TD>"
+ file.getFileExt() + "</TD></TR>");
out.println("<TR><TD>文g全名QFilePathNameQ?lt;/TD><TD>"
+ file.getFilePathName() + "</TD></TR>");
out.println("</TABLE><BR>");
// 文件另?br /> // file.saveAs("/upload/" + myFile.getFileName());
// 另存CWEB应用E序的根目录为文件根目录的目录下
// file.saveAs("/upload/" + myFile.getFileName(),
su.SAVE_VIRTUAL);
// 另存到操作系l的根目录ؓ文g根目录的目录?br /> // file.saveAs("c:\\temp\\" + myFile.getFileName(),
su.SAVE_PHYSICAL);
}
%>
</body>
</html>
四、文件下载篇
1、下载链接页面download.html
面源码如下Q?
<!--
文g名:download.html
作?者:U|软g制作中心雨亦?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展示了如何利用jspSmartUploadlg来下载文Ӟ从下面的源码中就可以看到Q下载何其简单?
源码如下Q?
<%@ page contentType="text/html;charset=gb2312"
import="com.jspsmart.upload.*" %><%
// 新徏一个SmartUpload对象
SmartUpload su = new SmartUpload();
// 初始?br /> su.initialize(pageContext);
// 讑֮contentDisposition为null以禁止浏览器自动打开文gQ?br /> //保证点击链接后是下蝲文g。若不设定,则下载的文g扩展名ؓ
//docӞ览器将自动用word打开它。扩展名为pdfӞ
//览器将用acrobat打开?br /> su.setContentDisposition(null);
// 下蝲文g
su.downloadFile("/upload/如何赚取我的W一桉.doc");
%>
注意Q执行下载的面Q在Java脚本范围外(?lt;% ... %>之外Q,不要包含HTML代码、空根{回车或换行{字W,有的话将不能正确下蝲。不信的话,可以在上q源码中%><%之间加入一个换行符Q再下蝲一下,保证出错。因为它影响了返回给览器的数据,D解析出错?
3、如何下载中文文?
jspSmartUpload虽然能下载文Ӟ但对中文支持不。若下蝲的文件名中有汉字Q则览器在提示另存的文件名Ӟ昄的是一堆ؕ码,很扫人兴。上面的例子是q样。(q个问题也是众多下蝲lg所存在的问题,很少有h解决Q搜索不到相兌料,可叹Q)
ZljspSmartUploadlg增加下蝲中文文g的支持,我对该组件进行了研究Q发现对q回l浏览器的另存文件名q行UTF-8~码后,览器便能正显CZ文名字了。这是一个o人高兴的发现。于是我对jspSmartUploadlg的SmartUploadcd了升U处理,增加了toUtf8Stringq个ҎQ改动部分源码如下:
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~码的串,以便下蝲时能正确昄另存的文件名.
* U|软g制作中心雨亦?003.08.01
* @param s 原文件名
* @return 重新~码后的文g?br /> */
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();
}
注意源码中粗体部分,原jspSmartUploadlg对返回的文g未作M处理Q现在做了编码的转换工作Q将文g名{换ؓUTF-8形式的编码Ş式。UTF-8~码对英文未作Q何处理,对中文则需要{换ؓ%XX的Ş式。toUtf8StringҎ中,直接利用Java语言提供的编码{换方法获得汉字字W的UTF-8~码Q之后将其{换ؓ%XX的Ş式?
源码编译后打包成jspSmartUpload.jarQ拷贝到Tomcat的shared/lib目录下(可ؓ所有WEB应用E序所׃nQ,然后重启Tomcat服务器就可以正常下蝲含有中文名字的文件了。另QtoUtf8StringҎ也可用于转换含有中文的超U链接,以保证链接的有效Q因为有的WEB服务器不支持中文链接?
结QjspSmartUploadlg是应用JSPq行B/SE序开发过E中l常使用的上传下载组Ӟ它用简单,方便。现在我又ؓ其加上了下蝲中文名字的文件的支持Q真个是如虎ȝQ必赢得更多开发者的青睐?
---------------------------------------------------------------------------------------------------------------------------
题目Q彻底解决中文名文g下蝲和下载文件内容ؕ码问?/p>
之前,写过一个Download.jsp文g,可以解决下蝲文gq问题(诸如:DOC,XSL文g{等).
后来发现,遇到中文名的文g的时?文g下蝲会报错~~~~
今天,通过改写原Download.jsp文g已经d解决了这个问题~
现在,把一整套的文件上传下载的Ҏl脓出来~~~以便大家借鉴!~!~!~!~!
作?古埃及法?br />-----------------------------------------------------
试环境:WEBLOGIC 8.1,WIN XP SP4,IE 6.0
-----------------------------------------------------
文g上传:
-----------------------------------------
准备工作:导入著名的SmartUpload.jarlg?br />upload.jsp文g
-----------------------------------------------------
<%@ page contentType="text/html; charset=gb2312" %>
<%
request.setCharacterEncoding("gb2312"); // q句话很重要Q否则遇C文就出错~
%>
<HTML><HEAD><TITLE>上传</TITLE>
<META content="text/html; charset=gb2312" http-equiv=Content-Type>
</HEAD>
<BODY leftMargin=0 topMargin=0>
<table width="100%" height="100%" border="0" cellpadding="0" cellspacing="0" bgcolor="#DEE7EF">
<tr>
<td align="center">
<FORM action="upload_ok.jsp" method=post name="Upload" enctype="multipart/form-data">
<br>
误入附件文件的所在\?lt;FONT color=red> * </FONT>为必填项?lt;br>
<br>
<TABLE width="317" border=0 cellPadding=0>
<TBODY>
<TR>
<TD align=right vAlign=center nowrap>附g路径Q?lt;/TD>
<TD><input type="file" name="file" style="border: 1px #FFFFFF solid;background:#efefef" > <FONT color=red>*</FONT></TD>
</TR>
<TR align="center">
<TD height=60 colspan="2" vAlign=center nowrap> <INPUT style="height:22px" name=B1 type=submit value=" ??" >
<INPUT style="height:22px" name=B2 type=reset value=" ??" >
</TD>
</TR>
</TBODY>
</TABLE>
</FORM>
</td>
</tr>
</table>
</BODY></HTML>
-----------------------------------------------------
upload_ok.jsp文g
-----------------------------------------------------
<%@ page contentType="text/html;charset=gb2312" %>
<%@ page import="com.jspsmart.upload.*" %>
<HTML><HEAD><TITLE>上传成功!</TITLE>
<META content="text/html; charset=gb2312" http-equiv=Content-Type>
</HEAD>
<BODY leftMargin=0 topMargin=0>
<jsp:useBean id="mySmartUpload" scope="page" class="com.jspsmart.upload.SmartUpload" />
<table width="80%" border="0" cellpadding="0" cellspacing="0" bgcolor="#DEE7EF">
<tr>
<td align="center">
<%
int count=0;
String fileName = null;
mySmartUpload.initialize(pageContext);
mySmartUpload.upload();
com.jspsmart.upload.File myFile = mySmartUpload.getFiles().getFile(0);
if (!myFile.isMissing()) {
//String ext=myFile.getFileExt();//得到后缀
fileName = myFile.getFileName();
myFile.saveAs("/files/" + fileName);//你要存放文g所在文件夹的相对\?br /> out.println("文g:<b>"+fileName+"</b>上传成功!<br>文g大小:" + myFile.getSize() + "kb<BR>");
}
%>
</BODY></HTML>
---------------------------------------------------
文g下蝲:
-----------------------------------------
文g的超q接写法范例:
<% String fname ="中文试.xsl"; //假设你的文g名是:中文试.xsl
%>
<A target="_blank" href="Download.jsp?filename=<%=fname%>">??lt;/A>
文g的超q接写法范例-2 重新用utf-8Ҏ件名~码:
<%@ page contentType="text/html;charset=gb2312" session="true"%>
<% String name=java.net.URLEncoder.encode("世界文化.doc","UTF-8"));%> <a href="c:\<%=name%>">世界文化.doc</a>
Download.jsp文g
---------------------------------------------------
<%
java.io.BufferedInputStream bis=null;
java.io.BufferedOutputStream bos=null;
try{
String filename=request.getParameter("filename");
filename=new String(filename.getBytes("iso8859-1"),"gb2312");
response.setContentType("application/x-msdownload");
response.setHeader("Content-disposition","attachment; filename="+new String(filename.getBytes("gb2312"),"iso8859-1"));
bis =new java.io.BufferedInputStream(new java.io.FileInputStream(config.getServletContext().getRealPath("files/" + filename)));
bos=new java.io.BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[2048];
int bytesRead;
while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff,0,bytesRead);
}
}
catch(Exception e){
e.printStackTrace();
}
finally {
if (bis != null)bis.close();
if (bos != null)bos.close();
}
%>