Java Web 文件上传下载

版权声明:[email protected] https://blog.csdn.net/zhaoxuyang1997/article/details/82912339

今天傍晚看了李宏利老师的PPT,感触很深,自己很早就实现了文件上传下载,可是一直没有机会梳理,看了他的PPT后,花了三小时写下此文。本文1-3参照老师的PPT,4-6贴出自己的项目。

1文件上传概述

1.1文件上传的作用

例如网盘就是用来上传下载文件的,填写一个完整的简历还需要上传照片。

1.2文件上传对页面的要求

上传文件的要求比较多,需要记一下:

  1. 必须使用表单,而不能是超链接;
  2. 表单的method必须是POST,而不能是GET;
  3. 表单的enctype必须是multipart/form-data;
  4. 在表单中添加file表单字段,即<input type=”file”/>

例如:

<form action="${pageContext.request.contextPath}/FileUploadServlet" method="post" enctype="multipart/form-data">
	用户名:<input type="text" name="username"/><br/>
	文件1:<input type="file" name="file1"/><br/>
	文件2:<input type="file" name="file2"/><br/>
	<input type="submit" value="提交"/>
</form>
    

1.3 比对文件上传表单和普通文本表单的区别

通过 httpWatch 查看 文件上传表单普通文本表单 的区别。

  • 文件上传表单的enctype="multipart/form-data",表示多部件表单数据;
  • 普通文本表单可以不设置enctype属性:
    • method="post" 时,enctype的默认值为 application/x-www-form-urlencoded ,表示使用url编码正文;
    • method="get" 时,enctype的默认值为 null ,没有正文,所以就不需要enctype了。

1.4 对普通文本表单的测试

通过httpWatch测试,查看表单的请求数据正文,我们发现请求中只有文件名称,而没有文件内容。也就是说,当表单的 enctype不是multipart/form-data 时,请求中不包含文件内容,而只有文件的名称,这说明普通文本表单中 input:fileinput:text 没什么区别了。


测试-前端页面
测试-前端页面


测试-请求正文
测试-请求正文


测试-请求头
测试-请求头

扫描二维码关注公众号,回复: 3442862 查看本文章

1.5 文件上传对Servlet的要求

当提交的表单是文件上传表单时,那么对Servlet也是有要求的。

首先要肯定一点,文件上传表单的数据也是被封装到request对象中的。

request.getParameter(String) 方法获取指定的表单字段字符内容,但文件上传表单已经不在是字符内容,而是字节内容,所以失效。

这时可以使用request的getInputStream()方法获取ServletInputStream对象,它是InputStream的子类,这个ServletInputStream对象对应整个表单的正文部分(从第一个分隔线开始,到最后),这说明我们需要的解析流中的数据。当然解析它是很麻烦的一件事情,而Apache已经帮我们提供了解析它的工具:commons-fileupload


可以尝试把request.getInputStream()这个流中的内容打印出来,再对比httpWatch中的请求数据。

public void doPost(HttpServletRequest request, HttpServletResponse response) 
	throws ServletException, IOException {
	InputStream in = request.getInputStream();
	String s = IOUtils.toString(in); 
	System.out.println(s);
}
/*
-----------------------------7ddd3370ab2
Content-Disposition: form-data; name="username"
hello
-----------------------------7ddd3370ab2
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain
aaa
-----------------------------7ddd3370ab2
Content-Disposition: form-data; name="file2"; filename="b.txt"
Content-Type: text/plain
bbb
-----------------------------7ddd3370ab2--
*/

2 commons-fileupload

为什么使用fileupload?

上传文件的要求比较多,需要记一下:

  • 必须是POST表单;
  • 表单的enctype必须是multipart/form-data
  • 在表单中添加file表单字段,即 <input type="file"/>

Servlet的要求:

  • 不能再使用request.getParameter()来获取表单数据;
  • 可以使用request.getInputStream()得到所有的表单数据,而不是一个表单项的数据;

这说明不使用fileupload,我们需要自己来对request.getInputStream()的内容进行解析

