Web项目通过Servlet3.0完成文件上传

servlet3.0新特性

  • 支持注解开发
  • 支持文件上传
  • 支持异步请求

文件上传:

文件上传的概述

  • 指的是将本地的文件上传到服务器端的硬盘上

文件上传技术

  • JSPSmartUpload : 嵌入到JSP中完成的文件上传,主要用于Model1.0的年代
  • Servlet3.0 : 文件上传
  • FileUpload : Apache的文件上传组件
  • Struts2 : 底层是FileUpload

文件上传的要素

  • 表单提交的方式必须是post,因为get方式提交有大小限制64K
  • 表单中必须有name属性,需要通过name获取文件
  • 表单的enctype属性值必须是multipart-formdata,否则获取的文件就只有文件名,没有文件内容

代码实现:

web项目的目录结构:

这里写图片描述
这里写图片描述

JSP页面代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Servlet3.0完成文件上传</h1>
<form method="post" action="${ pageContext.request.contextPath }/FileUploadServlet" enctype="multipart/form-data"> <!-- 必须是post提交,因为get方式提交有大小限制64K -->
    <table >
        <tr>
            <td>文件上传:</td>
            <td><input type="file" name="upload"  /></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="上传"/></td>
        </tr>
    </table>
</form>

</body>
</html>

servlet代码:

package servlet;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

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
 */
@WebServlet("/FileUploadServlet")
@MultipartConfig  // 需要使用servlet3.0中的方法,必须加上这个注解,否则无法使用servlet3.0中的方法
public class FileUploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 接收文件
        Part part = request.getPart("upload");

        // 获得Content-Disposition头信息form-data; name="upload"; filename="hello.txt"
        String header = part.getHeader("Content-Disposition");

        // 获得filename的索引
        int lastIndexOf = header.lastIndexOf("filename");

        // 获得文件名,应为servlet3.0技术不够成熟,没有提供字节获取文件名的方法,必须要手动截取
        String filename = header.substring(lastIndexOf+10, header.length()-1);

        // 获得文件的输入流
        InputStream is = part.getInputStream();

        // 获得文件上传到服务器的路径
        String realPath = this.getServletContext().getRealPath("/fileupload");

        // 获得输出流
        OutputStream os = new FileOutputStream(realPath+"/"+filename);

        // 输出文件
        int len = 0;
        byte[] bys = new byte[1024];
        while((len = is.read(bys)) != -1) {
            os.write(bys, 0, len);
        }

        // 释放资源
        os.close();
        is.close();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

文件重名问题:

解决方法:使用UUID获得一个随机的文件名。

UUIDUtils代码:

package utils;

import java.util.UUID;

public class UUIDUtils {
    /**
     * 获得随机的UUID
     * 
     * @return
     */
    public static String getUUID() {
        return UUID.randomUUID().toString().replace("-", "");
    }
    /**
     * 通过给定的文件名,获得UUID
     * 
     * @param filename
     * @return
     */
    public static String getUUIDFilename(String filename) {
        return getUUID() + "-" + filename;
    }
}

解决重名问题的servlet代码:

package servlet;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

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;

import utils.UUIDUtils;

/**
 * 文件上传的servlet
 */
@WebServlet(urlPatterns="/FileUploadServlet2")
@MultipartConfig  // 需要使用servlet3.0中的方法,必须加上这个注解,否则无法使用servlet3.0中的方法
public class FileUploadServlet2 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 接收文件
        Part part = request.getPart("upload");

        // 获得Content-Disposition头信息form-data; name="upload"; filename="hello.txt"
        String header = part.getHeader("Content-Disposition");

        // 获得filename的索引
        int lastIndexOf = header.lastIndexOf("filename");

        // 获得文件名,应为servlet3.0技术不够成熟,没有提供字节获取文件名的方法,必须要手动截取
        String filename = header.substring(lastIndexOf+10, header.length()-1);

        // 获得文件的输入流
        InputStream is = part.getInputStream();

        // 获得文件上传到服务器的路径
        String realPath = this.getServletContext().getRealPath("/fileupload");

        // 获得输出流
        OutputStream os = new FileOutputStream(realPath+"/"+UUIDUtils.getUUIDFilename(filename));

        // 输出文件
        int len = 0;
        byte[] bys = new byte[1024];
        while((len = is.read(bys)) != -1) {
            os.write(bys, 0, len);
        }

        // 释放资源
        os.close();
        is.close();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

