KevinGong

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  15 Posts :: 1 Stories :: 9 Comments :: 0 Trackbacks

2007年2月3日 #

/*
 * 简单的读/写文本文件的示例
 * 这里包含了三个例子,即
 * 1. 将文件读入到内存(这里是StringBuffer)的例子
 * 2. 将内容中的文本写到文件
 * 3. 将一个文件的内容读出来写入另一个文件中
 *    同时也展示了如果从输入流中读出来内容写入输出流中(仅限文本流)
 * 三个例子可以独立存在,所以根据需要只看其中一个就行了。
 */

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;

public final class AccessTextFile {

    /**
     * 1. 演示将流中的文本读入一个 StringBuffer 中
     * @throws IOException
     */
    public void readToBuffer(StringBuffer buffer, InputStream is)
        throws IOException {
        String line;        // 用来保存每行读取的内容
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        line = reader.readLine();       // 读取第一行
        while (line != null) {          // 如果 line 为空说明读完了
            buffer.append(line);        // 将读到的内容添加到 buffer 中
            buffer.append("\n");        // 添加换行符
            line = reader.readLine();   // 读取下一行
        }
    }

    /**
     * 2. 演示将 StringBuffer 中的内容读出到流中
     */
    public void writeFromBuffer(StringBuffer buffer, OutputStream os) {
        // 用 PrintStream 可以方便的把内容输出到输出流中
        // 其对象的用法和 System.out 一样
        // (System.out 本身就是 PrintStream 对象)
        PrintStream ps = new PrintStream(os);  
        ps.print(buffer.toString());
    }

    /**
     * 3*. 从输入流中拷贝内容到输入流中
     * @throws IOException
     */
    public void copyStream(InputStream is, OutputStream os) throws IOException {
        // 这个读过过程可以参阅 readToBuffer 中的注释
        String line;
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        PrintWriter writer = new PrintWriter(new OutputStreamWriter(os));
        line = reader.readLine();
        while (line != null) {
            writer.println(line);
            line = reader.readLine();
        }
        writer.flush();     // 最后确定要把输出流中的东西都写出去了
                            // 这里不关闭 writer 是因为 os 是从外面传进来的
                            // 既然不是从这里打开的,也就不从这里关闭
                            // 如果关闭的 writer,封装在里面的 os 也就被关了
    }

    /**
     * 3. 调用 copyStream(InputStream, OutputStream) 方法拷贝文本文件
     */
    public void copyTextFile(String inFilename, String outFilename)
        throws IOException {
        // 先根据输入/输出文件生成相应的输入/输出流
        InputStream is = new FileInputStream(inFilename);
        OutputStream os = new FileOutputStream(outFilename);
        copyStream(is, os);     // 用 copyStream 拷贝内容
        is.close(); // is 是在这里打开的,所以需要关闭
        os.close(); // os 是在这里打开的,所以需要关闭
    }

    public static void main(String[] args) throws IOException {
        int sw = 1;     // 三种测试的选择开关
        AccessTextFile test = new AccessTextFile();
       
        switch (sw) {
        case 1: // 测试读
        {
            InputStream is = new FileInputStream("E:\\test.txt");
            StringBuffer buffer = new StringBuffer();
            test.readToBuffer(buffer, is);
            System.out.println(buffer);     // 将读到 buffer 中的内容写出来
            is.close();
            break;
        }
        case 2: // 测试写
        {
            StringBuffer buffer = new StringBuffer("Only a test\n");
            test.writeFromBuffer(buffer, System.out);
            break;
        }
        case 3: // 测试拷贝
        {
            test.copyTextFile("E:\\test.txt", "E:\\r.txt");
        }
            break;
        }
    }

}

posted @ 2007-02-03 21:58 KevinGong 阅读(31380) | 评论 (0)编辑 收藏

2006年10月29日 #

这一章我主要介绍X系统用的主要配置文件XF86Config-4,我采用了对照的方法介绍,一边贴出我的XF86Config-4文件,一边介绍具体的内容。这篇文章对于大家没有什么立杆见影的帮助,不果可以让你对于X的只是有一个基本的了解。

XF86Config-4文件是X系统的主要配置文件。在Redhat 8以前版本中都叫做XF86Config-4这个名字,Redhat 8已经不再叫做这个名字。

编辑这个文件需要小心谨慎一点,因为一点错误,你的X将不能启动。不果没关系啦,改回来就是了学习Linux最好的办法当然还是求助于男人(man),大家有什么问题尽管看看man的帮助就是了。如果你要删除文件中的内容,最好不要直接的删除,而应该在前面加上#符号把它变成注释。

在Redhat 8以前的版本中,X的配置工具是Xconfigurator,在Debian中X的配种方法是:
dpkg-reconfigure xserver-xfree86
当然你都得用root的身份来运行。

在/usr/share/doc/xfree86-common/FAQ.gz文件中你可以看到具体的技巧。


第一段是Files段,这个部分用来配置X系统说能够使用的字体,每一行都代表一个目录,保存了具体的字体和字体的配置信息。
代码:

Section "Files"
        FontPath        "/usr/X11R6/lib/X11/fonts/xp"
   FontPath        "/usr/X11R6/lib/X11/fonts/XChinese"
   FontPath   "unix/:7100"         # 这是本地字体服务器
   # 如果本地字体服务器出了问题,我们可以使用下面的配置
   FontPath   "/usr/lib/X11/fonts/misc"
   FontPath   "/usr/lib/X11/fonts/cyrillic"
   FontPath   "/usr/lib/X11/fonts/100dpi/:unscaled"
   FontPath   "/usr/lib/X11/fonts/75dpi/:unscaled"
   FontPath   "/usr/lib/X11/fonts/Type1"
   FontPath   "/usr/lib/X11/fonts/Speedo"
   FontPath   "/usr/lib/X11/fonts/100dpi"   #这两个字体是每一个X
   FontPath   "/usr/lib/X11/fonts/75dpi"   #系统都必需安装的英文字体
EndSection

下面的是模块段,用来配置X系统加载的模块。
代码:

Section "Module"
   Load   "xtt"      #gtk1使用的字体引擎,效果好,速度稍慢
   Load   "GLcore"   #如果你是用的是Nvidia的显卡,似乎一定要注消掉这一行
   Load   "bitmap"
   Load   "dbe"
   Load   "ddc"
   Load   "dri"
   Load   "extmod"
#   Load   "freetype"   #如果你使用了xtt模块,那么freetype模块就需要注消掉
   Load   "glx"
   Load   "int10"
   Load   "record"
   Load   "speedo"
   Load   "type1"
   Load   "vbe"
EndSection


下面的段是用来配置你的键盘的,属于“输入设备”
代码:

Section "InputDevice"
   Identifier   "Generic Keyboard"   #这是你的键盘的名字,随便你啦
   Driver      "keyboard"      #键盘的驱动…哇,键盘也有驱动
   Option      "CoreKeyboard"      #如果你有多个键盘,那么你需要在这里指定哪一个键盘是主要的键盘
   Option      "XkbRules"   "xfree86"
   Option      "XkbModel"   "pc104"   #键盘的分布格式,一般来说
   Option      "XkbLayout"   "us"   #美国104键盘是大家通用的。
EndSection


这里配置你的鼠标,当然你可以配置两个鼠标,如果你有的话
代码:

Section "InputDevice"
   Identifier   "Configured Mouse"   #鼠标的名字
   Driver      "mouse"         #鼠标的驱动
   Option      "CorePointer"      
   Option      "Device"      "/dev/input/mice"
   #注意,这里很重要,这是鼠标的设备文件
   #我的鼠标是光电鼠标,用的USB接口,对应的鼠标文件是/dev/input/mice
   #如果你的鼠标是普通的滚轮鼠标,用的是PS2接口,那么你应该使用
   #/dev/mouse或者/dev/psaux或者/dev/ttys0这个设备
   Option      "rotocol"      "ImPS/2"
   #这是鼠标的类型,如果不是是滚轮鼠标,那么使用PS/2
   Option      "Emulate3Buttons"   "true"
   #在Linux系统中,鼠标的第三个键非常有用,
   #如果你的鼠标没有第三个键,那么我们应该允许使用双键同时点击来模拟
   Option      "ZAxisMapping"      "4 5"
EndSection


下面的设备是显卡,这是最头痛的设备了,如果你的显卡太新潮,很有可能不能支持哦。Nvidia的GForce2显卡就必需自己编译显卡的驱动程序才能使用
代码:

Section "Device"
   Identifier   "Generic Video Card"
   Driver      "ati"      #如果你是Nivida的显卡,这里应该是"nvidia"
EndSection



这个设备是显示器。
代码:

Section "Monitor"
   Identifier   "Generic Monitor"   #显示器的名字
   HorizSync   30-60         #显示器的频率,一半来说你的显示器
   VertRefresh   50-75         #应该可以达到我的这个水平
                  #因为我的显示器是15"的老显示器了
                  #大家的电脑都比我的好吧?
   Option      "DPMS"
EndSection


下面是综合以上你的配置的设备的各种显示效果
代码:

Section "Screen"
   Identifier   "Default Screen"   #效果的名字
   Device      "Generic Video Card"   #你可以指定你的显卡的名字
   Monitor      "Generic Monitor"   #指定你的显示器的名字
   DefaultDepth   24         #默认的颜色深度
   SubSection "Display"
      Depth      1
      Modes      "1024x768"
   EndSubSection
   SubSection "Display"
      Depth      4
      Modes      "1024x768"
   EndSubSection
   SubSection "Display"
      Depth      8
      Modes      "1024x768"
   EndSubSection
   SubSection "Display"
      Depth      16
      Modes      "1024x768"   #在这里你可以指定扫描频率例如
                  #"1024x768 @ 85"就是用85mhz的频率
   EndSubSection
   SubSection "Display"
      Depth      24
      Modes      "1024x768"
   EndSubSection
EndSection



最终你必需定义下面的段用来告诉X服务器你使用的配置
代码:

Section "ServerLayout"
   Identifier   "Default Layout"   #刚才我们给我们的配置取的名字
   Screen      "Default Screen"   #给我们的效果取的名字
   InputDevice   "Generic Keyboard"   #我们的键盘的名字
   InputDevice   "Configured Mouse"   #我们的鼠标的名字
               #这些名字一定要在前面的配置中已经定义
EndSection

Section "DRI"
   Mode   0666
EndSection



一般来说我们X启动时候会遇到的问题是:
1:no screen found
这有可能是你没有正确的定义所需要的效果,也有可能是你的其他部分定义出错倒置你的效果不能实现
2:xtt和freetype的冲突,注消一个就可以了
3:驱动没有找到,如果你的显卡非常的新潮,那么多半是这个错误了,编译你的驱动吧…

posted @ 2006-10-29 14:26 KevinGong 阅读(193) | 评论 (0)编辑 收藏

现在请输入你的用户名和密码,当然,我们输入root,这样获得一切管理权限!

你一定非常希望立刻看到那些非常漂亮的图形界面,但是也许我要让你失望了。我建议在没有使用图形界面以前,首先熟练的掌握基本的Linux命令,这样才是一个真正的Linuxer。从哪里开始呢?

1. ls 列出文件和目录的命令

你一定很想知道你的电脑里面有哪些东西,现在执行命令ls,啊,怎么什么都没有?当然啦,这是你第一次登录到这个系统,你的默认位置是你的个人目录,而不是系统根目录。你还没有在这个目录里面存放任何的个人文件,当然什么都没有啦。如果你是用root用户登录的话,你的个人目录就是/root目录;如果你是用普通用户登录,比如叫做kris,那么kris的个人目录是/kris。前面的/是什么意思呢?就是“根”的意思,就是最前面的那个目录,在根目录下面建立有很多的子目录,我们在第一章已经讨论过了。

ls命令有很多的选项,常用的是:

-A 选项用来列出所有的文件,包括那些隐藏的文件。为什么我们要隐藏文件呢?道理和你为什么要把情书藏起来不让爸妈发现是一样的。就是为了保密啊。现在执行ls -A看看?是不是有一个隐藏文件“.bashrc”被显示出来啦?聪明的你一定奇怪的发现这个文件名前面有一个点,对!记住,只要文件名前面第一个字符是一个“.”,这个文件就是隐藏文件。一个目录名前面的第一个字符如果是“.”这个目录就是隐藏目录。
-l 这个选项用来显示一个列表,包含了这个目录下面所有的文件的绝大部分属性的列表。你可以每个文件的大小,所有者,你的权限还有修改日期等等。
-R R的意思就是recursive递归,明显这个选项让系统显示出这个目录下面的所有文件以外,还要显示出所有子目录下面的文件。也就是把我们那一大堆水果全部抖出来。
--color 这个选项特别有用,我估计大家的显示器都是彩显吧,什么?你的显示器还是黑白的?天哪!既然是彩显,那么我们可以让ls命令用不同的眼色代表不同的文件类型。比如可执行文件用绿色,普通文件是白色,目录是蓝色。也许你会问,目录也是文件吗?对的,在Linux里面一切都是文件,所有的硬件设备都用一个文件来代替,比如你的软驱,就是用/dev/fd0来代替的。目录也是一个文件。
--help 这个选项几乎是每一个Linux命令都有的,用来显示出该命令的帮助信息。

2. cd 和 mkdir 以及 rm 改变当然所在目录,建立新目录以及删除目录命令

