作者按:
******************************************************************
近日上网冲浪,发现还有人在问“B/S 中如何上传文件”,于是把 2001 年时写的一篇文章翻了出来。该文已在某网络媒体上发表过(不好意思,忘了媒体的名称了)。我确实是原作者,不是抄袭的,发表于 BLOG 中,并无侵权之意。 现在的开发人员,可能不会费这么大劲自己去写上传程序,通常会直接使用 SmartUpload 等工具,真是幸福啊。发表本文的目的,仅为了解释一下原理。 *******************************************************************
 
用Java Servlet实现文件上载                            
 
    各位大侠可能会对263电子邮箱中的“上传附件”功能有印象,就是:在浏览器中点击“浏览”,弹出一个对话框,选中文件后,单击“确定”,文件就被上传到了服务器端。 
 
    因为需要,就到网上找了几个控件,如 SmartUpload 等,但都觉得不好用,或者说是不合用,决定自己做一个。近日看到网上也有人提问怎么上载文件,于是把编制过程整理一遍,希望对大家有所帮助,不足之处,请多多指教。  
 
    一、准备
 
    侦听工具,如SpyNet(包括 CaptureNet 和 PeepNet ),目的是用于分析数据包格式;
    Java环境:至少要包括一个Servlet引擎,一套JDK;如果没有,可以访问 “[url]http://www.jsp001.com/article/Application_Server_Comparison_Matrix_20010226.html[/url]” 从这36款中随便找出一种来,安装运行即可。JSP服务器都会支持SERVLET,因为JSP本身就是先被编译成SERVLET再执行的。  
 
    二、分析
 
    1、制作HTML页面,用于上传文件。需要注意:要指定enctype属性为“multipart /form-data”,因为数据流的格式是不一样的。  
 
    <form action="/java/servlet/UploadFile" method=post enctype="multipart/form-data">
      <p><input type=radio name=type value=0>model
         <input type=radio name=type value=1>report      
         <input name=id >
         <input type=file name=file value="test">
      </p>
     <input type=submit>
   </form>
 
    2、HTML页面做好后,就可以开始分析数据流了。先打开侦听器,然后在浏览器(IE, Netscape)中打开本页面,随意选择一个文件,单击“确定”,看看侦听器听到了什么。在跳过前面几个包后,可以得到下面这两个相关的包。
 
    第一个包的很容易明白,在Servlet中,用getHeader(String)能得到的内容就在这里面。不过这个包,用HttpServletRequest的getInputStream是得不到的。关于HTTP协议的更多信息,可以查阅 RFC 文档。
 
第一个包
  再看第二个包,可以看到,所要传的参数都在。下文只分析这个包。
 
第二个包

    以下对各标号作出说明:
    (1)开始,这是整个能得到的输入流的开端;
    (2)第1段结束。每一段包含一个参数的信息,这些信息包括类型、名称、内容等。
    (3)和(4)与(2)是一样的。
    (4)以后就是输入流的结束标志:boundary。
    (5)为从输入流中能读到的最后一个字符。
 
    注意了第一个包中,有一项叫做“boundary”。顾名思义,这个boundary是 “分界”标志了。每一段的开头都会有一个boundary,然后是 0D 0A,然后是一些相关信息,接着是 0D 0A 0D 0A,紧跟着参数的实际值,然后是下一个boundary,标志着下一段的开始。而整个输入流呢,以一个boundary结束。如果只有一个参数,那输入流的结构应该是下面这样的:
 
 边界

    三、编程