2.1 fileupload概述

fileupload是由apache的commons组件提供的上传组件。它最主要的工作就是帮我们解析request.getInputStream()。

fileupload组件需要的JAR包有:

  1. commons-fileupload.jar,核心包;
  2. commons-io.jar,依赖包。

下载地址:http://cdn.zxy97.com/s/20180930194932.zip

2.2 fileupload简单应用

fileupload的核心类有:DiskFileItemFactoryServletFileUploadFileItem

使用fileupload组件的步骤如下:

  1. 创建工厂类DiskFileItemFactory对象:
  2. 使用工厂创建解析器对象:
  3. 使用解析器来解析request对象:

DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
List<FileItem> list = fileUpload.parseRequest(request);

介绍FileItem类,它才是我们最终要的结果

一个FileItem对象对应一个表单项(表单字段)。一个表单中存在文件字段和普通字段,可以使用FileItem类的isFormField()方法来判断表单字段是否为普通字段,如果不是普通字段,那么就是文件字段了。

  • String getName():获取文件字段的文件名称;
  • String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件;
  • String getFieldName():获取字段名称,例如:,返回的是username;
  • String getContentType():获取上传的文件的类型,例如:text/plain。
  • int getSize():获取上传文件的大小;
  • boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,说明是文件字段;
  • InputStream getInputStream():获取上传文件对应的输入流;
  • void write(File):把上传的文件保存到指定文件中。

2.3 简单上传示例

现在写一个简单的上传示例:

  • 表单包含一个用户名字段,以及一个文件字段;
  • Servlet保存上传的文件到uploads目录,显示用户名,文件名,文件大小,文件类型。

2.3.1 index.jsp

第一步:完成 index.jsp,只需要一个表单。注意表单必须是post的,而且enctype必须是mulitpart/form-data的。

<form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">
	用户名:<input type="text" name="username"/><br/>
	文件1:<input type="file" name="file1"/><br/>
	<input type="submit" value="提交"/>
</form>

2.3.2 FileUploadServlet

第二步:完成FileUploadServlet

public void doPost(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
	// 因为要使用response打印,所以设置其编码
	response.setContentType("text/html;charset=utf-8");
	
	// 创建工厂
	DiskFileItemFactory dfif = new DiskFileItemFactory();
	// 使用工厂创建解析器对象
	ServletFileUpload fileUpload = new ServletFileUpload(dfif);
	try {
		// 使用解析器对象解析request,得到FileItem列表
		List<FileItem> list = fileUpload.parseRequest(request);
		// 遍历所有表单项
		for(FileItem fileItem : list) {
		// 如果当前表单项为普通表单项
		if(fileItem.isFormField()) {
			// 获取当前表单项的字段名称
			String fieldName = fileItem.getFieldName();
			// 如果当前表单项的字段名为username
			if(fieldName.equals("username")) {
				// 打印当前表单项的内容,即用户在username表单项中输入的内容
				response.getWriter().print("用户名:" + fileItem.getString() + "<br/>");
			}
		} else {//如果当前表单项不是普通表单项,说明就是文件字段
			String name = fileItem.getName();//获取上传文件的名称
			// 如果上传的文件名称为空,即没有指定上传文件
			if(name == null || name.isEmpty()) {
				continue;
			}
			// 获取真实路径,对应${项目目录}/uploads,当然,这个目录必须存在
			String savepath = this.getServletContext().getRealPath("/uploads");
			// 通过uploads目录和文件名称来创建File对象
			File file = new File(savepath, name);
			// 把上传文件保存到指定位置
			fileItem.write(file);
			// 打印上传文件的名称
			response.getWriter().print("上传文件名:" + name + "<br/>");
			// 打印上传文件的大小
			response.getWriter().print("上传文件大小:" + fileItem.getSize() + "<br/>");
			// 打印上传文件的类型
			response.getWriter().print("上传文件类型:" + fileItem.getContentType() + "<br/>");
			}
		}
	} catch (Exception e) {
		throw new ServletException(e);
	} 
}

