你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 网络与通信
4.8 Http上传技术的Java实现
 

许多基于B/S结构的Web系统都有直接通过网页上传文件的功能。Web程序员通常都是调用现成的第三方文件上传组件来实现,就JSP来说,国内使用的一般是JspSmartJavaBean组件,目前JspSmart已经不再提供该类型组件,今后再有类似的开发需求就需要开发人员自行实现。其实,这些Beans采用的都是基于Http协议的文件上传技术,其原理及规范已在RFC文档中说明,实现并不很难。本文将通过一个实例来介绍如何编写一个JavaBean来实现Web上传技术。

一、上传原理

在最初的Http 协议中,没有上传文件方面的功能。rfc1867 (http://www.ietf.org/rfc/rfc1867.txt) http 协议添加了这个功能。客户端的浏览器,如 Microsoft IE Mozila Opera 等,按照此规范将用户指定的文件发送到服务器。服务器端的网页程序,如PHPASPJSP 等,可以按照此规范,解析出用户发送来的文件。Microsoft IEMozilaOpera 已经支持此协议,在网页中使用一个特殊的 form 就可以发送文件。绝大部分 Web服务器 ,包括 tomcat ,已经支持此协议,可接受发送来的文件。

要上传文件,提交数据的formenctype必须为“multipart/form-data”,method必须是“post”,同时要在form中添加typefileinput标记,如下例(010305)

Line 01: <form method=post action="show1.jsp" enctype="multipart/form-data">

Line 02: file,name=f<br>

Line 03: <input type=file name=f size=50><br>

Line 04: file,name=f1<br>

Line 05: <input type=file name=f1 size=50><br>

Line 06: text,name=mytext<br>

Line 07: <input type=text name=mytext size=50><br>

Line 08: checkbox, name=mycheck<br>

Line 09: <input type="checkbox" name="mycheck" value=check1>value=check1<br>

Line 10: <input type="checkbox" name="mycheck" value=check2>value=check<br>

Line 11: <input type="checkbox" name="mycheck" value=check3>value=check<br>

Line 12: <input type="submit" value="upload">

Line 13: </form>

通过该表单,浏览器向Web服务器上传一个包含文件数据的报文,报文主体格式如下:

-----------------------------7d52e01220324

Content-Disposition: form-data; name="f"; filename="filename1"

Content-Type: plain/Text

文件数据

-----------------------------7d52e01220324

Content-Disposition: form-data; name="f1"; filename="filename2"

Content-Type: application/octet-stream

文件数据

-----------------------------7d52e01220324

Content-Disposition: form-data; name="myText"

变量值

-----------------------------7d52e01220324

Content-Disposition: form-data; name="mycheck"

变量值

-----------------------------7d52e01220324

Content-Disposition: form-data; name="mycheck"

变量值

-----------------------------7d52e01220324—

其中,------------------7d52e01220324分隔符,分隔多个文件、表单项,报文的每一行都以“\r\n”结束。我们只需编写一个Java Bean在服务器端运行,解析报文,将文件数据保存为相应的文件即可。

二、程序实现

主要包括三个类:

1Class Parameter ,每一个Parameter对象负责客户端传过来的非文件类型变量的信息存取操作,包括变量名、变量值等。

主要方法包括:

public void setParaName(String name)  赋变量名

public String getParaName() 取变量名

public void addParaValue() 添加变量值(变量可以是多值变量,故变量的名值是一对多的关系)

public String[] getParaValues() 取变量值,返回一组数值

2Class UploadFile,每一个UploadFile对象描述一个文件类型数据的相关信息

主要方法包括:

public void setRemotePathName(String str) 赋上传文件在客户端的路径名和文件名

public String getRemotePathName() 取上传文件在客户端的路径名

public Striang getFileName() 取文件名

public void setLocalPathName(String str) 赋上传文件在服务器端的存储路径

public String getLocalPathName() 取上传文件在服务器端的存储路径

public void setContentDisp(String str) Content-Disposition

public String getContentDisp() Content-Disposition

public void setContentType(String str) Content-Type

public String getContentType() Content-Type

3Class Upload,解析上传的http报文,将要上传的文件存储到服务器端指定的路径。下主要方法包括:

public void init(PageContext pc) 初始化

public Enumeration getFiles() 获取UploadFile对象集合

public int getFileCount() 获取UploadFile对象数量,即上传文件数

public Enumeration getParameterNames() 获取非文件类型变量名集合

public String[] getParameterValues(String key) 获取变量名为key的变量值

public void uploadFiles() throws Exception 上传文件

public void setPath(String path) 设置上传文件在服务器上的保存路径

Upload通过方法init来取得request对象和服务器当前路径,通过方法uploadFiles在服务器端保存文件。

由于篇幅有限,现在只介绍类Upload 中的解析报文保存文件的核心程序。

1Upload要引用的类库

import edu.tjut.cs.minipuss.UploadFile;// 自定义的UploadFile

import javax.servlet.jsp.PageContext;

import javax.servlet.ServletRequest;

import javax.servlet.ServletInputStream;

import java.io.*;

import java.util.*;

其中javax.servlet类包不在J2SE中,通常在我们使用的JSP服务器(如Tomcat)中会包含该类包的jar,本文所使用的服务器是Resin2.1.0,Resinlib目录下的jsdk23.jar即包含该类包,编译时在classpath中指定该jar即可。

2)成员变量和类的初始化

