服务器通过Servlet接收文件

       在上传文件时,需要分析multipart/form-data格式数据,从客户浏览器传递的数据中分离出表示文件内容的二进制数据和表示相关属性的文本数据。使用Servlet直接根据HTTP协议处理请求消息。

      利用Servlet得到上传的文件及上传文件混合在一起的表单域信息,不是件容易的事。因为所有这些信息都在请求实例中,所以需要对请求实例进行深入的分析。
  实际问题已经转化为对一段固定格式的文本进行分析,并得出特定标记下的内容。在Java中用面向对象的方法处理这类问题,一般是将文本以流的形式读 入。当读到特定标记时,可以触发一个事件。在事件的实现方法中进行数据处理,在读的过程中根据读到的标记不同可以设一个状态变量来记录当前的状态。用这种 方式处理的好处是:以流的方式读入文本,文本的大小不受限制,可以处理大型文本,效率也比较高。
  读取表单域的文本值和通过表单上传的文件数据,是通过UploadFileServlet完成的。其完整程序如下:
[code]
package com.ren.servlet;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
/**
* <p>Title: 第一事业部技术论坛</p>
* <p>Description: 文件上传和表单分析</p>
* <p>Copyright: Copyright (c) 2005</p>
* <p>Company: LCW</p>
* @author 任再旺
* @version 1.0
*/
public class UploadFileServlet extends HttpServlet {
        public void doPost(HttpServletRequest req, HttpServletResponse res) 
                        throws ServletException, IOException {
                final int NONE = 0;                          // 状态码,表示没有特殊操作
                final int DATAHEADER = 1;                 // 表示下一行要读到报头信息
                final int FILEDATA = 2;                 // 表示下面要读的是上传文件和二进制数据
                final int FIELDDATA = 3;                 // 表示下面要读到表单域的文本值
                
                // 请求消息实体的总长度(请求消息中除消息头之外的数据长度)
                int totalbytes = req.getContentLength();
                
                // 容纳请求消息实体的字节数组
                byte[] b = new byte[totalbytes];
                // 请求消息类型
                String contentType = req.getContentType();
                
                String fieldname = "";                         // 表单域的名称
                String fieldvalue = "";                 // 表单域的值
                String filename = "";                         // 上传的文件名称
                String boundary = "";                          //分界符字符串
                String lastboundary = "";                 // 结束分界符字符串
                
                int fileSize = 0; // 文件长度
                
                // 容纳表单域的名称/值的哈希表
                Hashtable formfields = new Hashtable();
                
                // 在消息头类型中找到分界符的定义
                int pos = contentType.indexOf("boundary=");
                
                String fileID;  // 上传的文件ID
                
                if (pos != -1) {
                        pos += "boundary=".length();
                        boundary = "--" + contentType.substring(pos); // 解析出分界符
                        lastboundary = boundary + "--"; // 得到结束分界符
                }
                
                int state = NONE;                         // 起始状态为NONE
                
                // 得到请求消息的数据输入流
                DataInputStream in = new DataInputStream(req.getInputStream());
                in.readFully(b);          // 根据长度,将消息实体的内容读入字节数组b中
                in.close();                 // 关闭数据流
                String reqcontent = new String(b); // 从字节数组中得到表示实体的字符串
                
                // 从字符串中得到输出缓冲流
                BufferedReader reqbuf = new BufferedReader(new StringReader(reqcontent));
                
                // 设置循环标志
                boolean flag = true;
                int i = 0;
                while (flag == true) {
                        String s = reqbuf.readLine();
                        if (s == lastboundary || s == null) break;
                        switch (state) {
                        case NONE:
                                if (s.startsWith(boundary)) {
                                        // 如果读到分界符,则表示下一行一个头信息
                                        state = DATAHEADER;
                                        i += 1;
                                }
                                break;
                        case DATAHEADER:
                                pos = s.indexOf("filename=");
                                // 先判断出这是一个文本表单域的头信息,还是一个上传文件的头信息
                                if (pos == -1) {
                                        // 如果是文本表单域的头信息,解析出表单域的名称
                                        pos = s.indexOf("name=");
                                        pos += "name=".length() + 1; // 1表示后面的"的占位
                                        s = s.substring(pos);
                                        int l = s.length();
                                        s = s.substring(0, l-1);
                                        fieldname = s; // 表单域的名称放入fieldname
                                        state = FIELDDATA; //设置状态码,准备读取表单域的值
                                } else {
                                        // 如果是文件数据的头,先存储这一行,用于在字节数组中定位
                                        String temp = s;
                                        // 先解析出文件名
                                        pos = s.indexOf("filename=");
                                        pos += "filename=".length() + 1; //1表示后面的"的占位
                                        s = s.substring(pos);
                                        int l = s.length();
                                        s = s.substring(0, l-1);
                                        pos = s.lastIndexOf("//");
                                        s = s.substring(pos + 1);
                                        filename = s;                 // 文件名存入filename
                                        // 下面这一部分从字节数组中取出文件的数据
                                        pos = byteIndexOf(b, temp, 0); // 定位行
                                        
                                        // 定位下一行,2 表示一个回车和一个换行占两个字节
                                        b = subBytes(b, pos + temp.getBytes().length+2, b.length);
                                        
                                        // 再读一行信息,是这一部分数据的Content-type
                                        s = reqbuf.readLine();
                                        
                                        // 设置文件输入流,准备写文件
                                        File f = new File("F:/java-workspace/luntan/html/image/" + filename);
                                        DataOutputStream fileout = new DataOutputStream(new FileOutputStream(f));
                                        
                                        // 字节数组再往下一行,4表示两回车换行占4个字节,本行的回车换行2个字节,Content-type的下
                                        // 一行是回车换行表示的空行,占2个字节
                                        // 得到文件数据的起始位置
                                        b = subBytes(b, s.getBytes().length + 4, b.length);
                                        pos = byteIndexOf(b, boundary, 0);         // 定位文件数据的结尾
                                        b = subBytes(b, 0, pos - 1);                 //取得文件数据
                                        fileout.write(b, 0, b.length - 1);         //将文件数据存盘
                                        fileSize = b.length - 1;                         //文件长度存入fileSize
                                        state = FIELDDATA;
                                }
                                break;
                        case FIELDDATA:
                                // 读取表单域的值
                                s = reqbuf.readLine();
                                fieldvalue = s;                 // 存入fieldvalue
                                formfields.put(fieldname, fieldvalue);
                                state = NONE;
                                break;
                        case FILEDATA:
                                // 如果是文件数据不进行分析,直接读过去
                                while ((!s.startsWith(boundary)) && (!s.startsWith(lastboundary))) {
                                        s = reqbuf.readLine();
                                        if (s.startsWith(boundary)) {
                                                state = DATAHEADER;
                                        } else {
                                                break;
                                        }
                                }
                                break;
                        }
                        
                }
                // 指定内容类型,并且可以显示中文
                res.setContentType("text/html;charset=gb2312");
                PrintWriter out = res.getWriter();
                out.println("<HTML");
                out.println("<HEAD><TITLE>文件上传结果</TITLE></HEAD>");
                out.println("<BODY>");
                out.println("<H1>文件上传结果</H1><hr>");
                out.println("ID为" + formfields.get("FileID") + "的文件" + filename + "已经上传!" + "文件长度为:" + fileSize + "字节");
                out.println("</BODY>");
                out.println("</HTML>"); 
        }
        /**
         * 字节数组中的indexOf函数,与String类中的indexOf类似
         * b 要搜索的字节数组
         * s 要找的字符串
         * start 搜索的起始位置
         * 如果找到,返回s的第一个字节在b中的下标,没有则返回-1
         */
        private static int byteIndexOf(byte[] b, String s, int start) {
                return byteIndexOf(b, s.getBytes(), start);
        }
        /**
         * 字节数组中的indexOf函数,与String类中的indexOf类似
         * b 要搜索的字节数组
         * s 要找的字节数组
         * start 搜索的起始位置
         * 如果找到,返回s的第一个字节在b中的下标,没有则返回-1
         */
        private static int byteIndexOf(byte[] b, byte[] s, int start) {
                int i;
                if (s.length == 0) {
                        return 0;
                }
                int max = b.length - s.length;
                if (max < 0) {
                        return -1;
                }
                if (start > max) {
                        return -1;
                }
                if (start < 0) {
                        start = 0;
                }
                // 在b中找到s的第一个元素
        search:
                for (i = start; i <= max; i++) {
                        if (b[i] == s[0]) {
                                // 找到了s中的第一个元素后,比较剩余的部分是否相等
                                int k = 1;
                                while (k < s.length) {
                                        if (b[k + i] != s[k]) {
                                                continue search;
                                        }
                                        k++;
                                }
                                return i;
                        }
                }
                return -1;
        }
        /**
         * 用于从一个字节数组中提取一个字节数组
         * 类似于String 类的substring()
         */
        private static byte[] subBytes(byte[] b, int from ,int end) {
                byte[] result = new byte[end - from];
                System.arraycopy(b, from, result, 0, end - from);
                return result;
        }
        
