关于Servlet与Applet的澄清,进行大文件上传

好久没有发过相关的技术文章了。这次由于要搞毕业设计,对大文件要进行上传 。因此,这一段时间对Applet与Servlet,Socket的资料看了一些,也总结了一些东西,希望对“后来者”有所方便之处

这篇文章主要对文件上传的技术分析,在后面(或另开贴)贴有相关雏形代码

一、Applet:

优点

1、界面的改进 。这个东西是个好东西,或许以前许多对它“丑陋”的外表感到厌恶,但在JDK5+,Swing外观可以加入自设的外观界面,像XP,Office2005,Linux等风格(可以开源下载Java开源 Swing外观 )。

2、开源参考的资源多 。Applet同时属于较重量级的Rich界面,可以实现的功能也仍然未能一一列数过来,网上也有专门收集Applet小程序的网站,但好像外国比较多www.gamelan.com ,里面就可以找到大多数的Applet。想要利用好国外资源,专业E文就要提高啦~

3、沙箱限制可以突破 。前些时间我也是这么想的,Applet在Java强大的安全体系下,只可以和宿主主机(就是运行Applet网页的网站,简明点 )通讯,甚至只能与 Web服务同一个端口进行通讯(这一点还未查实)。事实 ,上面提到的是真的,但安全审核总有信任的存在,要让Applet可以得到本地主机的信任,就可以用数字签名了。对于数字签名,我也还没有去试过,但看过运行的效果,像JUpload 本地资源可以得到访问,并且可以选择多个文件上传,但对于大文件(size>300)是否可以上传,还没有做测试(因为例子是PHP的,看了一下代码,大概懂,也是上传到服务器内存)。 原创:Vange  liyanqing_qq

缺点

1、JRE (Java Runtime Environment)。这可以说是一个最大的可惜点了。虽然微软自带也有JRE,但其支持大概,好像是Java1.1的水平(不确定)。要运行现在写 的Applet,只有安装新的JRE,但新的JRE的大小有>=14M。这个是普遍问题,只能通过各种途经去说服用户安装新JRE,就看文字工夫 了~

二、Servlet

优点原创: Vange  liyanqing_qq

1、响应各种请求 。Servlet可以说是CGI的轻量级实现,Servlet不仅可以响应浏览器的请求,也可以响应Applet,Application。或者由其它语言写的程序。只要定了通讯规范,就可以响应一切的请求,后面要讨论的大文件上传也就是用Servlet来实现的。

缺点

1、网页界面显示 。这可以说是Servlet的内伤了,但是借助JSP,补充了界面这一块的缺陷。

2、新生的Action 。现在许多的Java Web框架产生,对于原始的Servlet的使用越来越少了。像Struts里面的Action。里面同样可以实现Servlet的功能。

三、Socket Server

优点:

1、网络传输首选 。socket算是网络传输大量数据的首选了,可以打开OutputStream和 InputStream来分别进行发送和接收数据,对于Socket的多线程处理,我还没有试验成功。但在最初的大文件上传构想中,我想把Socket的 server放在Tomcat的容器,随网站启动一起启动,同时结合多线程来处理来自用户的Socket,但是,在容器里面启动自己的多线程是不理智 ,建议在WEB容器里面尽量不要启动自己的线程,注册新端口来提供新的服务。这样做有许多的不确定性。以前做过一个相关的WEB多线程处理用户请求的Servlet((原创)关于《请求排队系统》1.1 )。可以将并发的请求转换为串行的请求。

缺点:

1、传输的稳定 。Socket采用UDP传输,只负责发送接收信息,不包括信息是否真正到到达,接收有没有错误,建议Socket技术在比较稳定的网络上应用,但对于现今的网络,Socket也是可以信任得过了~特别是局域网,采Socket是不错的选择。

 