3 文件上传之细节

3.1 把上传的文件放到WEB-INF目录下

如果没有把用户上传的文件存放到WEB-INF目录下,那么用户就可以可能通过浏览器直接访问上传的文件,这是非常危险的。

假如说用户上传了一个a.jsp文件,然后用户在通过浏览器去访问这个a.jsp文件,那么就会执行a.jsp中的内容,如果在a.jsp中有如下语句:Runtime.getRuntime().exec("shutdown –s –t 1"); 那么你就会…

通常我们会在WEB-INF目录下创建一个uploads目录来存放上传的文件,而在Servlet中找到这个目录需要使用ServletContext的getRealPath(String)方法。

例如在我的upload1项目中有如下语句:

   ServletContext servletContext = this.getServletContext();
   String savepath = servletContext.getRealPath("/WEB-INF/uploads");
   /*
   savepath为:F:\tomcat6_1\webapps\upload1\WEB-INF\uploads
   */

3.2 文件名称(完整路径、文件名称)

IE6获取的上传文件名称是完整路径,而其他浏览器获取的上传文件名称只是文件名称而已。浏览器差异的问题我们还是需要处理一下的。

	String name = file1FileItem.getName();
	response.getWriter().print(name);

使用不同浏览器测试,其中IE6就会返回上传文件的完整路径,不知道IE6在搞什么,这给我们带来了很大的麻烦,就是需要处理这一问题。

处理这一问题也很简单,无论是否为完整路径,我们都去截取最后一个“\”后面的内容就可以了。

	String name = file1FileItem.getName();
	int lastIndex = name.lastIndexOf("\\");//获取最后一个“\”的位置
	if(lastIndex != -1) {//注意,如果不是完整路径,那么就不会有“\”的存在。
	   name = name.substring(lastIndex + 1);//获取文件名称
	}
	response.getWriter().print(name);

3.3 中文乱码问题

3.3.1 文件名称中包含中文时

需要设置编码,commons-fileupload组件为我们提供了两种设置编码的方式:

  • request.setCharacterEncoding(String):这种方式是我们最为熟悉的方式了;
  • fileUpload.setHeaderEncdoing(String):这种方式的优先级高于前一种。

3.3.2 文件内容中包含中文时

通常我们不需关心上传文件的内容,因为我们会把上传文件保存到硬盘上。

也就是说,文件原来是什么样子,到服务器这边还是什么样子,但是如果你有这样的需求,非要在控制台显示上传的文件内容,那么你可以使用fileItem.getString("utf-8")来处理编码。

即:文本文件内容和普通表单项内容使用FileItem类的getString("utf-8")来处理编码。

3.3 上传文件同名问题(文件重命名)

通常我们会把用户上传的文件保存到uploads目录下,但如果用户上传了同名文件呢?这会出现覆盖的现象。处理这一问题的手段是使用UUID生成唯一名称,然后再使用“_”连接文件上传的原始名称。

例如用户上传的文件是我的一寸照片.jpg,在通过处理后,文件名称为:891b3881395f4175b969256a3f7b6e10_我的一寸照片.jpg,这种手段不会使文件丢失扩展名,并且因为UUID唯一性,上传的文件同名,但在服务器端是不会出现同名问题的。

public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	request.setCharacterEncoding("utf-8");
	DiskFileItemFactory dfif = new DiskFileItemFactory();
	ServletFileUpload fileUpload = new ServletFileUpload(dfif);
	try {
	     List<FileItem> list = fileUpload.parseRequest(request);
	     //获取第二个表单项,因为第一个表单项是username,第二个才是file表单项
	     FileItem fileItem = list.get(1);
	     String name = fileItem.getName();//获取文件名称
		
	    // 如果客户端使用的是IE6,那么需要从完整路径中获取文件名称
	    int lastIndex = name.lastIndexOf("\\");
	    if(lastIndex != -1) {
		name = name.substring(lastIndex + 1);
	    }
			
		// 获取上传文件的保存目录
	     String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");
	     String uuid = CommonUtils.uuid();//生成uuid
	     String filename = uuid + "_" + name;//新的文件名称为uuid + 下划线 + 原始名称
			
	     //创建file对象,下面会把上传文件保存到这个file指定的路径
	     //savepath,即上传文件的保存目录
	     //filename,文件名称
	     File file = new File(savepath, filename);
			
	     // 保存文件
	      fileItem.write(file);
	} catch (Exception e) {
		throw new ServletException(e);
	} 
  }