明白了数据流的结构,编程就简单了,以下给出一段源程序。该程序易于使用, (当然,也不必交版权费啦……)。先给出如下的调用示例,而把源程序附于末尾。
public void doPost(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, java.io.IOException
      {
        //新建一个对象,其实,若写成static的,连这一步都可省了
        DecodeRequestStream decode = new DecodeRequestStream(); 
        //调用Decode方法,返回一个哈希表
        Hashtable hashtable = decode.Decode(req, 2);
        ……
        //获取type的值
        String type = (String)hashtable.get("type");
        //获取id的值
        String id = (String)hashtable.get("id");
        //以字节数据的方式获得文件的内容
        byte[] filecontent = (byte[])hashtable.get("file");
        //获得文件名
        String filename = (String)hashtable.get("filename");
        //获得文件类型
        String filetype = (String)hashtable.get("filetype");
        ……
      }
Decode函数的声明如下:
      入参:
          (1)HttpServletRequest: 从这个参数中可以得到输入流;
          (2)int ParamsCount: 这个参数表示输入流中除文件外,普通参数的个数
             提供这个参数是从性能的角度出发的,下文中会有说明;
      出参:
          一个哈希表。如果是普通参数,则以(string name, string value)的方式
          保存,如果是文件,则以(string name, byte[] value)的方式保存;
      
      对DecodeRequestStream类,作如下说明:
      1、本类一次只能处理一个文件的上载。如果有多个文件,将会保存在一个字
         节数组里面。实际上,可以很容易地把本程序改写成支持多文件的;
         
      2、文件必须是作为最后一个参数。此前有多少个参数必须在调用时通过Param
         sCount参数指定。细心的大侠会发现这个参数也是为了性能。因为确定边
         界boundary的位置是一个很费时的操作,需要先拷贝某个位置起与boundary
         相同长度的字节数组,然后再与boundary比较。在确定文件内容的结束位置
         时,要从文件流的开始处一直搜索到文件的结束处,对于大的文件,这是
         很费时的。所以本程序中做了一点小动作,那就是,对于第ParamsCount+1
         的那个参数(也就是文件参数),不用常规方法搜索,而是直接跳到输入流的
         末尾(末尾是boundary 0D 0A),再往前倒数boundary的长度外加4个字节。
         然后从这个位置开始定位boundary(一找一个准)。程序中,用了5个字节,
         是"留有余地"的想法,其实不用。
         
      3、本程序在 Tomcat 3.2.1 + Sun JDK 1.3.0_02 下运行通过,客户端浏览器
         为Internet Exploere 5.0、Netscape Communacator 4.77 和 Netscape 6。
以下是源程序:

Code:

[Ctrl+A Select All]

版权声明:原创作品,如需转载,请注明出处。否则将追究法律责任
servletupload上载

4

分享

收藏

上一篇:在项目中集成 Google Desktop,提供全文检索能力下一篇:老九戏说韩非子的《说难》

猜你喜欢

  • 【随笔】JVM核心:JVM运行和类加载
  • JavaWeb程序架构模式的演进
  • Java定时任务调度详解
  • JavaEE_Spring_Aspect无法切入Controller层
  • Java高级特性——注解,这也许是最简单易懂的文章了
  • 局部内部类的成员变量被final修饰才能被局部内部类的方法所访问
  • java实现小球碰撞反弹
  • 双色球--最多2个号码相同的内幕

发表评论

Ctrl+Enter 发布

发布

取消

11条评论

按时间倒序按时间正序

随风之幻

1楼  2007-01-21 01:12:27

看了这篇文章,参考了网上的一些技术文章,写下了下面基于web的文件上传程序。谢谢豪哥。
main.html:
<body>
  <form action="jsp1.jsp" enctype="MULTIPART/FORM-DATA" method=post> 
           作者: <input type="text" name="author" /> 
           <br /> 
           公司: <input type="text" name="company" /> 
           <br /> 
           选择要上载的文件 <input type="file" name="filename" /> 
           <br /> 
           <input type="submit" value="上载" /> 
           </form> 
</body>

jsp1.jsp:
<jsp:useBean id="thebean" scope="page" class="com.zzl.SimpleBean" /> 
<% 
thebean.doUpload(request);
%>

SimpleBean.java
package com.zzl;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.FileWriter;

public class SimpleBean {
     public void doUpload(HttpServletRequest req) throws ServletException,IOException { 
           PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("D:\\Demo.out"))); 
           ServletInputStream in = req.getInputStream(); 
           int i=in.read(); 
           while(i!=-1) { 
                 pw.print((char)i); 
                 i=in.read(); 
           } 
     pw.close(); 
     } 
}

FileUpload.java:
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.ServletInputStream; 
import java.util.Dictionary; 
import java.util.Hashtable; 
import java.io.PrintWriter; 
import java.io.BufferedWriter; 
import java.io.FileWriter; 
import java.io.IOException;

public class FileUploadBean { 
     private String savePath, filepath, filename, contentType; 
     private Dictionary fields; 
     
     public String getFilename() { 
           return filename; 
     } 
     
     public String getFilepath() { 
           return filepath; 
     } 
     