同一个目录下存放过多文件的问题

解决方案:

  • 按用户分 :一个用户创建一个或多个路径.
  • 按时间分 :按月,星期,天进行划分.
  • 按个数分 :一个路径中存3000个文件.
  • 按分离算法分 :按照一定的算法进行划分.

以下介绍按目录算法分的解决方案:

实现原理:

    通过UUID的方式获得的文件名基本上(除非你有买彩票中1000万的好运气)是唯一的,使用唯一文件名.hashCode(); 得到一个代表当前这个文件的int类型的值
    Int类型占4个字节,32位,可以让hashCode值&0xf;(1111);得到一个int值,使用这个int值作为一级目录
    让hashCode右移四位&0xf;(1111);得到一个int值,作为2级目录,以此类推。
    int类型的hashcode值是4个字节,32位,理论上可以获得43亿个文件夹。

UploadUtils代码:

package utils;

/**
 * 文件上传的工具类
 * 
 * @author zhw
 *
 */
public class UploadUtils {
    /**
     * 文件分离算法
     * 获得目录分离文件的目录的绝对路径
     * @param UUIDFilename 唯一的文件名
     * @param dirLevel  想要获得的文件夹层级
     * @return
     */
    public static String getPath(String uuidFilename, int dirLevel) {
        // 因为每一个UUIDFilename都是为一个,获取到的哈希值都是不一样的
        StringBuffer sb = new StringBuffer();
        if (dirLevel > 0 && dirLevel <= 8) {
            int code = uuidFilename.hashCode();// 目录的哈希值
            for (int i = 1; i <= dirLevel; i++) {
                int dir = code & 0xf; // 获得目录名
                code = code >>> 4;
                sb.append("\\").append(dir);
            }
        } else {
            throw new RuntimeException("目录层级最小为1级,最多为8级");
        }
        return sb.toString();
    }
}

解决重名问题和同一目录下文件过多的Servlet代码:

package servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

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;

import utils.UUIDUtils;
import utils.UploadUtils;

/**
 * 文件上传的servlet
 */
@WebServlet(urlPatterns = "/FileUploadServlet3")
@MultipartConfig // 需要使用servlet3.0中的方法,必须加上这个注解,否则无法使用servlet3.0中的方法
public class FileUploadServlet3 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 接收文件
        Part part = request.getPart("upload");

        // 获得Content-Disposition头信息form-data; name="upload";
        // filename="hello.txt"
        String header = part.getHeader("Content-Disposition");

        // 获得filename的索引
        int lastIndexOf = header.lastIndexOf("filename");

        // 获得文件名,应为servlet3.0技术不够成熟,没有提供字节获取文件名的方法,必须要手动截取
        String filename = header.substring(lastIndexOf + 10, header.length() - 1);

        // 获得文件的输入流
        InputStream is = part.getInputStream();

        // 获得文件上传到服务器的路径
        String realPath = this.getServletContext().getRealPath("/upload");

        // 通过UUID获得唯一的文件名
        String uuidFilename = UUIDUtils.getUUIDFilename(filename);

        // 获得文件的绝对路径,两层目录
        String path = realPath + UploadUtils.getPath(uuidFilename, 8);

        File file = new File(path);
        if (!file.exists()) {
            // 如果路径不存在,则创建所有文件夹
            file.mkdirs();
        }

        // 获得输出流
        OutputStream os = new FileOutputStream(path + "/" + uuidFilename);

        // 输出文件
        int len = 0;
        byte[] bys = new byte[1024];
        while ((len = is.read(bys)) != -1) {
            os.write(bys, 0, len);
        }

        // 释放资源
        os.close();
        is.close();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值