趁热打铁的,刚才说了目录,我们每一次登录都有一个默认目录就是我们的个人用户目录。我们怎么才能到其他的目录去呢?cd就是用来改变当前所在的目录的。前面我们说过,“/”代表根目录,那么执行cd /就可以进入根目录。不试一下吗?
让我们看看根目录下面有哪些文件和子目录吧,执行ls,我们发现,根目录下面有一个目录名子特别变态,叫做usr,进去看看,cd usr,看看这里面有什么?你会发现一个更psycho(变态)的目录叫做src,进入src目录看看?没什么好玩的。那么我们现在回到刚才的usr目录,怎么做?是不是cd usr?执行试一下,好像不行,系统报告出错 cd: usr: No such file or directory。这是怎么搞的?问题在于我们现在所在的目录是/usr/src下,我们执行cd usr的意思是进入/usr/src/usr目录而不是/usr目录。正确的方法是cd /usr。
就好比你在中华美食的箩筐里面看到一个四川的箩筐,里面有一个成都的小箩筐,现在你进入以后发现成都的小箩筐里面有一种叫做“麻辣烫”的很辣的食品。你大饱口福以后想要吃一些甜点,于是准备去福州。你能站在成都的箩筐里面去福州吗?当然不行,福州并不在成都的箩筐里面啊,你应该进入“/中华美食/福州”而不是“/中华美食/四川/成都/福州”对不对?
好的,一个问题出现了,难道我每一次进入一个目录,都要用/usr/src...这么复杂的方式来表示吗?不一定。我们用“..”的方式来表示上一层目录。如果你现在在/usr/src目录下,进入/usr目录有两种办法:cd /usr和cd ..他们是一样的。

怎样才能知道我现在在哪个目录?用命令pwd,这个命令没有什么好说的,执行一次就知道了。

现在我想在我自己的个人目录里面建立一个目录叫做LoveLetter。我应该首先回到我自己的目录,这里有一个简单的方法,就是直接运行cd不带任何参数,这样就可以回到自己的目录,当然也可以cd /root或者cd /home/kris,看你是用什么用户登录的。
进入我自己的目录以后,建立新目录的命令是
mkdir 新目录名
我执行 mkdir LoveLetter 就可以建立一个新的叫做LoveLetter的目录。进入这个目录看看?什么都没有。不着急,慢慢来。我都不着急你急什么?
突然我想起这台电脑我的爸妈也要使用,他们看到我的情书目录怎么办?你忘了刚才我说的可以用加一个点“.”在前面的方法来隐藏目录和文件的?我们可以改变这个目录的名字,但是这个命令我准备等会儿讲,现在我们用一个很无聊的办法来完成这个要求。这个办法就是删掉刚才建立的oveLetter目录在新建一个.LoveLetter目录,之所以说这个办法很无聊,是因为我们现在是在做实验,如果来真的,你原意删掉你的情书吗?是不是另有新欢啦?哈哈。

删除目录的命令其实也可以删除文件,就是rm。
rm 待删除的文件名/目录名
我记得Redhat会提示你是不是真的要删除。按y就是确定,按n就是取消。如果Redhat没有提示你,那么等会请根据我说的方法修改一下系统让它提示咱们。免得以后心痛。删除一个文件很简单。麻烦的是删除一个目录,如果一个目录里面已经有文件,rm是不让直接删除的,你必需先把目录里面的所有文件删除,再删除目录。但是有一个参数可以改变一下,就是 -rf ,这个参数有一定的危险性,因为即使系统本来要提醒一下是不是真的删除目录,加上这个参数也不会有提示了。执行rm 目录 -rf会在一眨眼的时间里面让你的资料下课!
那么我现在就删除LoveLetter目录了:rm LoveLetter -rf
建立一个新的目录mkdir .LoveLetter
现在ls看看,是不是看不到LoveLetter目录了?但是ls -A还是能看到的。所以这种隐藏方式只能偏偏自己,真正让你的文件安全的方式还是以后再讲吧。

3. mv 改变文件名和目录名的命令
cp 复制文件和目录命令
man 命令使用方法参考工具

mv 老文件名 新文件名
mv 老目录名 新目录名
就可以改变文件或者目录的名字。
我现在想要把刚才的这个目录.LoveLetter改名回去,因为这种无聊的隐藏方式很变态,我们有更高级的方法来做这样一件事情:就是不要告诉爸妈你的密码!!!
mv .LoveLetter LoveLetter

cp命令用来把一个文件复制成为一个新的文件,

cp 老文件名 新文件名

这个老文件明和新文件名如果在同一个目录下面,那么当然需要名字不一样,很简单的道理,如果文件名一样何必建立两个文件?如果新老文件在不同的目录,我们就可以让它们有相同的名子。下面的例子说明了这一点:

cp LoveLetter LoveLetter_yesterday 新的文件LoveLetter_yesterday和旧的LoveLetter在同一个目录,所以名子不一样。
cp LoveLetter /home/LoveLetter 新的文件在/home目录下面,但是旧的文件LoveLetter在某一个用户的个人目录下面,当然两者名子可以相同。

cp命令也可以复制整个目录,但是现在我们暂时不讲这么复杂。其实cp还有rm以及ls这些命令不仅是整个Linux的基本命令,更包含了非常多的功能。如果大家有兴趣,可以使用man

man 命令名字

比如man ls,这样就可以看到所有ls命令和参数的详悉解释,尤其是一部分常用的命令的man帮助已经由志愿者翻译了,大家看起来更容易。

一点幽默

好了,说了好多东西了,我想休息一下,给大家说一个有趣的事情,我们说了好多命令和目录的名子,你们是不是觉得有点奇怪。说实在话,我第一次看到usr这个目录时也不知道是什么意思,后来才发现以下对应关系:
usr ->; user
ls ->; list
mkdir ->; make dir
rm ->; remove
src ->; source
mv ->; move
cp ->; copy

是不是很有趣,在UNIX世界,包括Linux世界,人们的想象力就是这么无敌!简写居然能简写成这样子。大家一般的想法是取一个单词的前三个或者前四个字母作为简写,可是UNIX的牛人就是喜欢把move简写成为mv,真不知道他们怎么想的。大家一起捉摸吧

4. nano 和 vi编辑文件的命令 和 cat 以及 more显示文本文件

nano是一个小巧自由,并且友好的编辑器,我认为nano更适合初学Linux的朋友使用。我们现在只学习怎样编辑一个文件以及怎样保存。

nano 文件名

如果你写的文件名已经存在,那么就打开并且编辑,否则就建立一个新的文件。编辑的方法还用说吗?呵呵,当你想要退出的时候,按ctrl+x,nano会问你是不是保存编辑的文件。按Y就是保存,按N就不保存。

nano最大好处在于用户可以不用记忆太多的操作键,大部分常用的功能的操作方法都在屏幕下放列出了。新手需要注意的是“^X”就是按住ctrl键不放再按X的意思。

下面简单的介绍vi。vi是一个非常强大的编辑软件。它太庞大了,足够写一本书专门来讲解。我们这里从使用的角度出发,讲一下vi的用法。
vi有两种模式,一种是命令模式,一种是编辑模式。进入vi以后,默认处于命令模式。

现在我们执行vi LoveLetter。进入以后,按一下键盘上的Insert功能键或者i键可以进入编辑状态,可以插入字符,再按一下Insert变成复盖模式,这两种模式的区别很容易体现,大家尝试一下就可以了。上下左右四个方向键可以移动光标。基本的编辑命令和Windows里面没有区别。是不是很容易呢?当你把需要的内容输入完成以后,我们要保存,这时候按一下ESC键从编辑模式回到命令模式,首先输入一个冒号“:”,也就是按住SHIFT键不放再按分号“;”这样首先输入一个“:”,然后,输入w,回车,就可以保存我们编辑的内容到LoveLetter文件。现在我们按一下Insert就可以继续编辑。再按ESC,输入“:”,再按w又可以保存。可是现在我们不需要保存,我们想要不保存就退出,怎么做呢?当我们输入w的时候是write的意思,保存,那么我们输入q就是quit退出的意思。好,输入q,回车,vi提示我们刚才进行的修改还没有保存,所以记住!一旦需要放弃我们的修改,不能直接用q命令退出,而需要用“q!”命令。输入q!,好了,退出了。
我们想看看我们刚才编辑的LoveLetter是不是真的保存好了,再vi LoveLetter,ok,看到了吧?现在我们想要直接退出,就可以只输入“:q”就可以了,不用输入那个“!”因为我们没有修改文件内容。如果我们修改一下这篇文章,我们在退出的时候可以输入“ESC : wq”就可以了。不需要把w和q分成两次输入。

vi的最最基本用法说到这里差不多了,要是你还想多了解一些vi的知识,在进入vi以后直接按F1就可以了,有详悉的帮助和教学。

其实刚才我们想要看一下编辑的LoveLetter是不是保存好了,不用再vi进去的,只需要用命令

cat LoveLetter

就可以了。cat就是用来显示文本文件内容的命令。如果我们的文本文件很长,一个屏幕显示不完,cat是不会自动分页的。我们可以换用命令

more LoveLetter

more命令显示文本文件时,如果内容过多,会自动的在每一页结束时暂停下来,等到用户按一下空格键再继续。

5. 最重要的命令:halt reboot 关机和重新启动命令

在Linux里面,不能够直接用电源按钮关机,也不能直接用reset按钮重新启动,这对系统,尤其是硬盘有比较大的影响。关机命令是halt,重启动命令是reboot。其实还有shutdown命令完成类似功能,需要的话,请用今天学会的man命令学习使用。
posted @ 2006-10-29 14:17 KevinGong 阅读(306) | 评论 (1)编辑 收藏

2006年9月22日 #

http://www.m-heaven.com/dreamweaver/
posted @ 2006-09-22 14:23 KevinGong 阅读(374) | 评论 (2)编辑 收藏

2006年9月14日 #

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>无标题文档</title>
</head>

<body>
<p>
  <object id="player" style="display:none" height="400" width="400" classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6">
    <param name="invokeURLs" value="-1">
    <param NAME="AutoStart" VALUE="-1">
    <param name="currentPosition" value="0">
    <param name='uiMode' value='mini'>
    <param NAME="url" VALUE="test.mpg">
  </object>
</p>
<input name="submit" type="submit" onclick="getInfo()">
<p>
 
<script language="javascript">
 var time;
  function getInfo(){
  var pl=document.getElementById("player");
   time=pl.currentMedia.durationString;
   alert(parseInt(pl.currentMedia.durationString.substring(0,2)*60));
   //alert(parseInt(pl.currentMedia.durationString.substring(3,5)));
  alert(parseInt(pl.currentMedia.durationString.substring(0,2)*60)+parseInt(pl.currentMedia.durationString.substring(3,5)));
 }
</script>
</p>
</body>
</html>

posted @ 2006-09-14 14:49 KevinGong 阅读(4223) | 评论 (1)编辑 收藏

2006年9月11日 #

    首先,我们必须明确,为什么要使用J2EE?J2EE优点是什么?使用J2EE的主要原因是多层结构,传统的两层C/S结构难于维护,稳定性极差,界面代码和数据库代码混淆在一起,牵一动百,多层结构使得界面和数据库完全分离,并且诞生了中间件这样的技术,如下图:

Web+EJB能组成真正的多层结构

  为什么使用EJB我原先认为这不是一个讨论的话题,因为EJB是J2EE重要的组成部分,可以说没有EJB的J2EE只是一种Web系统,这样的系统非常容易丧失了多层结构的大部分优点(仔细想想那些混合多种层次功能JavaBeans和传统两层结构有什么区别?)。

  当然,可以人为地在Javabeans之间进行层次划分,例如Hibernate算数据持久层,某些JavaBeans是业务核心层,但是因为都是普通JavaBeans,这种划分没有一种强制性和明显标志性,这样的系统更换了主创人员或设计师,可能就会被新的程序员修改得非常混乱。

  我们先看看一个包含EJB的J2EE系统是如何清晰地表达层次。如下图:

  Web完全只是一个MVC模式的实现,关键业务核心是在EJB的服务层实现,这样做的优点是,Web只负责界面相关部分,因为,如果是一个智能客户端,如Swing或J2ME,在不需要修改任何业务核心的情况下能够方便地更换。同样,提供Web Services功能,也只是在 Web层修改,不会涉及EJB方面的修改,同样保证了系统的稳定性,保证了系统升级和未来的扩展性。

  如果不使用EJB,在EJB服务层实现的业务核心将由普通JavaBeans实现,使用何种架构或设计能够保证负责MVC的JavaBeans和负责业务核心的JavaBeans清晰地分开,又如何保证在新的程序员不会破坏和打乱你精心布局的JavaBeans架构?

EJB提供性能优化支持

  最主要的是性能问题,由于以前国内中文Java网站有些人弯曲EJB,认为EJB性能低,其实这是一种非常肤浅错误的认识,我们首先看看在一般Java环境中是如何提高性能。

  假定一个JavaBeans为A,那么一般使用这个JavaBeans命令如下:

  A a = new A();

  但是,在高访问量的环境中,new A()其实是很费时消耗系统性能的,因此,能不能在软件系统启动时候就预先建立一些对象,这样,系统运行时,从这些已经生成的对象池中借用一个,这样,就无需在使用时进行New,节约了开销,提高了性能,因此,真正成熟性能解决方案都是需要对象池等支持。

  在一个纯Web结构的系统(也就是只能运行在Tomat环境中),例如Struts + Hibernate等这样的系统,除非自己动手做,一般是没有对象池技术支持的,因此他们的性能只能算是Demo演示版本的性能,根本无法承受大容量并发访问,也无法称为一个成熟的系统,所以,我们研究成熟的开源Web系统,如Jive、OFBize,LifeRay等,他们都在Web层拥有自己的对象池和缓存池。

  对象池和缓存机制是J2EE必须的吗?当然,是所有成熟系统必须的,Windows系统如果去掉缓存将会变得怎样?

  自己动手开发对象池和缓存机制并不是一件简单的事情,需要对多线程以及同步锁等底层原理有深层次的把握,这其实也是一门非常深入的Java研究分支,所以,你可以抛开你的客户焦急的催促,精心研究开发自己的对象池和缓存池。

  但是,EJB容器(如JBoss)已经提供了对象池和缓存机制,所以,没有事务机制的无状态Session Bean的性能肯定要强于普通JavaBeans。EJB容器不但在单机中提供了对象池和缓存,而且可以跨服务器实现动态负载平衡,这些都无需开发者自己开发任何软件代码,结构如下:

EJB组件能提供真正的可重用框架

  每一个jar包代表一个EJB组件,一个系统可以由多个可重用的EJB组件构成,例如:树形结构EJB组件;自增序号EJB组件;用户资料EJB组件等,这样的EJB组件可以象积木一样搭配在大部分应用系统中,提高了系统的开发效率,保证了开发质量。

  下图是某个新的具体系统时应用到的EJB组件图,在这个新的应用中,由于使用了以前大量可重用的EJB组件,新的开发工作基本集中在界面设计和流程安排上:

EJB提供了事务机制

  事务机制对于一些关键事务是很重要的,例如ATM机提款,提款有多个动作:修改数据库以及数钱等,如果这其中有任何一个环节出错,那么其它已经实现的操作必须还原,否则,就会出现,提款人没有拿到钱,但是卡上已经扣款等不可思议的事情发生。

  EJB提供的事务机制非常周全,但事务机制带来的缺点是性能的降低,因此,有些人认为EJB很重,因为在实际应用中,有的用户系统可能不需要事务机制,只是需要EJB提供的性能优化机制,这样,如果使用EJB,就象叫一个人来背东西,他除了背着我要的东西外,还背着我不要的东西。

  除非你是一个完美主义,在一般企业应用或数据库系统应用中,EJB不会对你构成很重的包袱。

CMP独特的优点

  开源以及一些数据库持久层技术崇拜者,一直抨击CMP,认为CMP慢无用,实际最大的问题是他们的设计和使用问题。

  由于EJB容器(如JBoss)对CMP实现有事务机制的缓存优化,因此,CMP特别适合多个用户同时更新同一个数据源的情况,CMP这种严格的事务完整性保证多个用户同时操作一个数据记录时,能够保证性能优化和数据的完整性,如果这个数据记录是是软件系统的状态标志,它的状态会影响系统中很多的环节,那么状态更改的重要性不言而喻。

  如果没有事务完整性支持,你的软件系统在用户访问量变大,就会变得发生各种不可能发生的逻辑错误,查看程序逻辑是正确的,那么问题出在哪里?出在数据完整性上。

  由于每个CMP在内存中都有一个缓存,在实际应用中,如果使用CMP批量读数据库数据,几万条查询完毕,内存中充满了几万条CMP缓存,如果这时你的EJB容器设置不当(如使用JBoss缺省配置),那么JVM的垃圾回收机制就会频繁启动,导致你的系统变慢甚至死机,这也是一些人抨击CMP慢的原因所在,其实他们使用方法不当,或者没有正确配置EJB容器CMP缓存。

  对于这种情况,根据J2EE核心模式,推荐使用DAO+JDBC方式。

小结

  除非你对设计模式非常精深,能够将自己系统中的JavaBeans使用模式或某种框架进行固定分层,同时,你孜孜不倦研发出对象池,又熟练于JTA等事务机制,你可以选择没有EJB的纯Web结构,就象Jive、OFBiz那样。当然还有一个前提,老板不懂或者非常有挑战性(做与IBM SUN 微软齐名的公司和技术)。

  不要再被TSS那些狂热的开源先生误导,他们有时间有保障可以做他们喜欢的事情,作为专业的J2EE程序员,按照J2EE标准去学习去行动,也不要认为,只要使用了J2EE其中某个技术如Jsp或JavaBeans就心安理得认为自己的系统是J2EE了。

  当然,我并不是说纯Web系统不能实现多层结构,但是至少在很多方面没有Web+EJB结构完善和清晰,所以,EJB不是J2EE可以忽视的部分,而是主要的重要的部分,重要业务功能核心都封装在EJB中,相反Web层是一种次要的、和界面相关的层次。

  补充:什么情况下不需要EJB,在SUN的SECA架构师试卷中回答:小型系统和不需要事务。另外过去那种认为“EJB有性能问题”根本是一种缪误,具体可参考下面有关问题。

posted @ 2006-09-11 14:56 KevinGong 阅读(198) | 评论 (0)编辑 收藏

2006年8月18日 #

java文件上传,介绍几种常用的方法,也是经过本人亲手调试过的
1.jspsmartupload
这个组件用起来是挺方便的,不过就是只适合小文件上传,如果大文件上传的话就不行,查看了一下他的代码,m_totalBytes = m_request.getContentLength(); m_binArray = new byte[m_totalBytes];居然把整个上传文件都读到内存去了,那如果是上传几十M的文件,同时几个用户上传,服务器稳挂,不过如果只是上传5M以内的小文件,这个组件还是挺实用的

