Java中的文件上传(原始Servlet实现)

从原始的Servlet来实现文件的上传,代码如下:

参考:https://my.oschina.net/Barudisshu/blog/150026

采用的是Multipart/form-data的方式上传文件。针对Multipart/form-data方式的上传解释,参考如下文件:

http://www.onmpw.com/tm/xwzj/network_35.html

http://892848153.iteye.com/blog/1847467

http://blog.csdn.net/five3/article/details/7181521

下面为具体的实现方式:

1、通过getInputStream()取得上传文件。

注意:这种方式相当的原始,通过分析body中的字符,然后再进行硬编码切割出文件字节,再进行保存。

JSP:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <div>
            <form action="UploadServlet" method="POST" enctype="multipart/form-data">
                <table>
                    <tr>
                        <td><label for="file1">文件1:</label></td>
                        <td><input type="file" id="file1" name="file"></td>
                    </tr>
                    <tr>
                        <td><label for="file2">文件2:</label></td>
                        <td><input type="file" id="file2" name="file"></td>
                    </tr>
                    <tr>
                        <td><label for="file3">文件3:</label></td>
                        <td><input type="file" id="file3" name="file"></td>
                    </tr>
                    <tr>
                        <td><label for="file3">Text:</label></td>
                        <td><input type="text" id="text1" name="text1"></td>
                    </tr>
                    <tr>
                        <td colspan="2"><input type="submit" value="上传" name="upload"></td>
                    </tr>
                </table>
            </form>
        </div>
    </body>
</html>

Servlet:

提示:使用了servlet3.0的标注免配置功能。

package uploadtest;


