JSP上传文件 规格严格

背景:

3月,公司的实习生来了,每年都会招,招来之后人家看明白你公司环境后,毕业就和你拜拜了。不管怎么样,部门要求培训、要求每天写日报,这下可好,我们组有5个实习生,每个5个Excel,天天如此,而且Excel每个1M,我就得天天删来删去,不仅仅我们小组的,别的小组也这样,于是,我就想能不能部署个傻瓜应用,每天你们上传上去,我去下载,然后我在上传上去。呵呵,这个想法比较幼稚啊。。。。。。。。。。对于开发Web的各位来说。

我Web的功力实在太差,于是正好前一阵子写了一个web小程序,一直处于开发进展中,我就加个小功能吧,先把原型搭出来。上网查了一下: 如果想要上传的话必须把form的enctype的格式设置成“multipart/form-data”,注意默认是什么URLEncoded的,这个咱也就不关心了。设置完之后,还必须是Post请求,必须的吗,上传文件还能Get?另外Servlet和JSP规范中并没有对这种格式进行解析,因此还得额外自己写代码。我shit,你下载文件只要设置一下响应头,你规范就不能为上传文件多做点东西么,还要我自己额外解析,虽说我对Web了解不多,可Apache我知道,common-fileupload这个组件我可知道,一查API,果真可以处理,等等,既然做了,就先自己造个轮子,毕竟是自己学习又不是做项目;再在网上搜,不错,找到oreilly上的一个开源Servlet工具库,看起来评价不错,我先收藏,慢慢再看。

对付这种web流,我就两种处理方式:

1、用HTTPWatch或者类似软件截获HTTP流,分析客户端提交数据

2、调试阶段在Web应用服务器上打出响应流信息,先别考虑神马性能问题。。。那是后话。

1、2弄完了,好了发现格式了如下,最好对着RFC看,但是真心看RFC太烂费时间了,不过对于真正想要做好这种类型的事情不仔细研读RFC以及各种浏览器的行为,总是会出现这样或者那样的问题。

-----------------------------7dc1e411c05fe
Content-Disposition: form-data; name="upfile"; filename="C:\Documents and Settings\Administrator\桌面\小舅好啊.doc"
Content-Type: application/msword
上面这个是文件流开始的元数据信息
 
multipart/form-data; boundary=---------------------------7dc1e411c05fe
这个是通过Request获取到的Content-Type,根据这个判断是否MultiPart,注意不仅仅可以传doc,应该MIME的都行,boundary很重要,这个就是处理文件流开始和结尾的。

通过分析流,发现其实邮件传输和这个大同小异,除了邮件使用了Base64编码吧?印象中好象是。

在文件流结尾会有这个:

-------------------------------7dc1e411c05fe
因此,通过分析boundary确定边界、通过分析Content-Disposition获取文件元信息,当然了,也可以不传文件,这样仅仅什么也不做而已。

分析完了,奉上我第一版本的粗糙代码,说实话,我这个就是为了研究原理,里面代码存在很大问题,先说出来,别让大家喷我:

1、编码格式,编码格式很重要,文件名可能是中文,因此需要设置UTF-8或者GBK,但是在解析流的时候,Encoding只能在解析元数据信息的时候的使用,其余时候

各位还是安心使用ASCII就是字节流吧,这样原来文件是什么,存储的就是什么,肯定不会出现问题,之前我用GBK了,就带来Excel上传每次都有格式问题。

2、流处理问题,缓冲流的操作,并且读取流需要分阶段来处理,先处理元数据信息,获取完后在处理文件真实信息,避免过多用户上传导致程序繁忙。

JSP如下,一个简单的JSP

<%@ page language="java" contentType="text/html; charset=gb2312"
    pageEncoding="gb2312"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>SOC FileUpload</title>
</head>
<body>
    <!--  <%=application.getServerInfo()%><br>-->
    <form method="post" action="/soc/UploadFile"
        enctype="multipart/form-data">
        请选择要上传的文件<input type="file" name="upfile" size="40"><input
            type="submit" value="提交">
    </form>
</body>
</html>

Servlet:

public class UploadFile extends HttpServlet {
    private static final long serialVersionUID = 1L;

    // 10M
    
// 这个版本的代码性能问题,读取大数据需要
    
// 逐步读取
    public static final long MAX_SIZE = 1024 * 1024 * 10;
    static final String FILE_NAME = "filename=\"";
    static final String BOUNDRAY = "boundary=";

    /**
     * 
@see HttpServlet#HttpServlet()
     
*/
    public UploadFile() {
        super();
        LogUtil.debug("UploadFile load.");
    }

    /**
     * 
@see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     
*/
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    /**
     * 
@see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     
*/
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // request set encoding
        LogUtil.debug("UploadFile called.");
        LogUtil.debug(request.getServletPath());
        LogUtil.debug(getServletContext().getRealPath(request.getServerName()));
        String base = (getServletContext().getRealPath(request.getServerName()));
        // platform
        base = base.substring(0, base.lastIndexOf(File.separatorChar) + 1);
        String rootPath = base + "upload";