对于大文件上传的分析:

  在本次的构架上,我想采用的是用Applet和Servlet。利用Applet 加上数字签名 可 以实现多个文件同时上传,进度条实时显示,断点续传等等。Applet作为用户上传文件界面可以有很多的应用空间,但难点就是用户必须要装jre。这一次 说真的,我也考虑了很久,真的很不想让用户再花时间来装个jre来运行的网页。但从另一方面考虑,这次架构的网站是在校园网中,下载速度是可以不用考虑 的。就差怎么让用户去安装了,其实这样想,装了新jre,用户可以运行其它含有Applet的网页,网上的Java程序。同时也为响应开源做一点贡献。还 有前天,看到兴业银行也有相关的技术要求。心里也就放心了。

  在传输方法,我原本是想用Applet作为Socket的客户端,由Tomcat启动Socket服务器端。但不幸,在启动Tomcat的时候 出错了。就在我想把Socket服务器的处理多线程化的地方。所以我放弃了这种搭配。并把想法转为用Applet打开URLConnection来向 Servlet发送数据。并在网上搜索到相关的文章说明,像applet与servlet的通信 这样的文章看到了许多篇,里面的算法都是正确的,但有一点一直都没有说明 ,那就是URLConnection 的 提交方式没有说明,我在这里说明一下:当你要把数据通过URLConnection发送到Servlet的时间,数据都是先输出到URL的 OutputStream中,但并没有马上提交并连接到Servlet去,而是把数据缓存在本地的内存里面了,当InputStream开始读的时候才会 把数据提交给Servlet。所以当数据太多的时候就出现什么情况了呢?内存溢出,没错。就是这样的错误OutofMemory。在网上找一下解决的方 法,没想到搜到的都是将JVM的内存调大点。这个根本是个治标不治本的办法。就像是上传文件到服务器的内存中,让服务器的内存加大点。那到底是什么原因让 URLConnection出现这种情况呢?我思考过,其实原理很简单,也很正确:HTTP协议是无状态的 (可能不是 很达意)。当提交数据时,Servlet与本地主机连接了,并接收数据,Servlet输出数据,本地主机接收数据后。断开两者连接。Servlet不用 像Socket一样专门提供一条固定且持续的连接来和本地主机通讯。所以本地主机提交的数据是先缓存在本机的内存里面,当数据输出到缓存完毕,再打开 InputStream来接收Servlet的响应数据时,这些缓存数据才被提交到Servlet。Servlet也在此时再运行起来,与本地主机连接, 接收本地主机数据,处理数据发送响应数据到本地主机。当本地主机接收完响应数据,两者的连接就断开了。现在问题就明显地摆在面前 了。

  看到这个问题出现,我有点郁闷了~难道这个方案也要被del掉,转而去考虑FTP方法??能过一段时间的思考,要解决这个问题有两个方法:1把本地主机的内存加大到1G,那文件在1G以上呢?不现实,del掉。2把本地主机的文件切块再上传 !! 这个才是王道。现在思想就到了这里,如何让文件分块并上传,服务器如何去把数据写入到对应的文件中?现在“规范”的作用就出来了。感觉好像自己在写协议一 样。那就是在发送数据里,在数据前面发送文件的相关信息,让服务器可以找到上传一半的文件,并重新文件写入新的数据。根据这样的想法,我写了下面的试验代 码,发现行了!!这就是理论加上实际了~呵呵。

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class SendFile {
public static void main(String[] args) {
File file = new File("c://upload.rmvb"); //有3百多M

   // 建立文件的输入流
FileInputStream aFile = null;

   try {
aFile = new FileInputStream(file);
} catch (Exception e) {
e.printStackTrace();
}

   byte[] buf = new byte[1024 * 1024]; // 读取数据的缓存块
int len = 0; // 每次读取的长度
long curPos = 0L; // 文件读取的当前位置

   byte actId = 1; // 操作标识,第一次提交为1.其它提交时为2
String fileId = ""; // 文件的唯一标识
byte actRet = 0; // 提交处理后结果

   // 分块读取文件
try {
while ((len = aFile.read(buf)) != -1) {
// 打开远程
URLConnection con = getConnection();
OutputStream os = con.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
// 发送基本信息
dos.writeByte(actId);
if (actId == 1)
actId = 2;
dos.writeUTF(fileId);
dos.writeLong(curPos);
dos.flush();
// 写入数据
os.write(buf, 0, len);
os.flush();

     // 关闭发送流,提交数据
os.close();
curPos += len;

     // 打印时间
System.out.println(System.currentTimeMillis()+" submit over");

     InputStream uis = con.getInputStream();
DataInputStream dis = new DataInputStream(uis);
byte retActId = dis.readByte();
String retFileId = dis.readUTF();
byte retActRet = dis.readByte();
fileId = retFileId;

     // 打印时间
System.out
.println(System.currentTimeMillis() + " listen over");
}
} catch (Exception e) {
e.printStackTrace();
}

}

private static URLConnection getConnection() throws IOException {
URL url = new URL("http://127.0.0.1:8080/testThread/UploadServlet ");
URLConnection con=url.openConnection();
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(true);
con.setDefaultUseCaches(true);
return con;
}
}