import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class UploadServlet
 */
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public UploadServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        this.processRequest(request, response);
    }
    
    //通过getInputStream()取得上传文件。循环多文件
    /**
     * Processes requests for both HTTP
     * <code>POST</code> methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        //读取请求Body
        byte[] body = readBody(request);
        //取得所有Body内容的字符串表示
        String textBody = new String(body, "ISO-8859-1");
        
        //取得文件区段边界信息,并通过边界循环获取文件流
        String contentType = request.getContentType();
        String boundaryText = String.format("--%1$s", contentType.substring(contentType.lastIndexOf("=") + 1, contentType.length()));
        for(String tempTextBody : textBody.split(boundaryText)){//多文件循环
            if(tempTextBody.length()>0){
                //取得上传的文件名称
                String fileName = getFileName(tempTextBody);
                if(fileName.length()>0){
                    //取得文件开始与结束位置
                    Position p = getFilePosition(tempTextBody);
                    //输出至文件
                    writeTo(fileName, tempTextBody.getBytes("ISO-8859-1"), p);                
                }
            }
        }
    }

    /**
     * 文件起始位置类
     *
     */
    class Position {

        int begin;
        int end;

        public Position(int begin, int end) {
            this.begin = begin;
            this.end = end;
        }
    }

    /**
     * 获取request的getInputStream,返回byte
     * 
     * @param request 
     * @return
     * @throws IOException
     */
    private byte[] readBody(HttpServletRequest request) throws IOException {
        //获取请求文本字节长度
        int formDataLength = request.getContentLength();
        //取得ServletInputStream输入流对象
        DataInputStream dataStream = new DataInputStream(request.getInputStream());
        byte body[] = new byte[formDataLength];
        int totalBytes = 0;
        while (totalBytes < formDataLength) {
            int bytes = dataStream.read(body, totalBytes, formDataLength);
            totalBytes += bytes;
        }
        return body;
    }

    /**
     * 获取文件起始位置
     * 
     * @param request
     * @param textBody
     * @return
     * @throws IOException
     */
    private Position getFilePosition(String textBody) throws IOException {
        //取得实际上传文件的起始与结束位置
        int pos = textBody.indexOf("filename=\"");
        pos = textBody.indexOf("\n", pos) + 1;
        pos = textBody.indexOf("\n", pos) + 1;
        pos = textBody.indexOf("\n", pos) + 1;
        int begin = ((textBody.substring(0, pos)).getBytes("ISO-8859-1")).length;
        int end = textBody.getBytes("ISO-8859-1").length;

        return new Position(begin, end);
    }

    /**
     * 获取文件名
     * 
     * @param requestBody
     * @return
     */
    private String getFileName(String requestBody) {
        try {
            String fileName = requestBody.substring(requestBody.indexOf("filename=\"") + 10);
            fileName = fileName.substring(0, fileName.indexOf("\n"));
            fileName = fileName.substring(fileName.indexOf("\n") + 1,fileName.indexOf("\""));
            // 取扩展名加随机数进行重命名
            fileName = new SimpleDateFormat("yyyyMMddHHmmsssss").format(new Date())+java.util.UUID.randomUUID() + fileName.substring(fileName.lastIndexOf("."),fileName.length());
            return fileName;
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 写文件
     * 
     * @param fileName
     * @param body
     * @param p
     * @throws IOException
     */
    private void writeTo(String fileName, byte[] body, Position p) throws IOException {
        String filePath = this.getServletContext().getRealPath("")+"/Uploads/"+new SimpleDateFormat("yyyyMMdd").format(new Date())+"/";//初始化保存的位置
        Tools.isExistDir(filePath);//看目录是否已经创建        
        FileOutputStream fileOutputStream = new FileOutputStream(filePath + fileName);
        fileOutputStream.write(body, p.begin, (p.end - p.begin));
        fileOutputStream.flush();
        fileOutputStream.close();
    }

} 

测试工程:https://github.com/easonjim/5_java_example/tree/master/uploadtest/test1

2、通过getPart()、getParts()取得上传文件。

Servlet3.0中新增了getPart()和getParts()函数用来处理上传文件,getPart()用于上传单文件,getParts()用于上传多个文件。详细参考:http://blog.csdn.net/new_one_object/article/details/51373802

同样的,用此方法只支持multipart/form-data请求类型的文件上传。

还有一点,在Servlet上必须标注特性标记头@MultipartConfig,以表示是multipart/form-data类型的MIME。

JSP:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <div>
            <form action="UploadServlet" method="POST" enctype="multipart/form-data">
                <table>
                    <tr>
                        <td><label for="file1">文件1:</label></td>
                        <td><input type="file" id="file1" name="file1"></td>
                    </tr>
                    <tr>
                        <td><label for="file2">文件2:</label></td>
                        <td><input type="file" id="file2" name="file2"></td>
                    </tr>
                    <tr>
                        <td><label for="file3">文件3:</label></td>
                        <td><input type="file" id="file3" name="file3"></td>
                    </tr>
                    <tr>
                        <td><label for="file3">Text:</label></td>
                        <td><input type="text" id="text1" name="text1"></td>
                    </tr>
                    <tr>
                        <td colspan="2"><input type="submit" value="上传" name="upload"></td>
                    </tr>
                </table>
            </form>
        </div>
    </body>
</html>

Servlet:

提示:使用了servlet3.0的标注免配置功能。

package uploadtest;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

/**
 * Servlet implementation class UploadServlet
 */
@MultipartConfig
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public UploadServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        this.processRequest(request, response);
    }
    
    //通过getPart()、getParts()取得上传文件。单文件
    /**
     * Processes requests for both HTTP
     * <code>POST</code> methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Part part = request.getPart("file1");//参数就是input标签的name属性,且这个name必须是唯一的
        String fileName = getFileName(part);
        writeTo(fileName, part);
    }

    //取得上传文件名
    private String getFileName(Part part) {
        String header = part.getHeader("Content-Disposition");
        String fileName = header.substring(header.indexOf("filename=\"") + 10, header.lastIndexOf("\""));
        // 取扩展名加随机数进行重命名
        fileName = new SimpleDateFormat("yyyyMMddHHmmsssss").format(new Date())+java.util.UUID.randomUUID() + fileName.substring(fileName.lastIndexOf("."),fileName.length());
        return fileName;
    }

    //存储文件
    private void writeTo(String fileName, Part part) throws IOException, FileNotFoundException {
        InputStream in = part.getInputStream();
        String filePath = this.getServletContext().getRealPath("")+"/Uploads/"+new SimpleDateFormat("yyyyMMdd").format(new Date())+"/";//初始化保存的位置
        Tools.isExistDir(filePath);//看目录是否已经创建        
        OutputStream out = new FileOutputStream(filePath + fileName);
        byte[] buffer = new byte[1024];
        int length = -1;
        while ((length = in.read(buffer)) != -1) {
            out.write(buffer, 0, length);
        }

        in.close();
        out.close();
    }


}

通过上面的代码很明显的区别出这个方法简单明了,省去了切割字符串的问题。

接下来再升级简化一下流的写入,将用到@MultipartConfig特性中的location属性,这个属性将指定一个本地目录,然后调用wtite方法直接写入。不过这个也有一个不好的特点,路径是死的,没法按照自定义输出。

Servlet改造如下:

package uploadtest;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

/**
 * Servlet implementation class UploadServlet
 */
@MultipartConfig(location = "d:\\\\workspace")
@WebServlet("/UploadServlet3")
public class UploadServlet3 extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public UploadServlet3() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        this.processRequest(request, response);
    }
    
    //通过getPart()、getParts()取得上传文件。单文件
    /**
     * Processes requests for both HTTP
     * <code>POST</code> methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Part part = request.getPart("file1");//参数就是input标签的name属性,且这个name必须是唯一的
        String fileName = getFileName(part);
        //将文件写入location指定的目录
        part.write(fileName);
    }

    //取得上传文件名
    private String getFileName(Part part) {
        String header = part.getHeader("Content-Disposition");
        String fileName = header.substring(header.indexOf("filename=\"") + 10, header.lastIndexOf("\""));
        // 取扩展名加随机数进行重命名
        fileName = new SimpleDateFormat("yyyyMMddHHmmsssss").format(new Date())+java.util.UUID.randomUUID() + fileName.substring(fileName.lastIndexOf("."),fileName.length());
        return fileName;
    }


}

对于@MultipartConfig更多的解释,参考:http://blog.csdn.net/chenqipc/article/details/50551450

测试工程(包含了多文件的实现):https://github.com/easonjim/5_java_example/tree/master/uploadtest/test2

 

posted @ 2017-03-15 15:53  EasonJim  阅读(1317)  评论(0编辑  收藏  举报