        String contentType = request.getContentType();
        LogUtil.debug(contentType);
        LogUtil.debug("donwload store: " + rootPath);
        if (contentType.indexOf("multipart/form-data") >= 0) {
            int length = request.getContentLength();
            if (length > MAX_SIZE) {
                response.getWriter().println(
                        "<P> File can't exceed " + MAX_SIZE + "<P><br>");
                return;
            }

            byte[] total = new byte[length];

            int totalRead = 0;
            int byteRead = 0;

            // not very good
            while (totalRead < length) {
                byteRead = request.getInputStream().read(total, totalRead,
                        length - totalRead);
                totalRead += byteRead;
            }

            String raw = new String(total);
            // LogUtil.debug("Raw: " + raw);
            LogUtil.debug("total: " + length + " read: " + totalRead);
            String saveFile = raw.substring(raw.indexOf(FILE_NAME)
                    + FILE_NAME.length());
            saveFile = saveFile.substring(0, saveFile.indexOf("\n"));
            saveFile = saveFile.substring(0, saveFile.lastIndexOf("\""));
            // 判断浏览器
            saveFile = saveFile.substring(saveFile.lastIndexOf("\\") + 1);
            LogUtil.debug("File: " + saveFile);

            String boudary = contentType
                    .substring(contentType.lastIndexOf("=") + 1);
            LogUtil.debug("Bound: " + boudary);

            String destFileName = rootPath + File.separatorChar + saveFile;
            LogUtil.debug(destFileName);

            int pos = raw.indexOf(FILE_NAME);
            pos = raw.indexOf("\n", pos) + 1;
            pos = raw.indexOf("\n", pos) + 1;
            pos = raw.indexOf("\n", pos) + 1;

            int startPosition = raw.substring(0, pos).getBytes().length;
            // -4
            LogUtil.debug("Start: " + raw.substring(0, pos));
            raw = null;
            raw = new String(total, "ascii");

            int boundaryPosition = raw.indexOf(boudary, pos) - 4;

            // LogUtil.debug("Bound: " + raw.substring(0, boundaryPosition));

            int endPosition = raw.substring(0, boundaryPosition).getBytes(
                    "ascii").length;
            // -----------------------------7dc251175d904aa
            
// -----------------------------7dc251175d904aa--
            
// ---------------------------7dc251175d904aa
            File file = new File(destFileName);

            if (!file.exists()) {
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                file.createNewFile();
            }

            FileOutputStream fout = new FileOutputStream(file);

            int destNumber = endPosition - startPosition;

            LogUtil.debug("Start: " + startPosition + " Boundary: "
                    + boundaryPosition + " End:" + endPosition + " Number: "
                    + destNumber);

            // LogUtil.debug("DestLog: "
            
// + new String(total, startPosition, destNumber));

            
// 避免写大文件
            ByteArrayInputStream bin = new ByteArrayInputStream(total,
                    startPosition, destNumber);
            byte[] cache = new byte[8 * 1024];
            int totalWrite = 0;
            while (totalWrite < destNumber) {
                int cnt = bin.read(cache);
                fout.write(cache, 0, cnt);
                fout.flush();
                totalWrite += cnt;
            }

            System.out.println("Write Total: " + totalWrite);
            fout.close();

            response.getWriter().println("<P>Upload File Successful<P><br>");
            response.getWriter().flush();
        }
    }
}

弄完后,我就知道,我这个处理不了多文件,其实也能处理,但是各位看官也看见了,代码写的实在难看,我连重构的欲望都没有,看来写程序必须先思考一下啊。。。奉上一个Oreilly的例子,看看这个多简洁:

public class UploadFileEx extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * 
@see HttpServlet#HttpServlet()
     
*/
    public UploadFileEx() {
        super();
    }

    /**
     * 
@see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     
*/
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    /**
     * 
@see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     
*/
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=692
        int maxPostSize = 5 * 1024 * 1024;
        MultipartRequest multi = new MultipartRequest(request, "d:\\",
                maxPostSize, "GB2312");
        Enumeration<?> filenames = multi.getFileNames();
        while (filenames.hasMoreElements()) {
            String filename = (String) filenames.nextElement();
            System.out.println("Upload -> " + filename);
         }
    }
}

短短几行,功能完成,当然Jar包里面也很多类处理了。核心是封装一个MultipartRequest ,这个类里面调用MultipartParser生成不同的Part,不同的Part有不同的

处理方法。

附带两个URL链接:

http://www.servlets.com/cos/

http://commons.apache.org/fileupload/

希望对大家有所帮助。

原文地址:https://www.cnblogs.com/diyunpeng/p/2429486.html