1)类Upload 包括如下5个成员变量:

    private PageContext pageContext;//pageContext对象

    private ServletRequest request;// request对象

    private Vector vector;// 非文件类型数据列表

    private Vector files; // 文件类型数据列表

    private String savePath;// 文件上传路径

2)构造函数,用于初始化成员变量

    public Upload(){

        pageContext = null;

        request = null;

        vector = new Vector();// 创建列表

        files = new Vector();// 创建列表

        savePath = null;

    }

3) 初始化函数

    public void init(PageContext pc){

    // 初始化

        if(pc !=null){

            this.pageContext = pc;

            this.request = this.pageContext.getRequest();

            // 取服务器端当前路径为上传路径

            this.savePath = this.request.getRealPath("/");

        }

    }

3ploadFiles函数及其调用的私有函数

1getNoteString, 取一行报文,不含回车换行符

private String getNoteString(ServletInputStream in){

        StringBuffer sb = new StringBuffer();

        int c;

        try{

            while( (c= in.read())!= -1){

                char ch =(char) c;

                sb.append(ch);

                if(ch=='\n') break;

            }

        }catch(IOException e){

        }

        //截取子串,去掉回车换行符

        String str = sb.toString();

        int endIndex = str.lastIndexOf("\r\n");

        if (endIndex==-1)

           endIndex = str.lastIndexOf('\n');

        return str.substring(0,endIndex);

    }

2getLine,读取一行报文,含回车换行符

private int getLine(ServletInputStream in,StringBuffer sb){

        int c=0;

        sb.delete(0,sb.length());//  清空

        try{

            while( (c= in.read())!= -1){

                char ch =(char) c;

                sb.append(ch);

                if(ch=='\n') break;

            }

        }catch(IOException e){

            return -1;

        }

        return c;// 若报文读完,则C=-1

    }

3getFileName,解析传入的字串,获取文件路径名

private String getFileName(String str){

    //str:Content-Disposition: form-data; name="xx"; filename="xxxx"

        int endIndex = str.lastIndexOf('"');

        int startIndex = str.indexOf("filename")+10;

        return str.substring(startIndex,endIndex);

}

类似函数还有getNamegetContentDispgetContentType,处理方法同上。

4)向非文件型数据列表添加Parameter对象,并返回该对象的引用