下面是源代码:
File类
/*
 * 创建日期 2006-7-29
 *
 * 更改所生成文件模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
package com.kinstar.issuing.file;

/**
 * @author gongyifeng
 *
 * 更改所生成类型注释的模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletException;

// Referenced classes of package com.jspsmart.upload:
// SmartUploadException, SmartUpload

public class File{
 private SmartUpload m_parent;
 private int m_startData;
 private int m_endData;
 private int m_size;
 private String m_fieldname;
 private String m_filename;
 private String m_fileExt;
 private String m_filePathName;
 private String m_contentType;
 private String m_contentDisp;
 private String m_typeMime;
 private String m_subTypeMime;
 private String m_contentString;
 private boolean m_isMissing;
 public static final int SAVEAS_AUTO = 0;
 public static final int SAVEAS_VIRTUAL = 1;
 public static final int SAVEAS_PHYSICAL = 2;

 File(){
  m_startData = 0;
  m_endData = 0;
  m_size = 0;
  m_fieldname = new String();
  m_filename = new String();
  m_fileExt = new String();
  m_filePathName = new String();
  m_contentType = new String();
  m_contentDisp = new String();
  m_typeMime = new String();
  m_subTypeMime = new String();
  m_contentString = new String();
  m_isMissing = true;
 }

 public void saveAs(String s) throws IOException, SmartUploadException{
  saveAs(s, 0);
 }

 public void saveAs(String s, int i) throws IOException, SmartUploadException{
  String s1 = new String();
  s1 = m_parent.getPhysicalPath(s, i);
  if(s1 == null)
   throw new IllegalArgumentException("There is no specified destination file (1140).");
  try
  {
   java.io.File file = new java.io.File(s1);
   FileOutputStream fileoutputstream = new FileOutputStream(file);
   fileoutputstream.write(m_parent.m_binArray, m_startData, m_size);
   fileoutputstream.close();
  }
  catch(IOException ioexception)
  {
   throw new SmartUploadException("File can't be saved (1120).");
  }
 }

 public void fileToField(ResultSet resultset, String s) throws ServletException, IOException, SmartUploadException, SQLException{
  long l = 0L;
  int i = 0x10000;
  int j = 0;
  int k = m_startData;
  if(resultset == null)
   throw new IllegalArgumentException("The RecordSet cannot be null (1145).");
  if(s == null)
   throw new IllegalArgumentException("The columnName cannot be null (1150).");
  if(s.length() == 0)
   throw new IllegalArgumentException("The columnName cannot be empty (1155).");
  l = BigInteger.valueOf(m_size).divide(BigInteger.valueOf(i)).longValue();
  j = BigInteger.valueOf(m_size).mod(BigInteger.valueOf(i)).intValue();
  try
  {
   for(int i1 = 1; (long)i1 < l; i1++)
   {
    resultset.updateBinaryStream(s, new ByteArrayInputStream(m_parent.m_binArray, k, i), i);
    k = k != 0 ? k : 1;
    k = i1 * i + m_startData;
   }
   
   if(j > 0)
    resultset.updateBinaryStream(s, new ByteArrayInputStream(m_parent.m_binArray, k, j), j);
  }catch(SQLException sqlexception){
   byte abyte0[] = new byte[m_size];
   System.arraycopy(m_parent.m_binArray, m_startData, abyte0, 0, m_size);
   resultset.updateBytes(s, abyte0);
  }catch(Exception exception)
  {
   throw new SmartUploadException("Unable to save file in the DataBase (1130).");
  }
 }

 public boolean isMissing(){
  return m_isMissing;
 }
 
 public String getFieldName(){
  return m_fieldname;
 }
 
 public String getFileName(){
  DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
  String date = df.format(new Date());
  
  return date+m_filename;
 }
 
 public String getFilePathName(){
  return m_filePathName;
 }
 
 public String getFileExt(){
  return m_fileExt;
 }
 
 public String getContentType(){
  return m_contentType;
 }
 
 public String getContentDisp(){
  return m_contentDisp;
 }
 
 public String getContentString(){
  String s = new String(m_parent.m_binArray, m_startData, m_size);
  return s;
 }
 
 public String getTypeMIME() throws IOException{
  return m_typeMime;
 }
 
 public String getSubTypeMIME(){
  return m_subTypeMime;
 }
 
 public int getSize(){
  return m_size;
 }
 
 protected int getStartData(){
  return m_startData;
 }
 
 protected int getEndData(){
  return m_endData;
 }
 
 protected void setParent(SmartUpload smartupload){
  m_parent = smartupload;
 }
 
 protected void setStartData(int i){
  m_startData = i;
 }
 
 protected void setEndData(int i){
  m_endData = i;
 }
 
 protected void setSize(int i){
  m_size = i;
 }
 
 protected void setIsMissing(boolean flag){
  m_isMissing = flag;
 }
 
 protected void setFieldName(String s){
  m_fieldname = s;
 }
 
 protected void setFileName(String s){
  m_filename = s;
 }
 
 protected void setFilePathName(String s){
  m_filePathName = s;
 }
 
 protected void setFileExt(String s){
  m_fileExt = s;
 }
 
 protected void setContentType(String s){
  m_contentType = s;
 }
 
 protected void setContentDisp(String s){
  m_contentDisp = s;
 }
 
 protected void setTypeMIME(String s){
  m_typeMime = s;
 }
 
 protected void setSubTypeMIME(String s){
  m_subTypeMime = s;
 }
 
 public byte getBinaryData(int i){
  if(m_startData + i > m_endData)
   throw new ArrayIndexOutOfBoundsException("Index Out of range (1115).");
  if(m_startData + i <= m_endData)
   return m_parent.m_binArray[m_startData + i];
  else
   return 0;
 } 
}

Files类
/*
 * 创建日期 2006-7-29
 *
 * 更改所生成文件模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
package com.kinstar.issuing.file;

/**
 * @author gongyifeng
 *
 * 更改所生成类型注释的模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
import java.io.IOException;
import java.util.*;

// Referenced classes of package com.jspsmart.upload:
// File, SmartUpload

public class Files{

 private SmartUpload m_parent;
 private Hashtable m_files;
 private int m_counter;
 
 Files(){
  m_files = new Hashtable();
  m_counter = 0;
 }
 
 protected void addFile(File file){
  if(file == null)
  {
   throw new IllegalArgumentException("newFile cannot be null.");
  } else {
   m_files.put(new Integer(m_counter), file);
   m_counter++;
   return;
  }
 }
 
 public File getFile(int i)
 {
 if(i < 0)
  throw new IllegalArgumentException("File's index cannot be a negative value (1210).");
 File file = (File)m_files.get(new Integer(i));
 if(file == null)
  throw new IllegalArgumentException("Files' name is invalid or does not exist (1205).");
 else
  return file;
 }
 
 public int getCount()
 {
  return m_counter;
 }
 
 public long getSize() throws IOException
 {
  long l = 0L;
  for(int i = 0; i < m_counter; i++)
  l += getFile(i).getSize();
  
  return l;
 }
 
 public Collection getCollection()
 {
  return m_files.values();
 }
 
 public Enumeration getEnumeration()
 {
  return m_files.elements();
 }
}

Request类
/*
 * 创建日期 2006-7-29
 *
 * 更改所生成文件模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
package com.kinstar.issuing.file;

/**
 * @author gongyifeng
 *
 * 更改所生成类型注释的模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
import java.util.Enumeration;
import java.util.Hashtable;

public class Request
{

 private Hashtable m_parameters;
 private int m_counter;
 
 Request(){
  m_parameters = new Hashtable();
  m_counter = 0;
 }
 
 protected void putParameter(String s, String s1) {
  if(s == null)
   throw new IllegalArgumentException("The name of an element cannot be null.");
  if(m_parameters.containsKey(s))
  {
   Hashtable hashtable = (Hashtable)m_parameters.get(s);
   hashtable.put(new Integer(hashtable.size()), s1);
  } else{
   Hashtable hashtable1 = new Hashtable();
   hashtable1.put(new Integer(0), s1);
   m_parameters.put(s, hashtable1);
   m_counter++;
  }
 }
 
 public String getParameter(String s){
 if(s == null)
  throw new IllegalArgumentException("Form's name is invalid or does not exist (1305).");
 Hashtable hashtable = (Hashtable)m_parameters.get(s);
 if(hashtable == null)
  return null;
 else
  return (String)hashtable.get(new Integer(0));
 }
 
 public Enumeration getParameterNames()
 {
  return m_parameters.keys();
 }
 
 public String[] getParameterValues(String s)
 {
  if(s == null)
   throw new IllegalArgumentException("Form's name is invalid or does not exist (1305).");
  Hashtable hashtable = (Hashtable)m_parameters.get(s);
  if(hashtable == null)
   return null;
  String as[] = new String[hashtable.size()];
  for(int i = 0; i < hashtable.size(); i++)
   as[i] = (String)hashtable.get(new Integer(i));
  
  return as;
 }
}

SmartUpload类
/*
 * 创建日期 2006-7-29
 *
 * 更改所生成文件模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
package com.kinstar.issuing.file;

/**
 * @author gongyifeng
 *
 * 更改所生成类型注释的模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
import java.io.*;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Vector;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;

// Referenced classes of package com.jspsmart.upload:
// Files, Request, SmartUploadException, File

public class SmartUpload
{

 protected byte m_binArray[];
 protected HttpServletRequest m_request;
 protected HttpServletResponse m_response;
 protected ServletContext m_application;
 private int m_totalBytes;
 private int m_currentIndex;
 private int m_startData;
 private int m_endData;
 private String m_boundary;
 private long m_totalMaxFileSize;
 private long m_maxFileSize;
 private Vector m_deniedFilesList;
 private Vector m_allowedFilesList;
 private boolean m_denyPhysicalPath;
 private boolean m_forcePhysicalPath;
 private String m_contentDisposition;
 public static final int SAVE_AUTO = 0;
 public static final int SAVE_VIRTUAL = 1;
 public static final int SAVE_PHYSICAL = 2;
 private Files m_files;
 private Request m_formRequest;
 
 public SmartUpload()
 {
  m_totalBytes = 0;
  m_currentIndex = 0;
  m_startData = 0;
  m_endData = 0;
  m_boundary = new String();
  m_totalMaxFileSize = 0L;
  m_maxFileSize = 0L;
  m_deniedFilesList = new Vector();
  m_allowedFilesList = new Vector();
  m_denyPhysicalPath = false;
  m_forcePhysicalPath = false;
  m_contentDisposition = new String();
  m_files = new Files();
  m_formRequest = new Request();
 }
 
 public final void init(ServletConfig servletconfig) throws ServletException
 {
  m_application = servletconfig.getServletContext();
 }
 
 public void service(HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse)throws ServletException, IOException
 {
  m_request = httpservletrequest;
  m_response = httpservletresponse;
 }
 
 public final void initialize(ServletConfig servletconfig, HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse)throws ServletException
 {
  m_application = servletconfig.getServletContext();
  m_request = httpservletrequest;
  m_response = httpservletresponse;
 }
 
 public final void initialize(PageContext pagecontext)throws ServletException
 {
  m_application = pagecontext.getServletContext();
  m_request = (HttpServletRequest)pagecontext.getRequest();
  m_response = (HttpServletResponse)pagecontext.getResponse();
 }
 
 public final void initialize(ServletContext servletcontext, HttpSession httpsession, HttpServletRequest httpservletrequest, HttpServletResponse httpservletresponse, JspWriter jspwriter) throws ServletException
 {
  m_application = servletcontext;
  m_request = httpservletrequest;
  m_response = httpservletresponse;
 }
 
 public void upload()throws ServletException, IOException, SmartUploadException
 {
  int i = 0;
  boolean flag = false;
  long l = 0L;
  boolean flag1 = false;
  String s = new String();
  String s2 = new String();
  String s4 = new String();
  String s5 = new String();
  String s6 = new String();
  String s7 = new String();
  String s8 = new String();
  String s9 = new String();
  String s10 = new String();
  boolean flag2 = false;
  m_totalBytes = m_request.getContentLength();
  m_binArray = new byte[m_totalBytes];
  int j;
  for(; i < m_totalBytes; i += j)
  try
  {
   m_request.getInputStream();
   j = m_request.getInputStream().read(m_binArray, i, m_totalBytes - i);
  }
  catch(Exception exception)
  {
   throw new SmartUploadException("Unable to upload.");
  }
  
  for(; !flag1 && m_currentIndex < m_totalBytes; m_currentIndex++)
  if(m_binArray[m_currentIndex] == 13)
   flag1 = true;
  else
   m_boundary = m_boundary + (char)m_binArray[m_currentIndex];
  
  if(m_currentIndex == 1)
   return;
  for(m_currentIndex++; m_currentIndex < m_totalBytes; m_currentIndex = m_currentIndex + 2)
  {
   String s1 = getDataHeader();
   m_currentIndex = m_currentIndex + 2;
   boolean flag3 = s1.indexOf("filename") > 0;
   String s3 = getDataFieldValue(s1, "name");
   if(flag3)
   {
    s6 = getDataFieldValue(s1, "filename");
    s4 = getFileName(s6);
    s5 = getFileExt(s4);
    s7 = getContentType(s1);
    s8 = getContentDisp(s1);
    s9 = getTypeMIME(s7);
    s10 = getSubTypeMIME(s7);
   }
   getDataSection();
   if(flag3 && s4.length() > 0)
   {
    if(m_deniedFilesList.contains(s5))
     throw new SecurityException("The extension of the file is denied to be uploaded (1015).");
    if(!m_allowedFilesList.isEmpty() && !m_allowedFilesList.contains(s5))
     throw new SecurityException("The extension of the file is not allowed to be uploaded (1010).");
    if(m_maxFileSize > 0L && (long)((m_endData - m_startData) + 1) > m_maxFileSize)
     throw new SecurityException("Size exceeded for this file : " + s4 + " (1105).");
    l += (m_endData - m_startData) + 1;
    if(m_totalMaxFileSize > 0L && l > m_totalMaxFileSize)
     throw new SecurityException("Total File Size exceeded (1110).");
   }
   if(flag3)
   {
    com.kinstar.issuing.file.File file = new com.kinstar.issuing.file.File();
    file.setParent(this);
    file.setFieldName(s3);
    file.setFileName(s4);
    file.setFileExt(s5);
    file.setFilePathName(s6);
    file.setIsMissing(s6.length() == 0);
    file.setContentType(s7);
    file.setContentDisp(s8);
    file.setTypeMIME(s9);
    file.setSubTypeMIME(s10);
    if(s7.indexOf("application/x-macbinary") > 0)
     m_startData = m_startData + 128;
     file.setSize((m_endData - m_startData) + 1);
     file.setStartData(m_startData);
     file.setEndData(m_endData);
     m_files.addFile(file);
    } else
    {
     String s11 = new String(m_binArray, m_startData, (m_endData - m_startData) + 1);
     m_formRequest.putParameter(s3, s11);
    }
    if((char)m_binArray[m_currentIndex + 1] == '-')
     break;
    }
 
   }
 
 public int save(String s)throws ServletException, IOException, SmartUploadException
 {
  return save(s, 0);
 }
 
 public int save(String s, int i)throws ServletException, IOException, SmartUploadException
 {
  int j = 0;
  if(s == null)
   s = m_application.getRealPath("/");
  if(s.indexOf("/") != -1)
  {
   if(s.charAt(s.length() - 1) != '/')
    s = s + "/";
   } else
   if(s.charAt(s.length() - 1) != '\\')
    s = s + "\\";
   for(int k = 0; k < m_files.getCount(); k++)
   if(!m_files.getFile(k).isMissing())
   {
   m_files.getFile(k).saveAs(s + m_files.getFile(k).getFileName(), i);
   j++;
  }
 
  return j;
 }
 
 public int getSize()
 {
  return m_totalBytes;
 }
 
 public byte getBinaryData(int i)
 {
  byte byte0;
  try
  {
   byte0 = m_binArray[i];
  }
  catch(Exception exception)
  {
   throw new ArrayIndexOutOfBoundsException("Index out of range (1005).");
  }
  return byte0;
 }
 
 public Files getFiles()
 {
  return m_files;
 }
 
 public Request getRequest()
 {
  return m_formRequest;
 }
 
 public void downloadFile(String s) throws ServletException, IOException, SmartUploadException
 {
  downloadFile(s, null, null);
 }
 
 public void downloadFile(String s, String s1) throws ServletException, IOException, SmartUploadException, SmartUploadException
 {
  downloadFile(s, s1, null);
 }
 
 public void downloadFile(String s, String s1, String s2)throws ServletException, IOException, SmartUploadException
 {
  downloadFile(s, s1, s2, 65000);
 }
 
 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=" + getFileName(s));
  else
  if(s2.length() == 0)
   m_response.setHeader("Content-Disposition", m_contentDisposition);
  else
   m_response.setHeader("Content-Disposition", m_contentDisposition + " filename=" + s2);
  while((long)k < l)
  {
   int j = fileinputstream.read(abyte0, 0, i);
   k += j;
   m_response.getOutputStream().write(abyte0, 0, j);
  }
  fileinputstream.close();
 }
 
 public void downloadField(ResultSet resultset, String s, String s1, String s2) throws ServletException, IOException, SQLException
 {
 if(resultset == null)
  throw new IllegalArgumentException("The RecordSet cannot be null (1045).");
 if(s == null)
  throw new IllegalArgumentException("The columnName cannot be null (1050).");
 if(s.length() == 0)
  throw new IllegalArgumentException("The columnName cannot be empty (1055).");
 byte abyte0[] = resultset.getBytes(s);
 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(abyte0.length);
 if(s2 == null)
  m_response.setHeader("Content-Disposition", "attachment;");
 else
 if(s2.length() == 0)
  m_response.setHeader("Content-Disposition", "attachment;");
 else
  m_response.setHeader("Content-Disposition", "attachment; filename=" + s2);
 m_response.getOutputStream().write(abyte0, 0, abyte0.length);
 }
 
 public void fieldToFile(ResultSet resultset, String s, String s1)throws ServletException, IOException, SmartUploadException, SQLException
 {
  try
  {
   if(m_application.getRealPath(s1) != null)
   s1 = m_application.getRealPath(s1);
   InputStream inputstream = resultset.getBinaryStream(s);
   FileOutputStream fileoutputstream = new FileOutputStream(s1);
   int i;
   while((i = inputstream.read()) != -1)
    fileoutputstream.write(i);
   fileoutputstream.close();
  }
  catch(Exception exception)
  {
   throw new SmartUploadException("Unable to save file from the DataBase (1020).");
  }
 }
 
 private String getDataFieldValue(String s, String s1)
 {
  String s2 = new String();
  String s3 = new String();
  int i = 0;
  boolean flag = false;
  boolean flag1 = false;
  boolean flag2 = false;
  s2 = s1 + "=" + '"';
  i = s.indexOf(s2);
  if(i > 0)
  {
   int j = i + s2.length();
   int k = j;
   s2 = "\"";
   int l = s.indexOf(s2, j);
   if(k > 0 && l > 0)
   s3 = s.substring(k, l);
  }
  return s3;
 }
 
 private String getFileExt(String s)
 {
  String s1 = new String();
  int i = 0;
  int j = 0;
  if(s == null)
   return null;
  i = s.lastIndexOf(46) + 1;
  j = s.length();
  s1 = s.substring(i, j);
  if(s.lastIndexOf(46) > 0)
   return s1;
  else
  return "";
 }
 
 private String getContentType(String s)
 {
  String s1 = new String();
  String s2 = new String();
  int i = 0;
  boolean flag = false;
  s1 = "Content-Type:";
  i = s.indexOf(s1) + s1.length();
  if(i != -1)
  {
   int j = s.length();
   s2 = s.substring(i, j);
  }
  return s2;
 }
 
 private String getTypeMIME(String s)
 {
  String s1 = new String();
  int i = 0;
  i = s.indexOf("/");
  if(i != -1)
   return s.substring(1, i);
  else
  return s;
 }
 
 private String getSubTypeMIME(String s)
 {
  String s1 = new String();
  int i = 0;
  boolean flag = false;
  i = s.indexOf("/") + 1;
  if(i != -1)
  {
   int j = s.length();
   return s.substring(i, j);
  } else
  {
   return s;
  }
 }
 
 private String getContentDisp(String s)
 {
  String s1 = new String();
  int i = 0;
  int j = 0;
  i = s.indexOf(":") + 1;
  j = s.indexOf(";");
  s1 = s.substring(i, j);
  return s1;
 }
 
 private void getDataSection()
 {
  boolean flag = false;
  String s = new String();
  int i = m_currentIndex;
  int j = 0;
  int k = m_boundary.length();
  m_startData = m_currentIndex;
  m_endData = 0;
  while(i < m_totalBytes)
  if(m_binArray[i] == (byte)m_boundary.charAt(j))
  {
   if(j == k - 1)
   {
    m_endData = ((i - k) + 1) - 3;
    break;
   }
   i++;
   j++;
  } else
  {
   i++;
   j = 0;
  }
  m_currentIndex = m_endData + k + 3;
 }
 
 private String getDataHeader()
 {
  int i = m_currentIndex;
  int j = 0;
  boolean flag = false;
  for(boolean flag1 = false; !flag1;)
  if(m_binArray[m_currentIndex] == 13 && m_binArray[m_currentIndex + 2] == 13)
  {
   flag1 = true;
   j = m_currentIndex - 1;
   m_currentIndex = m_currentIndex + 2;
  } else
  {
   m_currentIndex++;
  }
  
  String s = new String(m_binArray, i, (j - i) + 1);
  return s;
 }
 
 private String getFileName(String s)
 {
  String s1 = new String();
  String s2 = new String();
  int i = 0;
  boolean flag = false;
  boolean flag1 = false;
  boolean flag2 = false;
  i = s.lastIndexOf(47);
  if(i != -1)
   return s.substring(i + 1, s.length());
  i = s.lastIndexOf(92);
  if(i != -1)
   return s.substring(i + 1, s.length());
  else
  return s;
 }
 
 public void setDeniedFilesList(String s) throws ServletException, IOException, SQLException
 {
  String s1 = "";
  if(s != null)
  {
  String s2 = "";
  for(int i = 0; i < s.length(); i++)
  if(s.charAt(i) == ',')
  {
   if(!m_deniedFilesList.contains(s2))
   m_deniedFilesList.addElement(s2);
   s2 = "";
  } else
  {
   s2 = s2 + s.charAt(i);
  }
  
  if(s2 != "")
   m_deniedFilesList.addElement(s2);
  } else
  {
   m_deniedFilesList = null;
  }
 }
 
 public void setAllowedFilesList(String s)
 {
  String s1 = "";
  if(s != null)
  {
   String s2 = "";
   for(int i = 0; i < s.length(); i++)
   if(s.charAt(i) == ',')
   {
    if(!m_allowedFilesList.contains(s2))
    m_allowedFilesList.addElement(s2);
    s2 = "";
   } else
   {
    s2 = s2 + s.charAt(i);
   }
   
   if(s2 != "")
    m_allowedFilesList.addElement(s2);
  } else
  {
   m_allowedFilesList = null;
  }
 }
 
 public void setDenyPhysicalPath(boolean flag)
 {
  m_denyPhysicalPath = flag;
 }
 
 public void setForcePhysicalPath(boolean flag)
 {
  m_forcePhysicalPath = flag;
 }
 
 public void setContentDisposition(String s)
 {
  m_contentDisposition = s;
 }
 
 public void setTotalMaxFileSize(long l)
 {
  m_totalMaxFileSize = l;
 }
 
 public void setMaxFileSize(long l)
 {
  m_maxFileSize = l;
 }
 
 protected String getPhysicalPath(String s, int i)throws IOException
 {
  String s1 = new String();
  String s2 = new String();
  String s3 = new String();
  boolean flag = false;
  s3 = System.getProperty("file.separator");
  if(s == null)
   throw new IllegalArgumentException("There is no specified destination file (1140).");
  if(s.equals(""))
   throw new IllegalArgumentException("There is no specified destination file (1140).");
  if(s.lastIndexOf("\\") >= 0)
  {
   s1 = s.substring(0, s.lastIndexOf("\\"));
   s2 = s.substring(s.lastIndexOf("\\") + 1);
  }
  if(s.lastIndexOf("/") >= 0)
  {
   s1 = s.substring(0, s.lastIndexOf("/"));
   s2 = s.substring(s.lastIndexOf("/") + 1);
  }
  s1 = s1.length() != 0 ? s1 : "/";
  java.io.File file = new java.io.File(s1);
  if(file.exists())
   flag = true;
  if(i == 0)
  {
   if(isVirtual(s1))
   {
    s1 = m_application.getRealPath(s1);
    if(s1.endsWith(s3))
     s1 = s1 + s2;
    else
    s1 = s1 + s3 + s2;
     return s1;
   }
   if(flag)
   {
    if(m_denyPhysicalPath)
     throw new IllegalArgumentException("Physical path is denied (1125).");
    else
     return s;
   } else
   {
    throw new IllegalArgumentException("This path does not exist (1135).");
   }
  }
  if(i == 1)
  {
   if(isVirtual(s1))
   {
    s1 = m_application.getRealPath(s1);
   if(s1.endsWith(s3))
    s1 = s1 + s2;
   else
    s1 = s1 + s3 + s2;
   return s1;
  }
  if(flag)
   throw new IllegalArgumentException("The path is not a virtual path.");
  else
   throw new IllegalArgumentException("This path does not exist (1135).");
  }
  if(i == 2)
  {
   if(flag)
   if(m_denyPhysicalPath)
    throw new IllegalArgumentException("Physical path is denied (1125).");
   else
    return s;
   if(isVirtual(s1))
    throw new IllegalArgumentException("The path is not a physical path.");
   else
   throw new IllegalArgumentException("This path does not exist (1135).");
  } else
  {
   return null;
  }
 }
 
 public void uploadInFile(String s)throws IOException, SmartUploadException
 {
  int i = 0;
  int j = 0;
  boolean flag = false;
  if(s == null)
   throw new IllegalArgumentException("There is no specified destination file (1025).");
  if(s.length() == 0)
   throw new IllegalArgumentException("There is no specified destination file (1025).");
  if(!isVirtual(s) && m_denyPhysicalPath)
   throw new SecurityException("Physical path is denied (1035).");
  i = m_request.getContentLength();
  m_binArray = new byte[i];
  int k;
  for(; j < i; j += k)
  try
  {
   k = m_request.getInputStream().read(m_binArray, j, i - j);
  }
  catch(Exception exception)
  {
   throw new SmartUploadException("Unable to upload.");
  }
  
  if(isVirtual(s))
   s = m_application.getRealPath(s);
  try
  {
   java.io.File file = new java.io.File(s);
   FileOutputStream fileoutputstream = new FileOutputStream(file);
   fileoutputstream.write(m_binArray);
   fileoutputstream.close();
  }
  catch(Exception exception1)
  {
   throw new SmartUploadException("The Form cannot be saved in the specified file (1030).");
  }
 }
 
 private boolean isVirtual(String s)
 {
  if(m_application.getRealPath(s) != null)
  {
   java.io.File file = new java.io.File(m_application.getRealPath(s));
   return file.exists();
  } else
  {
   return false;
  }
 }
}

SmartUploadException 类
/*
 * 创建日期 2006-7-29
 *
 * 更改所生成文件模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
package com.kinstar.issuing.file;

/**
 * @author gongyifeng
 *
 * 更改所生成类型注释的模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
public class SmartUploadException extends Exception
{

 SmartUploadException(String s)
 {
  super(s);
 }
}

上传的Servlet
package com.kinstar.issuing.action;

import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.*;
import java.sql.SQLException;
import java.util.*;
import java.text.*;
import javax.servlet.*;
import javax.servlet.http.*;


import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.kinstar.issuing.file.File;
import com.kinstar.issuing.file.SmartUpload;
import com.kinstar.issuing.objects.t_user;
import com.kinstar.issuing.operation.UserOperation;
import com.kinstar.issuing.program.programService;
import com.kinstar.issuing.session.SessionGloble;
import com.kinstar.issuing.util.StringUtil;

/**
 * @version  1.0
 * @author gyf
 */