3.5 一个目录不能存放过多的文件(存放目录打散)

一个目录下不应该存放过多的文件,一般一个目录存放1000个文件就是上限了,如果在多,那么打开目录时就会很“卡”。你可以尝试打印C:\WINDOWS\system32目录,你会感觉到的。

也就是说,我们需要把上传的文件放到不同的目录中。但是也不能为每个上传的文件一个目录,这种方式会导致目录过多。所以我们应该采用某种算法来“打散”!

打散的方法有很多,例如使用日期来打散,每天生成一个目录。也可以使用文件名的首字母来生成目录,相同首字母的文件放到同一目录下。

  • 日期打散算法:如果某一天上传的文件过多,那么也会出现一个目录文件过多的情况;
  • 首字母打散算法:如果文件名是中文的,因为中文过多,所以会导致目录过多的现象。

我们这里使用hash算法来打散:
获取文件名称的hashCode
获取hCode的低4位,然后转换成16进制字符;
获取hCode的5~8位,然后转换成16进制字符;
使用这两个16进制的字符生成目录链。例如低4位字符为5

	//获取文件名的hashCode
	int hCode = name.hashCode();
	//获取hCode的低4位,并转换成16进制字符串
	String dir1 = Integer.toHexString(hCode & 0xF);
	//获取hCode的低5~8位,并转换成16进制字符串
	String dir2 = Integer.toHexString(hCode >>> 4 & 0xF);
	//与文件保存目录连接成完整路径
	savepath = savepath + "/" + dir1 + "/" + dir2;
	//因为这个路径可能不存在,所以创建成File对象,再创建目录链,确保目录在保存文件之前已经存在
	new File(savepath).mkdirs();

3.7 上传的单个文件的大小限制

有时我们需要限制一个请求的大小。也就是说这个请求的最大字节数(所有表单项之和)!实现这一功能也很简单,只需要调用ServletFileUpload类的setSizeMax(long)方法即可。

例如fileUpload.setSizeMax(1024 * 10);,则设置整个请求的上限为10KB。当请求大小超出10KB时,ServletFileUpload类的parseRequest()方法会抛出FileUploadBase.SizeLimitExceededException异常。

3.8 缓存大小与临时目录

大家想一想,如果我上传一个蓝光电影,先把电影保存到内存中,然后再通过内存copy到服务器硬盘上,那么你的内存能吃的消么?

所以fileupload组件不可能把文件都保存在内存中,fileupload会判断文件大小是否超出10KB,如果是那么就把文件保存到硬盘上,如果没有超出,那么就保存在内存中。

10KB是fileupload默认的值,我们可以来设置它。

当文件保存到硬盘时,fileupload是把文件保存到系统临时目录,当然你也可以去设置临时目录。

public void doPost(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
	request.setCharacterEncoding("utf-8");
	DiskFileItemFactory dfif = new DiskFileItemFactory(1024*20, new File("F:\\temp"));
	ServletFileUpload fileUpload = new ServletFileUpload(dfif);
	
	try {
		List<FileItem> list = fileUpload.parseRequest(request);
		FileItem fileItem = list.get(1);
		String name = fileItem.getName();
		String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");
	
		// 保存文件
		fileItem.write(path(savepath, name));
	} catch (Exception e) {
		throw new ServletException(e);
	} 
}

4 文件下载

文件下载的核心代码就三行:

	response.addHeader("Content-Disposition", "attachment;filename=" + childFileName);
	response.addHeader("Content-Length", childFileLength);
	response.setContentType("application/octet-stream");