private Parameter addParameter(String str){

        //若列表中有数据名=str的对象,则返回该对象的引用

        int size = this.vector.size();

        for(int i=0;i<size;i++){

            Parameter p = (Parameter) this.vector.get(i);

            if(str.compareTo(p.getParaName())==0)

                return p;

        }

        //若列表中没有数据名=str的对象,则创建之

        Parameter pa = new Parameter();

        pa.setParaName(str);// 令数据名为str

        this.vector.add(pa);// 加入列表

        return pa;

    }

5 uploadFiles,保存上传文件和非文件型数据

public void uploadFiles() throws Exception{

        // 按二进制流读取报文

        ServletInputStream in = this.request.getInputStream();

        StringBuffer sb = new StringBuffer();

        String noteString = getNoteString(in);// 取起始标记串

        int c=0;

        UploadFile uf = null;

        Parameter para = null;

        // 读取报文并做相应处理直到报文结束

        while((c=getLine(in,sb))!=-1){

            // 如果当前段描述的是一个文件型数据,则保存文件到服务器

            if( sb.indexOf("filename")!=-1){

                uf = new UploadFile();// 创建文件描述对象

                uf.setRemotePathName(getFileName(sb.toString()));

                uf.setContentDisp(getContentDisp(sb.toString()));

                uf.setLocalPathName(this.savePath);

                c = getLine(in,sb);// Content-Type

                uf.setContentType(getContentType(sb.toString()));

                c = getLine(in,sb);// 除去空行

                //如果上传文件名不为空,写文件

                if(uf.getFileName().length()!=0){

                    files.add(uf); // 将文件描述对象加入文件列表

                    try{

                        FileOutputStream fo = new

FileOutputStream(this.savePath+"\\"+uf.getFileName());

                        //读一行,写入一行,直到遇到段结束标记

                        String strTemp = null;

                        c = getLine(in,sb);// 读文件的第一行

                        do{

                            strTemp = sb.toString();

                            c = getLine(in,sb);// 读下一行

                            if(sb.indexOf(noteString)==-1){

                                for(int i=0;i<strTemp.length();i++)

                                    fo.write((byte)strTemp.charAt(i));

                            }else{

                            // 如果下一行是段结束标记,

                            // 不将前一行行末的回车换行符写入文件

                                for(int i=0;i<strTemp.length()-2;i++)

                                    fo.write((byte)strTemp.charAt(i));

                                break;// 文件结束,跳出

                            }

                        }while(true);

                        fo.close();// 关闭文件

                    }catch(IOException ioe){

                        // 若文件处理失败,将文件描述对象从列表中移除

                        files.remove(uf);

                        return;

                    }

                }else{ // 上传文件名为空

                    c = getLine(in,sb);// 除去正文空行

                    c = getLine(in,sb);// 取段结束标记行

                }

            }else{

            //若不是文件型数据,将数据存为Parameter对象,并加入列表

                para = addParameter(getName(sb.toString()));

                c = getLine(in,sb);// 除去空行

                para.addParaValue(getNoteString(in));// 存储数据值

                c = getLine(in,sb);// 取段结束标记行

            }

        }

    }

4)编译执行

上述代码在Windows2000 Professionalj2sdk1.4环境下编译成功,只需将编译好的类作为BeanJSP页中调用即可。本文所用的Web服务器为Resin 2.1.0,具体的环境配置方法可参考Resin的文档。

四、结语

目前软件架构正在向基于B/S结构的Web应用模式发展,越来越多的企业级应用开始采用Web技术。应用Java技术实现Web上传很有实用价值。本文给出了通用的方法,读者可以根据实际应用的需要进行必要的改变而加以利用。

 

  推荐精品文章

·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录
·2023年10月目录
·2023年9月目录 
·2023年8月目录 
·2023年7月目录
·2023年6月目录 
·2023年5月目录
·2023年4月目录 
·2023年3月目录 
·2023年2月目录 
·2023年1月目录 

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089