public class upload2ProgramAction extends HttpServlet{
  private ServletConfig config;
  /**
  * 初始化Servlet
  */
  final public void init(ServletConfig config) throws ServletException {
  this.config = config;
  }
  /**
  * 处理GET请求
  */
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   doPost(request,response);
  }
 
  /**
  * 响应POST请求
  */
  public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
   int count=0;
   SmartUpload mySmartUpload = new SmartUpload();
   try {
     // 初始化
     mySmartUpload.initialize(config,request,response);
 
     // 上载
     mySmartUpload.upload();
     com.kinstar.issuing.file.File f1 = mySmartUpload.getFiles().getFile(0);
   //  com.kinstar.issuing.file.File f2 = mySmartUpload.getFiles().getFile(1);
     String backPic = f1.getFileName();
   //String name2 = f2.getFileName();
   
     long size=0;
     
    // 保存上载文件到指定目录
   count=mySmartUpload.save("ads");  
   response.sendRedirect("program.jsp?dopass=ture");
  
    }     

     
    catch (Exception e){
     response.sendRedirect("fail.jsp");
   } 
}

2.common-fileupload组件
挺好用的,也能够上传大文件,我试过,300M以上的文件上传本地传非常快,异地测试也能够上传成功.
首先要下载org.apache.commons.fileupload包和org.apache.commons.io包

下面是我的servlet
package com.kinstar.issuing.action;

import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.*;
import java.sql.SQLException;
import java.util.*;
import java.text.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.regex.*;


import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;

import com.kinstar.issuing.objects.t_user;
import com.kinstar.issuing.operation.UserOperation;
import com.kinstar.issuing.program.programService;
import com.kinstar.issuing.session.SessionGloble;
import com.kinstar.issuing.util.StringUtil;

/**
 * @version  1.0
 * @author gyf
 */


public class uploadProgramAction extends HttpServlet{
  private static final String CONTENT_TYPE = "text/html; charset=GB2312";
 
  /**
  * 处理GET请求
  */
  public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    doPost(request,response);
  }
 
  /**
  * 响应POST请求
  */
  public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   // 变量定义
    response.setContentType(CONTENT_TYPE);
    HttpSession modifysession=request.getSession();
    SessionGloble logonUser;
    logonUser=(SessionGloble)modifysession.getAttribute("UserInfo");
       if(logonUser==null){
       response.sendRedirect("mainindex.jsp");
    }
    t_user userinfo=new t_user();
    UserOperation user=null;
    try {
     user = new UserOperation();
    } catch (Exception e1) {
    // TODO 自动生成 catch 块
    e1.printStackTrace();
    }
    try {
     userinfo=user.getUser(logonUser.getUserId());
    } catch (Exception e2) {
    // TODO 自动生成 catch 块
    e2.printStackTrace();
    }
     //System.out.println("figure="+userinfo.getUserFigure());
     PrintWriter out=response.getWriter();
     DateFormat updf = new SimpleDateFormat("yyyyMMddHHmm");
     String updateTime = updf.format(new Date());
     int isNeed = 0;
     String IsCheck="0";
  
    //省农行用户上传的节目必需显示,且审批已经合格
     if(userinfo.getUserFigure().equals("1")){
     isNeed = 1;
     IsCheck = "1";
     }
     else{
     isNeed = 0;
     IsCheck = "0";
     }
    int type=0;
    String avaTime="";
    String screen="";
    int fileTime=0;
    int fileTimeReal=0;
    int circle=0;
    String picSwitch="";
    String deleState="1";
    String backPic="";
   
    String fieldName="";
    String finalName="";
    String fileNameReal="";
    long size=0;
    String name="";
     try {
    DiskFileUpload fu = new DiskFileUpload();
    // 设置允许用户上传文件大小,单位:字节,这里设为2m
    fu.setSizeMax(5*1024*1024*1024);
    // 设置最多只允许在内存中存储的数据,单位:字节
    fu.setSizeThreshold(10*1024*1024);
    // 设置一旦文件大小超过getSizeThreshold()的值时数据存放在硬盘的目录
    fu.setRepositoryPath("C:\\WINDOWS\\Temp\\");
    //开始读取上传信息
    List fileItems = fu.parseRequest(request);
    //依次处理每个上传的文件
    Iterator iter = fileItems.iterator();
 
  
       //正则匹配,过滤路径取文件名
    String regExp=".+\\\\(.+)$";
 
    //过滤掉的文件类型
    String[] errorType={".exe",".com",".cgi",".asp"};
     
    Pattern p = Pattern.compile(regExp);
    StringUtil su = new StringUtil();
      
    while (iter.hasNext()) {
      FileItem item = (FileItem)iter.next();
    
       if(item.isFormField()) {
       // 获得表单域的名字
        fieldName = item.getFieldName();
        // 如果表单域的名字是name…
        if(fieldName.equals("type"))
            type = Integer.parseInt(item.getString());
      
       }
      if (!item.isFormField()) {
       name = item.getName();
       size = item.getSize();
       if((name==null||name.equals("")) && size==0)
        continue;
        Matcher m = p.matcher(name);
        boolean result = m.find();
        if (result){
        for (int temp=0;temp<errorType.length;temp++){
        if (m.group(1).endsWith(errorType[temp])){
        throw new IOException(name+": wrong type");
        }
        }
      
       DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
                String date = df.format(new Date());
       fileNameReal=date+m.group(1);
       finalName=date+Math.round(Math.random()*10000)+fileNameReal.substring(fileNameReal.indexOf("."));
        //保存上传的文件到指定的目录  
        //在下文中上传文件至数据库时,将对这里改写
        item.write(new File(getServletContext().getRealPath(".//ads//")+finalName));
        //out.print(finalName+size);
        }
       else
       {
      throw new IOException("fail to upload");
      
       } 
      }
      if(item.isFormField()) {
     // 获得表单域的名字
     fieldName = item.getFieldName();
     if(fieldName.equals("avaTime"))
      avaTime=item.getString();
     if(fieldName.equals("screen"))
      screen=item.getString();
     if(fieldName.equals("fileTime"))
      fileTime = Integer.parseInt(item.getString());
     if(fieldName.equals("fileTimeReal"))
      fileTimeReal = Integer.parseInt(item.getString());
     if(fieldName.equals("circle"))
      circle = Integer.parseInt(item.getString()); 
     if(fieldName.equals("switchPic"))
        picSwitch = item.getString(); 
      
      }   
       }
     }catch (IOException e){
       out.println(e);
     }catch (FileUploadException e){
       out.println(e);
     } catch (Exception e) {
    // TODO 自动生成 catch 块
     e.printStackTrace();
     }
     if(finalName.equals("")){
       response.sendRedirect("fail.jsp");
     }
     else{
   try {
    programService ps = new programService();
    ps.insertProgram(userinfo.getUserId(),updateTime,type,finalName,size,isNeed,avaTime,deleState,IsCheck,userinfo.getCity(),backPic,screen,fileTime,fileTimeReal,picSwitch,circle,userinfo.getUserFigure(),new String(fileNameReal.getBytes("GB2312"),"ISO8859-1"));
    response.sendRedirect("program.jsp?dopass=true"); 
   } catch (Exception e3) {
   // TODO 自动生成 catch 块
    e3.printStackTrace();
   }
     }
   }
}
 

posted @ 2006-08-18 16:29 KevinGong 阅读(927) | 评论 (1)编辑 收藏

2006年8月6日 #

最近做一个cts,其中有一个定时报警的需求,网上找了点资料参考!