///////////////////////////////

package servlets;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;

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

/**
* Servlet implementation class for Servlet: UploadServlet
*
*/
public class UploadServlet extends javax.servlet.http.HttpServlet implements
javax.servlet.Servlet {
public static final String firstUpload = "FirstUpload";

// 存放上传过程中文件信息

private static final String FILE_ID = "T23434AB"; //随便定义的

private static final String FILE_PATH = "f://temp.file";

protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException {

   RandomAccessFile file = null; // 文件写入
InputStream is = null; // request 的输入流
DataInputStream dis = null; // request的装配流
OutputStream os = null;// response的输出流
DataOutputStream dos = null; // response的装配流
try {
is = request.getInputStream();
dis = new DataInputStream(is);
os = response.getOutputStream();
dos = new DataOutputStream(os);

   } catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

   byte actId = 0; // 提交过来的操作id,1为刚上传,2为续传,3为结束通知
String fileId = ""; // 提交过来的操作文件id
long writePos = 0L; // 开始写入地址

   try {
// 读取操作标识及其它基本信息
actId = dis.readByte();
fileId = dis.readUTF();
writePos = dis.readLong();
// 对读取的信息处理

    if (actId == 1 && fileId.equals("")) {
// 如果是开始上传操作
// 在对应的位置创建文件,并把文件路径放入Map
// 将传入的流里面的数据写入到指定文件中
// 写入完成后,把写入的进度写入数据库
// 返回文件的唯一码
file = new RandomAccessFile(new File(FILE_PATH), "rw");
file.seek(writePos);
byte[] tmp = new byte[1024 * 1024];
int len = 0;
while ((len = is.read(tmp)) != -1) {
file.write(tmp, 0, len);
}
is.close();
dis.close();
dos.writeByte(3);
dos.writeUTF(FILE_ID);
dos.writeByte(1);
os.flush();
os.close();
} else if (actId == 2) {
// 如果是续传操作 ×××其实与上面情况一样××××代码还没有优化。。
// 从对应的路径里面调出文件
// 从数据库读取写入进度
// 将流写入到文件中
file = new RandomAccessFile(new File(FILE_PATH), "rw");
file.seek(writePos);
byte[] tmp = new byte[1024 * 1024];
int len = 0;
while ((len = is.read(tmp)) != -1) {
file.write(tmp, 0, len);
}
is.close();
dis.close();
dos.writeByte(3);
dos.writeUTF(FILE_ID);
dos.writeByte(1);
os.flush();
os.close();
} else if (actId == 3) {
// 如果是文件完成通知
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
if (file != null)
file.close();
if (os != null)
os.close();
if (dos != null)
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}

   System.out.println("tran end");
}

}

转载请注明出处:http://hi.baidu.com/romeroad

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值