     public void setSavePath(String savePath) { 
           this.savePath = savePath; 
     }

public String getContentType() { 
           return contentType; 
     }

public String getFieldValue(String fieldName) { 
           if (fields == null || fieldName == null) 
                 return null; 
           return (String) fields.get(fieldName); 
     }

private void setFilename(String s) { 
           if (s==null) 
                 return; 
           int pos = s.indexOf("filename=\"");

if (pos != -1) { 
                 filepath = s.substring(pos+10, s.length()-1);

// Windows浏览器发送完整的文件路径和名字 
                 // 但Linux/Unix和Mac浏览器只发送文件名字 
                 pos = filepath.lastIndexOf("\\"); 
                 if (pos != -1) 
                       filename = filepath.substring(pos + 1); 
                 else 
                       filename = filepath; 
           } 
     }

private void setContentType(String s) { 
           if (s==null) 
                 return; 
           int pos = s.indexOf(": "); 
           if (pos != -1) 
                 contentType = s.substring(pos+2, s.length()); 
     }

public void doUpload(HttpServletRequest request) throws IOException { 
           ServletInputStream in = request.getInputStream(); 
           byte[] line = new byte[128]; 
           int i = in.readLine(line, 0, 128);
           //第一行应该是分界符,而且如果没有错误的话,
           //它的长度应该大于3。如果它的长度小于3,我们可以认为出现了错误,doUpload方法应该立即返回 
           if (i < 3) 
                 return;
           int boundaryLength=i-2; 
           String boundary = new String(line, 0, boundaryLength); //-2丢弃换行字符 
           fields = new Hashtable(); 
           while (i!=-1) { 
                 String newLine = new String(line, 0, i); 
                 if (newLine.startsWith("Content-Disposition: form-data; name=\"")) { 
                       if (newLine.indexOf("filename=\"") != -1) { 
                             setFilename(new String(line, 0, i-2)); 
                             if (filename==null) 
                                   return;

//文件内容 
                             i = in.readLine(line, 0, 128); 
                             setContentType(new String(line, 0, i-2)); 
                             i = in.readLine(line, 0, 128); 
                             //空行 
                             i = in.readLine(line, 0, 128); 
                             newLine = new String(line, 0, i); 
                             PrintWriter pw = new PrintWriter(new BufferedWriter(new 
                             FileWriter((savePath==null? "" : savePath) + filename))); 
                             while (i != -1 && !newLine.startsWith(boundary)) {

// 文件内容的最后一行包含换行字符 
                                   // 因此我们必须检查当前行是否是最 // 后一行 
                                   i = in.readLine(line, 0, 128); 
                                   if ((i==boundaryLength+2 || i==boundaryLength+4)&&(new String(line, 0, i).startsWith(boundary))) 
                                         pw.print(newLine.substring(0, newLine.length()-2)); 
                                   else 
                                         pw.print(newLine); 
                                   newLine = new String(line, 0, i); 
                             } 
                             pw.close(); 
                       } 
                       else { 
                             // 普通表单输入元素 
                             // 获取输入元素名字 
                             int pos = newLine.indexOf("name=\""); 
                             String fieldName = newLine.substring(pos+6, newLine.length()-3); 
                             i = in.readLine(line, 0, 128); 
                             i = in.readLine(line, 0, 128); 
                             newLine = new String(line, 0, i); 
                             StringBuffer fieldValue = new StringBuffer(128); 
                             while (i != -1 && !newLine.startsWith(boundary)) { 
                                   // 最后一行包含换行字符 
                                   // 因此我们必须检查当前行是否是最后一行 
                                   i = in.readLine(line, 0, 128); 
                                   if ((i==boundaryLength+2 || i==boundaryLength+4) && (new String(line, 0, i).startsWith(boundary))) 
                                         fieldValue.append(newLine.substring(0, newLine.length()-2)); 
                                   else 
                                         fieldValue.append(newLine); 
                                   newLine = new String(line, 0, i); 
                             } 
                             fields.put(fieldName, fieldValue.toString()); 
                       } 
                 } 
                 i = in.readLine(line, 0, 128); 
           } 
     } 
}

本文转自 豪客 51CTO博客,原文链接:http://blog.51cto.com/wakan/7209,如需转载请自行联系原作者