如何在Web工程中实现任务计划调度

     好多朋友用过Windows的任务计划,也有不少程序迷自己曾写过时钟报警、系统自动关机等趣味程序,可却很少有朋友在Web工程中实现过类似功能。今天有空把笔者先前曾在Tomcat上实现的类似功能,搬出来与大家共享。
     早在几年前,我公司跟某市财政局合作项目开发,为加强财政局对所属单位财务状况的有效监管,开发、实施了财政局数据中心项目。此项目采用B/S加C/S混合结构模式。财政局Web服务器上架设数据同步接收装置,由市属单位每天下班前把财务信息通过HTTP协议上传至财政局中心服务器,与Web服务器上的接收装置对接。财政局内部各部门需要查阅大量财务信息,获取完备的市属单位当前财务状况信息,各部门按职能划分,需要准确的获取各部门各自所关注的汇总信息,以财政报表的形式提供。
    因财政数据量大,实时计算财政报表速度较慢,当初就考虑用报表缓存来减轻服务器的负担,但用缓存需要一个合理的缓存更新机制。考虑到各市属单位每天下班前才把财务数据上传,财政局每天所查看到的财务信息其实并不包括当天(除非有某位领导等到所属单位全部上传完之后才来查看信息,应该已经下班了),所以要是能实现任务计划调度,在每晚深夜把当天及历史财务信息汇总,更新缓存,速度瓶颈不就解决了吗。
    当时由于系统核心是基于Web部署的,报表计算引擎也相应的部署在Tomcat容器上,因此如果想要借用Windows的任务计划来实现定时计算,就需要额外编写普通桌面应用程序接口,稍显复杂。于是就琢磨着想在Web上实现,经过查阅较多相关资料,发现Java定时器(java.util.Timer)有定时触发计划任务的功能,通过配置定时器的间隔时间,在某一间隔时间段之后会自动有规律的调用预先所安排的计划任务(java.util.TimerTask)。另外,由于我们希望当Web工程启动时,定时器能自动开始计时,在整个Web工程的生命期里,定时器能在每晚深夜触发一次报表计算引擎。因此定时器的存放位置也值得考查,不能简单的存在于单个Servlet或JavaBean中,必须能让定时器宿主的存活期为整个Web工程生命期,在工程启动时能自动加载运行。结合这两点,跟Servlet上下文有关的侦听器就最合适不过了,通过在工程的配置文件中加以合理配置,会在工程启动时自动运行,并在整个工程生命期中处于监听状态。
    下面就Servlet侦听器结合Java定时器来讲述整个实现过程。要运用Servlet侦听器需要实现javax.servlet.ServletContextListener接口,同时实现它的contextInitialized(ServletContextEvent event)和contextDestroyed(ServletContextEvent event)两个接口函数。考虑定时器有个建立和销毁的过程,看了前面两个接口函数,就不容置疑的把建立的过程置入contextInitialized,把销毁的过程置入contextDestroyed了。
    我把ServletContextListener的实现类取名为ContextListener,在其内添加一个定时器,示例代码如下所示(为考虑篇幅,仅提供部分代码供读者参考):


    private java.util.Timer timer = null;
    public void contextInitialized(ServletContextEvent event) {
        timer = new java.util.Timer(true);
        event.getServletContext().log("定时器已启动");        
         timer.schedule(new MyTask(event.getServletContext()), 0, 60*60*1000);
        event.getServletContext().log("已经添加任务调度表");
    }
    public void contextDestroyed(ServletContextEvent event) {
        timer.cancel();
        event.getServletContext().log("定时器销毁");
    }

    以上代码中, timer.schedule(new MyTask(event.getServletContext()), 0, 60*60*1000)这一行为定时器调度语句,其中MyTask是自定义需要被调度的执行任务(在我的财政数据中心项目中就是报表计算引擎入口),从java.util.TimerTask继承,下面会重点讲述,第三个参数表示每小时(即60*60*1000毫秒)被触发一次,中间参数0表示无延迟。其它代码相当简单,不再详细说明。
   下面介绍MyTask的实现,上面的代码中看到了在构造MyTask时,传入了javax.servlet.ServletContext类型参数,是为记录Servlet日志方便而传入,因此需要重载MyTask的构造函数(其父类java.util.TimerTask原构造函数是没有参数的)。在timer.schedule()的调度中,设置了每小时调度一次,因此如果想实现调度任务每24小时被执行一次,还需要判断一下时钟点,以常量C_SCHEDULE_HOUR表示(晚上12点,也即0点)。同时为防止24小时执行下来,任务还未执行完(当然,一般任务是没有这么长的),避免第二次又被调度以引起执行冲突,设置了当前是否正在执行的状态标志isRunning。示例代码如下所示:


    private static final int C_SCHEDULE_HOUR   = 0;
    private static boolean isRunning = false;
         private ServletContext context = null;
    public MyTask(ServletContext context) {
        this.context = context;
    }
    public void run() {
        Calendar cal = Calendar.getInstance();        
        if (!isRunning)  {           
            if (C_SCHEDULE_HOUR == cal.get(Calendar.HOUR_OF_DAY)) {            
                    isRunning = true;                
                context.log("开始执行指定任务");
                
                //TODO 添加自定义的详细任务,以下只是示例
                int i = 0;
                while (i++ < 10) {
                    context.log("已完成任务的" + i + "/" + 10);
                }

                isRunning = false;
                context.log("指定任务执行结束");               
            }            
        } else {
            context.log("上一次任务执行还未结束");
        }
    }

    上面代码中“//TODO……”之下四行是真正被调度执行的演示代码(在我的财政数据中心项目中就是报表计算过程),您可以换成自己希望执行的语句。
到这儿,ServletContextListener和MyTask的代码都已完整了。最后一步就是把ServletContextListener部署到您的Web工程中去,在您工程的web.xml配置文件中加入如下三行:
   
        com.test.ContextListener
    
    当然,上面的com.test得换成您自己的包名了。保存web.xml文件后,把工程打包部署到Tomcat中即可。任务会在每晚12点至凌晨1点之间被执行,上面的代码会在Tomcat的日志文件中记录如下:
2003-12-05 0:21:39 开始执行指定任务
2003-12-05 0:21:39 已完成任务的1/10
    ……
2003-12-05 0:21:39 已完成任务的10/10
2003-12-05 0:21:39 指定任务执行结束

posted @ 2006-08-06 21:48 KevinGong 阅读(190) | 评论 (0)编辑 收藏

2006年7月27日 #

http://www.21tx.com/dev/2004/02/14/19372_3.html
posted @ 2006-07-27 21:00 KevinGong 阅读(302) | 评论 (1)编辑 收藏

最近一个项目要用到JMF,所以搜集了点资料,以供自己学习!
内容表格
1. 关于此指南
2. 一个简单的音频播放器
3. JMF用户接口组件
4. JMF概念
5. 传播和接收媒体
6. 总结以及资源



第一节. 关于此指南

此指南包含的内容?

Java媒体架构(JMF)是一个令人激动的通用的API,它允许Java开发者用许多不同的方法处理媒体。本指南主要通过使用工作的例子提供一个JMF的一些主要的特征的概述。阅读完本指南后,你将会明白JMF体系结构中的主要播放功能。你同样能正确的使用JMF,使用现存的例子和可为更多特殊功能扩展的源代码。

本指南包含着以下主题:
· 下载和安装JMF
· 主要的JMF类以及它们在JMF体系结构中的应用
· 播放本地的媒体文件
· 为媒体的存取和操作制作以和图形用户界面(GUI)
· 通过网络传播媒体
· 通过网络接收媒体

几乎所有的媒体类型的操作和处理都可以通过JMF来实现。全面的讨论JMF所提供的所有特征已经超过了本指南的范围,我们将使用三个简单的媒体应用程序来学习此框架的构建模块。通过这个方法,本指南将为你未来学习和实施更多特殊的应用提供准备。
我应该使用此指南吗?

本指南会带你学习使用JMF工作的基础。为完成这些,我们会创建三个的独立工作的例程序。每个例子都会建立前一个例子的基础上,显示JMF功能性的不同方面。
在本指南中的例子假定你曾经使用过并且已经熟悉了Java程序语言。除了Java核心和JMF的类之外,我们会使用一些Java AWT和Swing类(用于创建GUI),也会有一些Java网络类(用于在网络中传输媒体)。对GUI和网络类一些熟悉有助于你更快的明白观点和这里的例子,但并非是阅读本指南必须的。

我们将学习的例程序如下
· 一个简单的音频播放器(JMF的HelloWorld应用):这个字符界面的播放器通过在命令行中简单的输入媒体文件的名字就可以播放大多数的音频类型。此音频播放器的演示大体上显示了JMF的特有的类。
· 一个图形界面的媒体播放器:我们将使用JMF内置的接口组件来建立图形界面,所以在此练习中必须有一些图形界面的编程经验。这个媒体阅览器演示使用了一些Java AWT和Swing类来为用户显示图形组件。
· 一个媒体广播应用:此应用程序允许一个本地媒体文件通过网络传播。此程序能灵活的使媒体只传输到指定的网络节点,或者传输到一个子网络中的所有节点。此演示使用了一些Java的网络APIs来在网络中传输媒体。
作为第三个练习的一部分,我们将修改图形界面的播放器,让其能接收并且播放媒体。
跳至23页观看Resources,文章,指南,和其他参考书目的列表,这会帮助你学习到更到关于此指南包括的主题。

安装需求
要运行此指南中的例程序,你需要如下的工具和组件:
·  Java 2 平台,标准版,编译和运行演示程序
·  Java媒体框架,版本2.1.1a或者更高
· 一块已经安装并且配置号的适当的声卡
· 一台或者多台测试机器
· 演示的源代码文件在mediaplayer.jar中
最后的一个演示应用显示了JMF在网络中的应用。如果需要,此演示能运行在一个独立的机器上,使用此机器即是传输方也是接收方。可是要观察到在网络中使用JMF的所有功能,你仍然需要至少两台联网的机器。
在23页中的Resources可下载Java 2平台,完整的源代码文件,以及其他一些完成本指南所需要的工具。

下载安装文件
将JMF安装到你的计算机中的第一步是在JMF的主页中下载安装文件,它同样包括了JMF源代码和API文档的链接。23页的Resources中有下载JMF的链接。
目前,JMF有Windows, Solaris, Linux等版本,以及可运行在任何装有虚拟机的计算机上一个纯Java版本。为了增加性能,你需要下载一个与你操作系统所适应的版本。任何在一个操作系统JMF版本下书写和编译的代码都可以方便的移植到另外的操作系统上。例如,如果你下载了一个Solaris版本的JMF并且编译了一个类,这些类就可以在Linux上使用,不会有任何问题。
作为选择,你可以选择下载纯Java版本,或者跨平台版本的JMF。这些版本没有使用操作系统特有的库文件。如果没有合适的JMF版本适合的操作系统,那么跨平台版本就是一个不错的选择。

安装JMF
下载完JMF安装程序后,双击安装程序的图标。
大部分安装程序都会有个选项,安装本地库到系统目录中;例如,Windows版本安装程序会有一个选项“Move DLLs to Windows/System directory.”。最好将此选项选中,因为它能确保这些操作系统的库文件能正确的安装
在安装的过程中,你还需要选择项目来更新系统的CLASSPATH和PATH变量。如果这些选项被关闭,那么在你编译和运行本指南的例程序的时候就需要在classpath中引入JMF的jar文件。

关于作者
Eric Olson在Retek Inc工作的软件工程师。它在Java平台上有四年的工作经验,并且在不同的基于Java的技术上富有经验,包括JMF, Jini, Jiro, JSP, servlets, and EJBs。Eric毕业于St. Paul, MN的St. Thomas大学,获得计算机科学的学位。他在IBM的SanFrancisco项目组工作,负责WebSphere商业组件。他同时再为Imation Corp.工作,负责存储应用。现在,他正在开发零售行业的基于web的软件解决方案。再业余的时间,Eric和Paul
Monday在Stereo Beacon上合作—一个分布式的点对点的基于JMF的媒体播放器。联系
Eric zpalffy@yahoo.com.

第二节. 一个简单的音频播放器
浏览
在本节中,我们将进行创建一个简单的音频播放器的第一个练习。本例将介绍Manager类和Player接口,中两个都是建立大多数基于JMF应用的重要部分。
本例的功能目标是在字符界面下播放本地的音频文件。我们将学习此源代码,并了解每一行所做的任务。完成本节后,你将会有一个基于JMF的可播放包括MP3, WAV, AU等多种音频文件的演示程序。
在本练习后的源代码分类种可查询文件SimpleAudioPlayer.java。

引入必要的类
SimpleAudioPlayer类中包括了一些调用,在其前几行中需要引入所有必要的类:
import javax.media.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;

The javax.media包是由JMF定义的多个包之一。javax.media是一个核心包,包括了定义Manager类和Player接口等。本节中,我们主要学习Manager类和Player接口,其余的javax.media类放在后面的章节中。
除了引入javax.media声明外,以上的代码片断引入了一些创建媒体播放器的输入的声明。

Player接口
在下面的代码片断中,创建一个公共类SimpleAudioPlayer并举例定义一个Player变量:

public class SimpleAudioPlayer {
private Player audioPlayer = null;


术语Player听起来由点熟悉,因为它是建立在我们公用的音频或者视频播放器的基础上的。事实上,这个接口的例子就像是当作它们的真实的副本。Players揭示了一个实体上的媒体播放器(如立体音箱系统或者VCR)涉及到功能上的方法。例如,一个JMF媒体播放器可以开始和结束一个媒体流。在本节种,我们将使用Player的开始和结束功能。

在一个文件上创建一个Player
使用JMF获得一个特定媒体文件的Player实例非常简单。Manager类在JMF中如同一个工厂制作许多的特殊接口类型,包括Player接口。因此,Manager类的责任就是创建Player实例,如下例:

public SimpleAudioPlayer(URL url) throws IOException,
NoPlayerException,
CannotRealizeException {
audioPlayer = Manager.createRealizedPlayer(url);
}
public SimpleAudioPlayer(File file) throws IOException,
NoPlayerException,
CannotRealizeException {
this(file.toURL());
}



如果你看完本节的代码,你可以注意到Manager类包含了创建一个Player实例的其他方法。我们会研究其中的一些,如在后面的章节中的DataSource或者MediaLocator的实例化。

Player的状态
JMF定义了大量的一个Player实例可能存在的不同状态。如下:
· Prefetched
· Prefetching
· Realized
· Realizing
· Started
· Unrealized

使用这些状态
因为使用媒体常常是资源非常密集的,由JMF对象揭示的许多方法都是不闭塞的,允许一系列事件监听的状态改变的异步通知。例如,一个Player在它可以启动之前,必须经过Prefetched和Realized状态。由于这些状态的改变都需要一些时间来完成,JMF媒体应用可以分配一个线程来初始化创建Player实例,然后再继续其他的操作。当Player准备就绪的时候,它会通知应用程序其状态已经改变。

在一个如同我们的这样简单的程序中,多功能性的类型并不是很重要。处于这个原因,Manager类也提供了一些创建Realized player的有用方法。调用一个createRealizedPlayer()方法来阻塞调用线程,直到player达到Realized状态。为了调用一个无阻塞的创建player的方法,我们在Manager类中使用了一个createPlayer()方法。下面的一行代码中创建了一个我们需要在例程序中使用的

Realized player:
audioPlayer = Manager.createRealizedPlayer(url);



启动和停止Player
设定一个Player实例的启动或是停止就如同调用Player的一个简单的认证方法,如下所示:

public void play() {
audioPlayer.start();
}
public void stop() {
audioPlayer.stop();
audioPlayer.close();
}


调用SimpleAudioPlayer类中的play()方法来实现调用Player实例的start()方法。调用此方法后,你能听到本地的喇叭的声音文件。同样的,stop()方法使player停止并且关闭掉Player对象。

对于读取和或者播放本地媒体文件来说,关闭Player实例释放所有资源是一个有用的方法。因为这是一个简单的例子,关闭Player是终止一个会话可接受的方法。但是在实际的应用中,你需要小心的确认在除掉Player之前必须要关闭掉。一但你已经关闭掉player,在再次播放一个媒体之前你必须要创建一个新的Player实例(等待它的状态改变)。

建立一个SimpleAudioPlayer

最后,这个媒体播放应用程序要包含一个可以从命令提示行中输入命令而调用的main()方法。在此main()方法中,我们将调用创建SimpleAudioPlayer的方法:

File audioFile = new File(args[0]);
SimpleAudioPlayer player = new SimpleAudioPlayer(audioFile);



在播放音频文件之前的唯一的一些事情就是调用已经创建的音频player的方法play(),如下所示:

player.play();



要停止和清除掉音频player,在main()方法中也应该有如下调用:

player.stop();



编译和运行SimpleAudioPlayer
通过在命令提示行输入javac SimpleAudioPlayer.java来编译例程序。所创建的文件SimpleAudioPlayer.class在当前工作目录中。
然后在命令提示行中键入如下命令来运行例程序:

java SimpleAudioPlayer audioFile


将audioFile替换成你本地机器上的音频文件。所有的相对文件名都试相对于当前的工作目录。你会看到一些当前正在播放文件的标志信息。要终止播放,按下回车键。
如果编译失败,确认JMF的jar文件已经正确的包含在CLASSPATH环境变量中。


第三节. JMF用户界面组件
播放视频
在前一节中,我们学习了建立一个通过字符界面播放音频文件的应用程序。JMF中一个最重要的特点就是你不需要为了配置媒体播放器而去了解媒体文件的格式;一切都内置了。举一个例子,再我们前面的例子中,需要使用MP3格式的时候,我们不需要让应用程序为一个MP3文件建立一个特殊的Player。
如同你将会再本节所见到的,对于视频文件的操作同样有效。JMF有所有媒体文件类型接口的详细资料。
处理视频媒体与音频最大的不同就是,我们必须建立一个能播放视频的显示屏幕。幸运的是,JMF能处理许多的这些资料。如同再上例一样我们会建立一个Player对象,并且使用很多的可视组件来直接从JMF对象中创建我们的可视的媒体浏览器。
本节中,我们将学习两个例程序。In this section, we'll walk through the second example application. 请再后面的练习的源代码分布中查阅MediaPlayerFrame.java。

关于例子
在本节中,我们将创建一个能显示和运行本地音频和视频媒体的应用程序。作为练习的一部分,我们将研究JMF内置的一些GUI组件。熟悉AWT和Swing将有助于你理解本例,但这并不是必须的。除非需要直接涉及到JMF的GUI组件,或者我们是不会详细介绍源代码的。你可以在源代码的注释中找到这里未涉及的详细说明。
本例中我们使用的许多概念,类和方法都和第一个例子的类似。建立Player的基本操作大都一样。最大的不同就是我们需要对Player对象专研更深一点,特别当需要从Player获取媒体信息的时候。

如何开始
视频播放器例子被设计得如同音频播放例子一样通过命令行来运行,但是本例需要建立在GUI基础上。如同在上节一样,我们先通过媒体文件名调用应用。然后,应用程序显示一个带有可操作媒体组件的窗体。
在MediaPlayerFrame开始的一行中我们定义了类并扩展自,javax.swing.Jframe类。这就是使媒体播放器如同一个在桌面上的单独窗体的方法。任何客户机程序创建了本媒体播放对象后都可以通过调用Jframe类中定义的show()方法来显示。
下面是一个MediaPlayerFrame正在播放MPEG电影的屏幕截图:

获取GUI组件
Player界面有一些方法来获取已选择可视组件的涉及。在MediaPlayerFrame中,我们使用如下组件:
· player.getVisualComponent()是一个播放所有视频媒体的可视组件。
· player.getControlPanelComponent() 是一个操作时间轴的可视组件(包括开始,停止,回放),也包含了一些媒体流的有用信息。
· player.getGainControl().getControlComponent() 是操作音量(增加)的可视组件。getGainControl()方法返回一个GainControl实例,可用于改变节目的增加等级。

使用可视化组件
上面的界面方法都返回一个java.awt.Component类的实例。没个实例都视可加载到我们窗体上的可视组件。这些组件都与Player有直接的联系,所以在这些组件上的所有可视元素的处理都会产生Player播放媒体后相应的变化。
在我们将这些组件加入到我们的窗体的之前,必须要保证它们不为空。因为并不是所有的媒体播放器包括每一种可视组件,我们只需添加相关播放器类型的组件。比如,一般来说一个音频播放器没有可视组件,所以getVisualComponent()就要返回空。你不会想在音频播放器窗体上添加可视组件的。

获得媒体的特殊控制
一个Player实例也可以通过getControl()和getControls()方法来暴露其控制,getControls()返回一个控制对象集,而getControl()返回一个控制。不同的播放器类型可选择为特殊的操作来暴露控制集去指定的媒体类型,或者用于获取该媒体的传输机制。如果你在写一个只支持某些媒体类型的播放器,你需要依靠某些在Player实例中可用Control对象。
由于我们的播放器是非常抽象的,被设计于播放多种不同媒体类型,我们简单的为用户暴露所有的Control对象。如果找到任何扩展的控制集,我们就可使用getControlComponent()方法来增加相应的可视控件到标签面板上。通过这个办法,用户就可以观察播放器上的所有组件。以下代码片断将所有的控制对象暴露给用户:

Control[] controls = player.getControls();
for (int i = 0; i < controls.length; i++) {
if (controls[i].getControlComponent() != null) {
tabPane.add(controls[i].getControlComponent());
}
}


为了使一个真实的应用程序能用Control实例做一些有用的事(除了能显示可视组件之外),应用程序需要知道该Control的特殊类型,并分配它。此后,应用程序就可使用这些control来控制媒体节目了。例如,如果你知道你经常使用的媒体暴露javax.media.control.QualityControl类型的Control,你能使用QualityControl界面,之后在QualityControl界面上通过调用各种方法来改变性质设定。

使用一个MediaLocator
在我们新的基于GUI的媒体播放器和我们的第一个简单播放器之间最大的不同就是,我们使用一个MediaLocator对象而不是URL来创建Player实例,如下所示:

public void setMediaLocator(MediaLocator locator) throws IOException,
NoPlayerException, CannotRealizeException {
setPlayer(Manager.createRealizedPlayer(locator));
}


我们将在稍后的章节中讨论这个变化的原因。目前,在网络上资源站点上,关于MediaLocator对象和URL的描述被认为是非常相似的。事实上,你可以从一个URL创建一个MediaLocator,也可以从MediaLocator获取到URL。我们的新媒体播放器一个URL中创建一个MediaLocator,并使用该MediaLocator通过文件创建了一个Player。

编译和运行MediaPlayerFrame
通过在命令提示行输入javac MediaPlayerFrame.java来编译例程序。在工作目录下将创建一个名为MediaPlayerFrame.class的文件。
在命令提示行中键入如下来运行例程序:

java MediaPlayerFrame mediaFile


你需要用你本机上的一个媒体文件来替换掉mediaFile(音频或者视频文件都可以)。所有的相对文件名都是相对于当前工作目录。你会看见一个显示控制媒体文件的GUI控制集的窗口。欲了解JMF支持的音频和视频文件列表,在23页的资源。
如果初始编译时失败,请确认JMF的jar文件已经包含在当前的CLASSPATH环境变量中。

MediaPlayerFrame在行动
在本节前你看见的一个视频播放器正在播放MPEG视频文件的屏幕截图。下面的屏幕截图显示了一个音频播放器正在播放一个MP3文件:
要更多的学习本练习中的例子,查看完成的MediaPlayerFrame源代码。

第四节. JMF概念
JMF体系结构
你曾见过了使用JMF播放本地媒体文件是多么的容易,现在我们将后退一步,来看看一幅是如何通过JMF创建了如此成熟的基于媒体的应用程序的大的画面,是如何通过JMF创建了如此成熟的基于媒体的应用程序。全面的了解JMF体系结构是没有意义的,本节将给你一个大体的概念,关于高级的JMF组件是如何组合起来创建想得到的东西。
JMF的组件结构非常的灵活,它的组件一般可以分成三个部分:
· Input描述某种被用于在进程休息的时候作为一个输入的媒体。
· process执行某些输入上的活动。一个过程有一个明确的输入和输出。大量的过程可用, 能被用于一个输入或者一批输入。这些过程能被联系起来,一个过程的输出被用于另外一个过程的输入。在这种风格中,大量的过程可能被应用于一个输入。(这段期间是可选择的——我们开始的两个例子没有包含真正的数据过程,只有一个来自文件的输入和一个通过Player的输出。)
· Output 描述了媒体的某些目的地。

从这些描述中,你可以想象到JMF组件体系结构听起来就好像在一个典型的立体声系统或者VCR之后。很容易设想到,使用JMF就如同打开电视或者在立体声音箱系统下调节声音的风格。例如,录制喜爱的电视节目的简单的动作能在这些组件的基础中:
· Input 是电视广播流,在同一个频道运输音频和视频。
· Process 是一个记录设备(就是,一个VCR或者许多的数字设备)转换模拟或者数字音频视频广播流成适合复制到磁带或其他媒体上的格式。
· Output 是记录已格式化轨迹(音频和视频)到某些类型的媒体上。

JMF资料处理模式
以下图片说明了JMF数据处理模块并对每个类型给出了例子:
使用此模式,很容易明白我们前面的两个例子,从文件中输入音频和视频并输出到本地计算机上。在后面的章节中,我们也会谈论一些通过传播和接收音频媒体的JMF网络功能。

处理模型例子
将JMF的输入,处理和输出模式联系起来,我们能开始想象许多基于媒体的操作都可能通过JMF完成。一个例子,转换一种媒体类型为其他类型并将其输出存储到一个新的文件。举一个例子,我们想要在不损坏原始文件的前提下转化一个WAV格式的音频文件为MP3格式。以下的过程模式插图,就是我们将开始执行转换的步骤:
本例的输入是一个WAV文件。它被一个媒体格式转换工具加工,并输出到一个新的文件。现在,让我们看看JMF API中的这个模式的每一步。我们使用输入,处理和输出模式作为概念上的路标。

JMF输入
再JMF中,一般由一个MediaLocator对象来描述一个输入。如先前规定的,
MediaLocator的外观和行为都非常象一个URL,这样它可以唯一确定网络上的一个资源。事实上,使用一个URL来创建一个MediaLocator是完全可能的;我们在前面的两个例子中就是这样做的。
为了我们的媒体转换例子,我们需要建立一个MediaLocator来描述最初的WAV文件。如同我们将在后面的章节中见到的,一个MediaLocator也可以用于描述一个跨越网络中媒体流。在这个案例中,MediaLocator会描述传播的URL――很像一个被URL指定的在Web上的资源,用于取代指定一个本地文件系统的文件来建立MediaLocator。

一个MediaLocator和一个URL之间的不同
要成功的建立一个URL对象,需要适当的java.net.URLStreamHandler安装于系统中。这个流处理的用途是能够处理被URL描述的流类型。一个MediaLocator对象并没有这个需要。例如,我们的下个应用程序将使用实时传输协议(RTP)在网络上传输音频。由于多数的系统都未为RTP协议安装一个URLStreamHandler,所以创建一个URL对象会失败。在这个应用中,只有MediaLocator对象会成功。
要理解更多关于URL对象以及创建和注册一个URLStreamHandler的信息,查阅JDK帮助文档(查看23页资源)。

JMF处理机
当我们使用JMF的时候,应用程序的处理机组件被Processor接口实例描述。你需要已有些熟悉Processor,它扩展至Player接口。由于Processor继承直Player接口,它同样也从Player继承所有可用属性。另外,Processor增加了两个属性:Configuring和Configured。这些扩展的属性(和与之关联的方法)用于Processor从输入流收集信息时的通信。
在我们的最后的例程序中,我们将建立一个Processor用于将MP3编码格式的音频转换成适合在网络上传播的格式。在稍后的板块中我们会讨论创建一个简单的Processor的步骤。

JMF输出
有少许的方法用于描述JMF中处理模式的输出状态。最简单的(并且我们将在最后一个例子中使用的)是javax.media.DataSink接口。一个DataSink读取媒体内容并且将其传送到一些目的地。本节中最开始的音频格式转换过程中,MP3(输出)文件将被DataSink描述。在我们最后一个例子中,我们将使用一个DataSink在实际上完成网络中传播音频媒体的工作。一个DataSink是在Manager类中,由指定一个DataSource(输入到DataSink)和一个MediaLocator(输出到DataSink)完成的。
一个DataSource实例描述可用于Players,Processors和DataSinks的输入数据。一个处理机的输出也被描述成一个DataSource对象。
这就是为什么处理器能彼此联系起来,在同一媒体数据中完成多种操作。这也是来自Processor的输出能作为输入被Player或者DataSink使用的原因(它可将媒体传递到输出目的地)。
一个DataSink的最后目的文件由一个MediaLocator对象说明。如同前面一样,MediaLocator描述一个网络资源;这就是媒体流将被传递的地方。

第五节.传播接收媒体
JMF和实时传输协议(RTP)
许多的友善网络的特征直接建立在JMF中,这些使为客户端程序通过网络传输和接收媒体非常容易。当在一个网络上的一个用户想要接收任何种类的媒体流的时候,它不需要在观看媒体前等待全部的广播下载到机器上;用户可以实时的观看广播。在流媒体中些提出了这个概念。通过流媒体,一个网络客户端能接收到其他机器上广播的音频,甚至获取正在发生的实况视频广播。
在IETF RFC 1889中定义了实时传输协议(RTP)。发展在快速和可靠的状态下通过网络传输时间极其敏感的数据,RTP在JMF中用于提供给用户向其他网络节点中传输媒体流的方法。
在本节中,我们将学习我们的最后一个例程序。这里,你将学习到如何传输一个存储在一台机器上的MP3文件到另外的在同一个网络的机器上去。实际的MP3源文件并不从主计算机上移除,它也不使复制到其他机器上去;事实上它将会转换成能使用RTP传输的文件格式并通过网络发送。一旦被一个客户端接收到,源文件(现在是RTP信息包的形式)可以再次传输,这一次是在接收机器上可播放的一种格式。
在MediaTransmitter.java文件中源代码查看学习以下练习。

设置处理模式
我们可以在前面的章节中定义的处理模式的基础下来讨论我们的最终的例子。在传输机器上,处理模式看起来像这样:
事实上,MediaTransmitter对象源代码包括了以下三行:

private MediaLocator mediaLocator = null;
private DataSink dataSink = null;
private Processor mediaProcessor = null;



这三个实例变量可以直接映射到前面的处理模式图表,如下:
·  mediaProcessor变量是我们的处理器;它将负责转换音频文件从MP3文件模式到一个适合通过RTP协议传输的格式。
·  dataSink变量是我们的输出块。
· 当我们建立DataSink时我们需要指定一个MediaLocator,它是DataSink的目的文件。

当我们通过运行DataSink我们的处理过的媒体,它将传输到我们在MediaLocator中指定的地点。

RTP MediaLocator
在前面的两个练习中,我们通过从文件中获得的一个URL建立了MediaLocator实例。 在本练习中,我们必须建立一个MediaLocator来描述网络上媒体传播输出流;换句话说,我们必须创建一个能我们的音频传播的目的地的MediaLocator。一个RTP MediaLocator符合如下规则,看起来就像一个典型的URL:

rtp://address:port/content-type



让我们看看上面URL规范的每一段:
· address 是将传输的媒体的地址。以单播的模式传输(一个专用IP地址),地址将会是有意接收的机器的IP地址。以广播的模式传播(到子网中的所有机器),地址将会是以255作为最后的一块的子网地址。举个例子,如果我再子网中可指定地址为192.168.1和想要传播到子网中的所有节点,我可以指定192.168.1.255作为地址;这样允许子网中的每个节点监听广播媒体。
· port 必须是被传输者和接收者都允许的一个端口。
· content-type 是媒体流类型。在我们的案子中,这个将会是音频。
下面的一个简单的RTP传播MediaLocator例子会让所有在指定网络中的机器接收到媒体流:

rtp://192.168.1.255:49150/audio



创建一个处理机
在setDataSource()方法中我们首先要做的就是创建一个Processor实例。
下面的Processor的职责是转换MP3音频媒体为一个RTP来表示:
public void setDataSource(DataSource ds) throws IOException,
NoProcessorException, CannotRealizeException, NoDataSinkException {
mediaProcessor = Manager.createRealizedProcessor(
new ProcessorModel(ds, FORMATS, CONTENT_DESCRIPTOR));
在Manager类中,我们能创建一个Processor对象,通过两种方法中的一种:
createProcessor()或者createRealizedProcessor()。你很可能会注意到这两个方法样式的显示和前面例子中创建一个Player的方法很相似。在目前的例子中,我们将创建一个已实现的Processor。我们这样做是因为我们使用的应用非常简单,在Processo处于Realized状态时我们不需要关心任何真实的工作。

创建一个ProcessorModel
创建一个已实现的Processor,我们需要创建一个为Processor描述输入和输出媒体类型的ProcessorModel实例。为了创建ProcessorModel,我们需要下面的一些:
· 一个DataSource,将被处理的媒体(输入文件)。
· 一个javax.media.Format数组,描述输入媒体的格式。
· 一个javax.media.protocol.ContentDescriptor实例,为我们的处理机描述输出格式。传送者的DataSource是通过一个参数传递到此方法。

定义输入和输出格式
因为我们的MediaTransmitter类会被时常用于将输入媒体格式(MP3)转换成一种输出格式(音频RTP),中学对象被定义成静态。我们创建一个新的javax.media.format.AudioFormat实例用于描述媒体输入类型(在java帮助文档中查看可用格式)。这就是我们的处理机可以获取MP3音频文件的原因。
我们也创建一个javax.media.protocol.ContentDescriptor实例来描述想要处理机输出的。在我们的案子中,这是一个RTP媒体流。

这就是为什么我们的处理机可以只制造RTP流。
下面的代码片断显示了我们如何设置格式和内容描述符变量,用于创建ProcessorModel对象:

private static final Format[] FORMATS = new Format[] {
new AudioFormat(AudioFormat.MPEG_RTP)};
private static final ContentDescriptor CONTENT_DESCRIPTOR =
new ContentDescriptor(ContentDescriptor.RAW_RTP);



连接输入,处理机和输出
现在我们有一个处于Realized状态的Processor,我们需要设置DataSink以能实际上传播RTP媒体。创建DataSink是简单的大概使用另外一个调用给Manager对象,如下所示:

dataSink = Manager.createDataSink(mediaProcessor.getDataOutput(),
mediaLocator);



createDataSink()方法获取新Processor的输出(作为一个DataSource参数)和MediaLocator对象,我们和MediaTransmitter对象同时建立的。通过这样,你能开始我们的不同的组件是如何在处理模式中联系起来的:我们从一个Processor中获取输出并使用他们作为输入到其他组件。在这个特殊的应用中,Processor输出用于传输媒体的DataSink的一个输入。

创建一个DataSource实例
在这点上,我们全部都是做和设置我们的媒体播放器的广播传输。
我们需要创建DataSource对象,我们用于创建处理机(就是,在我们的MediaTransmitter中,参数传递到setDataSource()方法)。下面是创建一个DataSource实例的代码:

File mediaFile = new File(args[1]);
DataSource source = Manager.createDataSource(new MediaLocator(
mediaFile.toURL()));



这段代码是在MediaTransmitter对象中的vmain()方法。这里我们通过从命令行输入的第二个参数创建一个File对象。我们通过文件创建一个MediaLocator,而后通过位置创建一个DataSource。这个新近创建的DataSource是一个涉及到传送者的输入文件。我们能使用这个DataSource初始化传输者。

开始和停止MediaTransmitter
我们通过调用其中的startTransmitting()方法来开始MediaTransmitter,如下所示:

public void startTransmitting() throws IOException {
mediaProcessor.start();
dataSink.open();
dataSink.start();
}



这个方法首先开启处理机,然后打开并启动DataSink。在这个调用后,接收机器就可在媒体传送者上监听。
停止传输者是非常简单的。以下代码将DataSink和Processor都停止和关闭掉:

public void stopTransmitting() throws IOException {
dataSink.stop();
dataSink.close();
mediaProcessor.stop();
mediaProcessor.close();
}



编译和运行MediaTransmitter
通过在命令行中输入javac MediaTransmitter.java来编译例程序,可在你的工作目录中生成一个同名的.class文件。
要运行例程序,在命令提示行中输入以下代码:

java MediaTransmitter rtpMediaLocator audioFile



此例将创建一个myAudio.mp3文件的媒体广播。不要忘记将rtpMediaLocator替换成一个媒体传输的RTP URL,如同先前讨论的。
你同样也需要将audioFile替换成你本机的音频文件名。
所有的相对文件名都是相对于当前工作目录的。你会看见一些信息标志正在播放的文件。按下Enter键来停止播放。

为传送者的一个例命令行交互如下:
java MediaTransmitter rtp://192.168.1.255:49150/audio myAudio.mp3
如果初始编辑失败,确定JMF的jar文件包含CLASSPATH环境变量中。要近一步探索本程序和练习,请查阅MediaTransmitter源代码。

接收传输的媒体
现在你可能会问,“如果没有人可以看或者收听的话,这个传播媒体有什么好的?”
幸运的是,设定一个接收传播媒体的客户端只需要对我们在第二个例程序的MediaPlayerFrame源代码做很小的改动。
MediaPlayerFrame类需要一个很小的调节来接收和播放音频文件。在main()方法中,你需要注释掉如下的一行:

mpf.setMediaLocator(new MediaLocator(new File(args[0]).toURL()));


并且输入如下的一行:

mpf.setMediaLocator(new MediaLocator(args[0]));


这个简单的改动允许我们通过String来创建一个MediaLocator对象,而不是通过创建一个File来创建MediaLocator。
其他代码都一样。

指定RTP URL
在12页的说明编译和运行MediaPlayerFrame介绍了如何编译和运行MediaPlayerFrame例程序。这唯一的不同就是你需要为传输者指定RTP URL。为接收者的例命令行交互如下:

java MediaPlayerFrame rtp://192.168.1.255:49150/audio



运行网络媒体传送者的注意事项
如果你在网络上只有权使用一台机器,你仍然可以运行传输程序。当你启动传送程序的时候,你可以即使用RTP URL传输地址,也可指定你工作的机器的机器地址。为了能够调节传输,在开始前接收者必须使用精确的同样的RTP URL。
如果你运行本例真实的网络版本,每台你使用的机器都需要安装JMF,不论是传输还是接收媒体流。这是必须的,因为不论是传送程序还是接收程序都大量的使用了JMF的API。
在任一个案子中,确认在指定的RTP URL中使用了相同的地址和端口;否则媒体传输是不会工作的。

第六节. 约束和资源
摘要
我希望本指南能给你提供如何使用JMF的API的有用的浏览。
我们建立了三个小的应用程序来播放本地的音频和视频,也通过网络传播和接收媒体。这些应用程序的源代码中包含了很多的javadoc样式的注释。这就有助于你理解你剩余的问题。
许多JMF的主题在本指南中并没有涉及。实际上,我们更关注JMF的基本概念和应用;在此基础上,我们能轻易地扩展学习的其他范围。要更深入JMF的应用程序,你可能想要学习下面的面板中所提到的主题。更近一步的阅读本指南中的主题,查阅23页的资源。

高级主题
大量的值得做的练习在本指南的范围之上。在简单的说明之下自己更进一步的学习,你可以扩展我们的应用程序代码,也可以反展你的JMF相关知识。使用以下的练习开始:
· 媒体捕获:JMF包含了丰富的API来捕获媒体数据。如果你对使用JMF捕获媒体感兴趣,你可以使用javax.media.CaptureDeviceManager类和javax.media.protocol.CaptureDevice接口的API来学习。对于一个高级的练习,考虑使用CaptureDeviceManager和CaptureDevice接口来增加媒体捕获功能到媒体播放应用程序的GUI版本上。
· 会话管理:由于本指南是一个JMF的说明,我们使输出表现非常的简单,仅仅实现了javax.media.DataSink输出。
另外的输出表示是使用javax.media.rtp.SessionManager。这个管理类允许客户端创建并监视他们的RTP流和连接。通过SessionManager并随后创建流,它可能非常的接近监视RTP会话。作为一个高级的练习,转换我们的地三个演示程序来使用SessionManager,然后监听流出的RTP流已经哪些客户端在收听。
· 使用JMF的多点传送:我们的广播演示应用程序说明了如何传送一个网络的媒体到另外一个网络的一或多台机器上去。它也可能使用JMF中的多点传输协议来提供给更复杂,多用户的网络。
JMF用户指南提供了一个使用JMF的多播协议的更深入的探讨。更进一步追踪本主题查看23页资源。
· 传输视频: 我们的最后一个演示应用程序着眼于如何传输一个MP3音频文件,但是JMF也能够通过网络传递视频。关注API文档中的Format和ContentDescriptor类获得如何使用的更好的方法。
· 导入/导出RTP媒体流: JMF同样允许将RTP流保存为文件以便将来使用。举一个实例,一个远程电信会议可以保存下来以后再看。
由于流已经保存再RTP格式中,已经不需要再次转换,这样可导致传输程序的性能改进。通过一个文件而不是URL来设置DataSink对象中输入/输出MediaLocator。你会再JMF用户指南中发现更深层次的主题探讨。

资源
JMF
· 下载mediaplayer.jar,本指南中使用的完整的例源代码。
·  JMF主页 (http://www.javasoft.com/jmf)是最好的探讨JMF更多信息的资源。
· 你可以找到JMF说明书(http://java.sun.com/products/java-media/jmf/2.1.1/specdownload.html),再Java开发者联盟上包括API文档和JMF用户指南。你必须有权使用所有的这些资源,如果你想做任何更深入的JMF编程的话。
· 官方的JMF支持文件格式 页面
(http://java.sun.com/products/java-media/jmf/2.1.1/formats.html) 列出了所有可为JMF辨识并播放的文件格式。此文件格式页面也包括了学习更多关于捕获设备和RTP格式的参考。
· MPEG-4 Video for JMF (http://www.alphaworks.ibm.com/tech/mpeg-4), 来自IBM
alphaWorks, 是一个JMF的视频编解码器。
RTP
·  IETF RTP RFC (http://www.ietf.org/rfc/rfc1889.txt) 非常详细的描述了RTP协议。
· 查看 JMF API Guide
(http://java.sun.com/products/java-media/jmf/2.1.1/specdownload.html) ,有许多有关于RTP协议和描述以及它是如何在JMF上应用的。
· 哥伦比亚大学有一个比较有用的RTP FAQ(http://www.cs.columbia.edu/~hgs/rtp/faq.html).
Java技术
·  Java 2 Platform, Standard Edition (http://java.sun.com/j2se/) 可从sun公司获得。
· sun的指南关于JFC/Swing (http://java.sun.com/docs/books/tutorial/uiswing/index.html)
和 AWT (http://java.sun.com/docs/books/tutorial/information/download.html#OLDui) 是非常好的能学习到很多关于Java程序语言中GUI编程的好地方。
· 另外一个sun指南学习network programming 基础
(http://java.sun.com/docs/books/tutorial/networking/index.html)。
多点传输协议
· Explicit Multicast (XCAST)
(http://oss.software.ibm.com/developerworks/opensource/xcast/) 是IP多点传输的一种形式,为非常多的多点传输组设计提供可升级的支持,这些组有些少量的参与者代表。XCAST 代码得到了IBM Common Public License的认可。
· Todd Montgomery 的 MTP page (http://www.nard.net/~tmont/rm-links.html),
在这里你能找到一个广泛的涉及到多点传输协议的列表。
附加资源
· 你可以在
developerWorks Java technology zone (http://www-106.ibm.com/developerworks/java/)中找到许多的关于Java各方面的内容。
· 查看 developerWorks tutorials page
(http://www-105.ibm.com/developerworks/education.nsf/dw/java-onlinecourse-bytitle?OpenDocument&Count=for a complete listing of free tutorials.

posted @ 2006-07-27 20:05 KevinGong 阅读(435) | 评论 (1)编辑 收藏