下面给出一个文件下载的方法实现:

 /**
     * @param filePath
     * @param request
     * @param response
     * @return 
     * @throws java.io.FileNotFoundException
     * @throws java.io.UnsupportedEncodingException
     * @描述 文件下载
     */
    public static boolean downloadFile(String filePath, HttpServletRequest request, HttpServletResponse response)
    		throws FileNotFoundException, UnsupportedEncodingException, IOException {
        boolean isDownload = false;
        File childFile = new File(filePath);
        if (!childFile.exists()) {
            System.out.println("文件不存在或者已删除-下载失败!" + childFile.getAbsolutePath());
        } else {
            try (InputStream fis = new FileInputStream(childFile); OutputStream os = response.getOutputStream()) {
                String childFileName = URLEncoder.encode(childFile.getName(), "utf-8");
                String childFileLength = childFile.length() + "";
                response.addHeader("Content-Disposition", "attachment;filename=" + childFileName);
                response.addHeader("Content-Length", childFileLength);
                response.setContentType("application/octet-stream");
                int data;
                while ((data = fis.read()) != -1) {
                    os.write(data);
                }
                isDownload =true;
            }
        }
        return isDownload;
    }
    
    

5 实现一个图片外链网站

单就文件上传功能,现在你就可以来实现一个资源托管网站(http://cdn.zxy97.com)或者一个图片外链网站(http://img.zxy97.com),以下我将介绍实现图片外链网站的步骤。

5.1 项目结构

项目结构

5.2 具体步骤

5.2.1 在项目中导入commons-fileupload-1.3.2.jarcommons-io-2.5.jar

commons-fileupload-1.3.2.jarcommons-io-2.5.jar的下载地址:http://cdn.zxy97.com/s/20180930194932.zip
下载好后解压得到两个jar文件,在webroot/WEB-INF下创建lib文件夹,复制进去。
然后再用IDE工具导入到项目中。

5.2.1 在src下创建com.zxy97.img.servlet

com.zxy97是我的域名(zxy97.com)的反写,如果你想把项目直接跑起来,按照该步骤进行即可。

5.2.2 在该包下创建UploadServlet.java


package com.zxy97.img.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class UploadServlet extends HttpServlet {

    static final String IMG_FOLDER = "img";//默认的存放img的根文件夹
    /**
     * @描述 上传数据及保存文件
     * @param request
     * @param response
     * @return 上传结果
     */
    public static String upload(HttpServletRequest request, HttpServletResponse response) {
        try {
            //设置上传的文件大小
            final int MEMORY_THRESHOLD = 1024 * 1024 * 512;
            final int MAX_FILE_SIZE = 1024 * 1024 * 1024;
            final int MAX_REQUEST_SIZE = 1024 * 1024 * 1024;

            request.setCharacterEncoding("utf-8");
            response.setCharacterEncoding("utf-8");

            if (!ServletFileUpload.isMultipartContent(request)) {
                //检测是否为多媒体上传,如果不是则停止
                PrintWriter writer = response.getWriter();
                writer.println("错误:表单必须包含 enctype=multipart/form-data");
                writer.flush();
                return null;
            }
            DiskFileItemFactory factory = new DiskFileItemFactory();// 实例化,开始配置上传参数
            factory.setSizeThreshold(MEMORY_THRESHOLD);// 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
            factory.setRepository(new File(System.getProperty("java.io.tmpdir")));// 设置临时存储目录
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setFileSizeMax(MAX_FILE_SIZE);// 设置最大文件上传值
            upload.setSizeMax(MAX_REQUEST_SIZE);// 设置最大请求值 (包含文件和表单数据)

            upload.setHeaderEncoding("UTF-8");// 防止中文乱码

            // 解析请求的内容提取文件数据
            List<FileItem> formItems = upload.parseRequest(request);

            if (formItems != null && formItems.size() > 0) {
                for (FileItem item : formItems) {
                    // 处理不在表单中的字段
                    if (!item.isFormField()) {
                        String itemName = item.getName();//获取文件名,进行验证
                        if(checkFileName(itemName)){//验证通过
                            java.util.Date date = new java.util.Date();//保存当时的系统时间,也可以用别的命名方式
                            String path = "/" + IMG_FOLDER + new java.text.SimpleDateFormat("/yyyyMMddHHmmss").format(date) + getFileSuffix(itemName);//相对路径
                            /*
                                推荐几种:
                                SimpleDateFormat("/yyyy/MM/dd/HH/mm/ss/")   在img文件夹下创建多个文件夹保存文件
                                SimpleDateFormat("/yyyyMMddHHmmss_")    在img文件夹下将文件名改成这种形式
                            */
                            
                            String filePath = request.getSession().getServletContext().getRealPath("/") + path;
                            
                            File file = new File(filePath);//要保存的文件
                            file.getParentFile().mkdirs();//为要保存的文件创建文件夹,否则无法保存
                            item.write(file);
                            return path;
                        }
                    }
                }
            }
            return null;
        }catch (Exception ex) {
            return null;
        }
    }
    
    /**
     * @描述 获取文件后缀名 
     */
    private static String getFileSuffix(final String fileName){
        int index = fileName.lastIndexOf(".");
        if(index >= 0){
            return fileName.substring(index);
        }
        return fileName;
    }
   
//     private String createFolder(String path){
//        File folder = new File(path);
//        if(!folder.exists()){
//            if(folder.mkdirs()){
//                return folder.getAbsolutePath();
//            }            
//        }
//        if(folder.isDirectory()){
//            return folder.getAbsolutePath();
//        }
//        return null;
//    }
     
    /**
     * @描述 允许上传的文件后缀
     */
    private static final String []arrSuffix = {
        ".jpg",".png",".bmp",".ico",".jpge",".gif"
    };
    
    public static String arrSuffixToString(){
        String str = "";
        for(String s:arrSuffix){
            str += s + " ";
            
        }
        return str;
    }
    
    /**
     * @描述 根据允许上传的文件后缀,进行文件判断 
     */
    private static boolean checkFileName(final String fileName){
        String name = fileName.toLowerCase();//转小写
        for(String suffix:arrSuffix){
            if(name.endsWith(suffix)){
                return true;
            }
        }
        return false;
    }

    /**
     * @描述 去掉Servlet带来的冗余路径
     * 
     */
    private static String getURL(String requestURL){
        int index = requestURL.lastIndexOf("/");
        return requestURL.substring(0, index);
    }
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = upload(request, response);//上传
        if(path == null){
            path = "上传失败!支持的文件类型:" + arrSuffixToString() ;
        }else{
            path = getURL(request.getRequestURL().toString()) + path;
        }
        request.getSession().setAttribute("path", path);
        response.sendRedirect(getURL(request.getRequestURL().toString()));
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    public String getServletInfo() {
        return "Short description";
    }
}

5.2.3 创建webroot/WEB-INF/web.xml,用来配置servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>UploadServlet</servlet-name>
        <servlet-class>com.zxy97.img.servlet.UploadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UploadServlet</servlet-name>
        <url-pattern>/upload</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

5.2.4 创建webroot/index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%
    String path =(String) session.getAttribute("path");
    if(path == null){
        path ="";
    }
%>
<!DOCTYPE html>
<html>
    <head>
        <title>zxy97_免费图片托管</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <h4><a href="http://zxy97.com/">Zxy97</a> | 图片托管网站</h4>
        <form action="upload" method="post" enctype="multipart/form-data">
            <input type="file" name="uploadfile" title="请选择您的文件">
            <button type="submit" class="btn">上传文件 </button>
        </form>
        <p><%=path %></p>
    </body>
</html>

6 实现一个资源托管网站

如果你已经成功地将图片外链网站跑起来了,那么现在你可以对上面的那个项目稍加修改,动态地设置文件扩展名,用来实现一个资源托管网站:http://cdn.zxy97.com
你在src下写的java代码会编译成class文件存放到webroot/WEB-INF/classes下,发布项目时只需要打包webroot文件夹,当时设置的文件扩展名便无法修改了,所以你可以将文件扩展名写到配置文件中,由程序读取,到时候只需要修改配置文件即可设置网站的文件上传类型、表单最大值、文件最大值、缓存最大值、保存的文件夹等等参数。

首先你需要在webroot/WEB-INF文件夹下创建一个名为config.properties的配置文件。

6.1 创建webroot/WEB-INF/config.properties

目前webroot/WEB-INF/config.properties的内容只有一行,就是用空格分隔的文件扩展名:

suffixArray=.jpg .png .bmp .ico .jpge .gif .zip .rar .7z .mp3 .txt .html .xml

6.2 创建com/zxy97/img/GetPath.java


package com.zxy97.img.servlet;


import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;

public class GetPath {
    
    public String[] getSuffixArray() throws IOException{
        String str = getValueByKey("suffixArray");
        return str.split(" ");
    }
    private String getValueByKey(String key) throws IOException{
        return getValueByKey(getWebRootPath()+"/WEB-INF/config.properties",key);
    }
    private String getValueByKey(String configFilePath, String key) throws FileNotFoundException, IOException{
        String value;
        Properties prop = new Properties();
        InputStream in = new BufferedInputStream (new FileInputStream(configFilePath));
        prop.load(in);
        value = prop.getProperty(key);
        return value;        
 
    }
    
   
    /**
     * @return WebRoot的绝对路径,在src和jsp中通用的方法
     */
    public String getWebRootPath() {
        final String strClassName = getClass().getName();

        String strPackageName = "";
        if (getClass().getPackage() != null) {
            strPackageName = getClass().getPackage().getName();
        }

        String strClassFileName;
        if ("".equals(strPackageName)) {
            strClassFileName = strClassName;
        } else {
            strClassFileName = strClassName.substring(strPackageName.length() + 1, strClassName.length());
        }

        URL url = getClass().getResource(strClassFileName + ".class");

        String strURL = url.toString();
        strURL = strURL.substring(strURL.indexOf("/") + 1, strURL.lastIndexOf("WEB-INF"));
        return strURL;
    }
}

6.3 创建com.zxy97.img/UploadServlet.java


package com.zxy97.img.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class UploadServlet extends HttpServlet {

    static final String IMG_FOLDER = "s";//默认的存放文件的根文件夹
    /**
     * @描述 上传数据及保存文件
     * @param request
     * @param response
     * @return 上传结果
     */
    public static String upload(HttpServletRequest request, HttpServletResponse response) {
        try {
            //设置上传的文件大小
            final int MEMORY_THRESHOLD = 1024 * 1024 * 512;
            final int MAX_FILE_SIZE = 1024 * 1024 * 1024;
            final int MAX_REQUEST_SIZE = 1024 * 1024 * 1024;

            request.setCharacterEncoding("utf-8");
            response.setCharacterEncoding("utf-8");

            if (!ServletFileUpload.isMultipartContent(request)) {
                //检测是否为多媒体上传,如果不是则停止
                PrintWriter writer = response.getWriter();
                writer.println("错误:表单必须包含 enctype=multipart/form-data");
                writer.flush();
                return null;
            }
            DiskFileItemFactory factory = new DiskFileItemFactory();// 实例化,开始配置上传参数
            factory.setSizeThreshold(MEMORY_THRESHOLD);// 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
            factory.setRepository(new File(System.getProperty("java.io.tmpdir")));// 设置临时存储目录
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setFileSizeMax(MAX_FILE_SIZE);// 设置最大文件上传值
            upload.setSizeMax(MAX_REQUEST_SIZE);// 设置最大请求值 (包含文件和表单数据)

            upload.setHeaderEncoding("UTF-8");// 防止中文乱码

            // 解析请求的内容提取文件数据
            List<FileItem> formItems = upload.parseRequest(request);

            if (formItems != null && formItems.size() > 0) {
                for (FileItem item : formItems) {
                    // 处理不在表单中的字段
                    if (!item.isFormField()) {
                        String itemName = item.getName();//获取文件名,进行验证
                        if(checkFileName(itemName)){//验证通过
                            java.util.Date date = new java.util.Date();//保存当时的系统时间,也可以用别的命名方式
                            String path = "/" + IMG_FOLDER + new java.text.SimpleDateFormat("/yyyyMMddHHmmss").format(date) + getFileSuffix(itemName);//相对路径
                            /*
                                推荐几种:
                                SimpleDateFormat("/yyyy/MM/dd/HH/mm/ss/")   在img文件夹下创建多个文件夹保存文件
                                SimpleDateFormat("/yyyyMMddHHmmss_")    在img文件夹下将文件名改成这种形式
                            */
                            
                            String filePath = request.getSession().getServletContext().getRealPath("/") + path;
                            
                            File file = new File(filePath);//要保存的文件
                            file.getParentFile().mkdirs();//为要保存的文件创建文件夹,否则无法保存
                            item.write(file);
                            return path;
                        }
                    }
                }
            }
            return null;
        }catch (Exception ex) {
            return null;
        }
    }
    
    /**
     * @描述 获取文件后缀名 
     */
    private static String getFileSuffix(final String fileName){
        int index = fileName.lastIndexOf(".");
        if(index >= 0){
            return fileName.substring(index);
        }
        return fileName;
    }
   
//     private String createFolder(String path){
//        File folder = new File(path);
//        if(!folder.exists()){
//            if(folder.mkdirs()){
//                return folder.getAbsolutePath();
//            }            
//        }
//        if(folder.isDirectory()){
//            return folder.getAbsolutePath();
//        }
//        return null;
//    }
     
    /**
     * @描述 允许上传的文件后缀
     */
    private static String []arrSuffix;
    
    public static String arrSuffixToString(String []strs){
        String str = "";
        for(String s:strs){
            str += s + " ";
        }
        return str;
    }
    
    /**
     * @描述 根据允许上传的文件后缀,进行文件判断 
     */
    private static boolean checkFileName(final String fileName) throws IOException{
        String name = fileName.toLowerCase();//转小写
        arrSuffix = new GetPath().getSuffixArray();
 
        for(String suffix:arrSuffix){
            if(name.endsWith(suffix)){
                return true;
            }
        }
        return false;
    }

    /**
     * @描述 去掉Servlet带来的冗余路径
     * 
     */
    private static String getURL(String requestURL){
        int index = requestURL.lastIndexOf("/");
        return requestURL.substring(0, index);
    }
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String path = upload(request, response);//上传
        if(path == null){
            path = "上传失败!支持的文件类型:" + arrSuffixToString(arrSuffix) ;
        }else{
            path = getURL(request.getRequestURL().toString()) + path;
        }
        request.getSession().setAttribute("path", path);
        response.sendRedirect(getURL(request.getRequestURL().toString()));
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    @Override
    public String getServletInfo() {
        return "Short description";
    }
}

6.4 创建webroot/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>UploadServlet</servlet-name>
        <servlet-class>com.zxy97.img.servlet.UploadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UploadServlet</servlet-name>
        <url-pattern>/upload</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

6.5 创建webroot/index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%
    String path =(String) session.getAttribute("path");
    if(path == null){
        path ="";
    }
%>
<!DOCTYPE html>
<html>
    <head>
        <title>zxy97_免费资源托管</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <h4><a href="http://zxy97.com/">Zxy97</a> | 资源托管网站</h4>
        <form action="upload" method="post" enctype="multipart/form-data">
            <input type="file" name="uploadfile" title="请选择您的文件">
            <button type="submit" class="btn">上传文件 </button>
        </form>
        <p><%=path %></p>
    </body>
</html>

猜你喜欢

转载自blog.csdn.net/zhaoxuyang1997/article/details/82912339