用 Java Servlet 实现文件上载(老文新发)相关推荐

  1. 老文新读 | 大数据于国内影视行业的意义及应用

    编者注:本文由作者4年前发表于知乎专栏,前两天编者偶然读到,觉得很有意思,于是转过来与大家分享.原文标题:评析:触不到的大数据 作者 | 王义之,凡影合伙人 最近这几年,我们可以听到很多关于大数据在影 ...

  2. java servlet html文件_Servlet生成html页面

    Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容. 该Se ...

  3. Word处理控件Aspose.Words功能演示:在 Java 中将 HTML 文件转换为 Word 文档

    在各种情况下,您可能需要将 HTML 内容转换为 Word 文档.例如,用于从所见即所得 HTML 编辑器生成文档或将网页转换为 DOCX 或 DOC 格式.为了以编程方式执行此转换,本文介绍了如何将 ...

  4. Java Servlet实现文件上传下载操作

    1.配置对应的文件,导入相应的包 对应包下载地址:https://wws.lanzous.com/ipFtEoyv1je 2.编写jsp页面 代码如下: <%@ taglib prefix=&q ...

  5. drop box java_使用Dropbox Java API将文件上载到Dropbox

    我想使用Java API for DropBox上传文件.以下代码为我提供了oauth_token和oauth_secret.但是当我尝试上传文件时,我得到一个例外. Java类 package co ...

  6. 文件上载限制4gb_新get!百度网盘破除上传单个文件超4GB限制

    现在网盘的限制是越来越多,国内还在免费+能用的也就剩百度网盘了.虽然随便注册个账号大家都能凑合用用,但非会员5GB的使用空间.上传/下载速率限制,加上下载大文件必须使用网盘客户端等,让小编发现百度网盘 ...

  7. 老文新看:一口气说透中台--给你架构师的视角

    编 辑:彭文华 来 源:大数据架构师(ID:bigdata_arch) 彭友们好,我是你的老彭友.因公众号迁移,老文章都搜索不到了.这是之前的原创文章,现在重新再发一遍,看过的彭友直接略过就行哈~~ ...

  8. 旧文新发----真情与技巧的交响

    真情与技巧的交响 ­--读二熊诗词有感 杨森翔 今年"五一"前,接到诗词学会寄来的<寒塘韵语>,顺手翻开,便读到五言律绝<飞鸿><秋草>< ...

  9. 侵略伤痕(旧文新发)

    这篇文章是我中学时候几篇作品的集合,最初发表在红袖添香,不知道这些年她经历了什么,今天在自己的博客,再次将这个文章发出来.只为祭奠,那逝去的青春,那些错过的人.那时候的爱情那样简单,爱的遮遮掩掩,被爱 ...

最新文章

  1. 现代软件工程 作业 团队冲刺阶段的要求
  2. 格雷编码Python解法
  3. Spring 环境与profile(一)——超简用例
  4. 企业网站 源码 服务邮箱:_公司企业邮箱购买,外贸企业邮箱用哪家服务好?
  5. HDU Integer's Power(容斥原理)
  6. 反函数连续性定理 反三角_高中数学:三角函数诱导公式及诱导公式口诀
  7. 爱普生690k打印针测试软件_办公室打印机什么牌子好 办公室打印机怎么选购【详解】...
  8. php定时器使用,PHP定时器的说明
  9. 显著性检测(saliency detection)评价指标之KL散度距离Matlab代码实现
  10. 将SpringBoot项目打包并部署到服务器
  11. 怎么创建css样式表,为HTML5表单创建CSS样式
  12. pdf文档统计字数的问题
  13. 记录archlinux第n次修复引导区
  14. PDF图片怎么提取?看完这篇你就会了
  15. EBS中应用,职责,数据组,请求组等关系
  16. hive面试题总结(2020最新版)
  17. DDR controller控制器之AXI接口模块设计
  18. 2.企业发放的奖金根据利润提成。
  19. 查看锐捷poe交换机供电状态_锐捷 RG-S2910-24GT4SFP-UP-H 24个电口支持PoE和PoE+供电交换机...
  20. 使用qemu在windows系统下搭建树莓派3b环境运行RT-Thread

热门文章

  1. 疯狂Java讲义(五)----第二部分
  2. Java第三次平时作业
  3. 新一代前端框架的探索与思考
  4. 从键盘接收一百分制成绩( 0~100),要求输出其对应的成绩等级 A~E。其中,90 分以上为'A',80~89 分为'B', 70~79分为'C', 60~69分为'D', 60 分以下为'E'。
  5. 合同法律风险管理 动态合同履约衔接与函件往来
  6. 省常中NOIP模拟 失意failure
  7. lordoftheroot
  8. 感想,记一次沙盘游戏
  9. 吴恩达机器学习ex3多类别分类
  10. 八大排序(JAVA)