        /**
         * 用于从一个字节数组中提取一个字符串
         * 类似于String类的substring()
         */
        private static String subBytesString(byte[] b, int from, int end) {
                return new String(subBytes(b, from, end));
        }
}
[/code]

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Servlet是Java编写的服务器端程序,它可以接收来自Web客户端(例如Web浏览器)的HTTP请求,并生成HTTP响应。Servlet通常用于动态Web应用程序中,可以根据用户的请求生成动态内容,例如生成网页、读取数据库或者执行其他业务逻辑。在Java Web开发中,Servlet是一个非常重要的组件。 ### 回答2: 在Java Web开发中,Servlet是一种服务器端的程序,用于处理客户端的请求并生成响应。配置文件中的Servlet指的是将Servlet程序与具体的URL路径、请求和响应进行绑定的配置信息。 通常情况下,Servlet的配置信息会被写入Web应用程序的配置文件中,如web.xml或注解中。配置文件中包含了Servlet的名称、类名、URL映射、初始化参数等信息。 配置文件中的Servlet起到了两个重要的作用。首先,它将Servlet与具体的URL路径绑定在一起,当客户端请求某个URL时,Servlet容器会根据配置文件中的信息选择合适的Servlet来处理请求。其次,配置文件中的Servlet还可以指定Servlet需要的初始化参数,以提供给Servlet在初始化时使用,这些参数可以用于配置Servlet的行为或传递一些数据。 例如,一个名为HelloServletServlet在配置文件中的配置可能如下所示: ```xml <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.example.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> ``` 上述配置中,将HelloServlet与路径“/hello”进行了绑定。当客户端请求“/hello”时,Servlet容器会实例化并调用com.example.HelloServlet来处理请求。 配置文件中的Servlet是一种将Servlet与URL路径进行映射的机制,能够帮助开发者按照需要分配请求给不同的Servlet处理,并方便地传递初始化参数。 ### 回答3: 配置文件中的Servlet是一种Java程序,用于处理来自Web服务器的客户端请求。Servlet工作在服务器端,接收客户端的请求,并生成相应的响应。Servlet可以接收多种类型的请求,如GET、POST等,并根据请求的不同执行相应的操作。 在配置文件中,一般通过XML格式来配置Servlet。配置文件中包含了Servlet的名称、URL映射、Servlet类名等信息。通过配置文件,可以将Servlet与特定的URL路径关联起来,使得当客户端访问该URL时,由相应的Servlet来处理请求。 配置文件中还可以定义Servlet的初始化参数,即给Servlet传递一些特定的参数值。这些参数可以通过getInitParameter()方法在Servlet中获取并使用。通过初始化参数,可以在不修改源码的情况下,灵活地配置Servlet的一些自定义属性。 配置文件中还可以定义Servlet的加载顺序,即指定Servlet在Web应用启动时的加载顺序。这样可以确保某些Servlet在其他Servlet之前加载,以满足特定需求。 总之,配置文件中的Servlet是指在Web应用部署时,通过配置文件来定义和配置的用于处理客户端请求的Java程序。通过配置Servlet,能够实现灵活的URL映射和参数配置,使得Web应用能够根据不同的请求做出相应的处理和响应。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值