一、基于表单的上传文件

1. enctype属性

当表单需要上传文件时,需指定表单 enctype 的值为 multipart/form-data。


在 form 元素的语法中,enctype 属性指定将数据发送到服务器时浏览器使用的编码类型。


  


enctype 属性取值: 


application/x-www-form-urlencoded:表单 enctype 属性的默认值。这种编码方案使用有限的字符集,当使用了非字母和数字时,必须用”%HH”代替(H 代表十六进制数字)。对于大容量的二进制数据或包含非 ASCII 字符的文本来说,这种编码不能满足要求。


multipart/form-data:form 设定了enctype=“multipart/form-data”属性后,表示表单以二进制传输数据 。


2. Commons-fileupload 组件

Commons-fileupload 组件是 Apache 开源代码组织用来处理表单文件上传的一个子项目,该组件性能优异,可以支持任意大小的文件的上传。


Commons-fileupload 组件从 1.1 版本开始依赖 Apache 的另一个项目:commons-io。



Commons-fileupload 组件上传的基本原理 


FileUpload组件将页面提交的所有元素(普通form表单域,如text和文件域file)都看作一样的FileItem,这样上传页面提交的 request请求也就是一个FileItem的有序组合,FileUpload组件可以解析该request,并返回一个一个的FileItem。而对每一个FileItem,FileUpload组件可以判断出它是普通form表单域还是文件file域,从而根据不同的类型,采取不同的操作。如果是表单域,就读出其值,如果是文件域,就保存文件到服务器硬盘上或者内存中。


3. 示例

UploadServlet.java

package cn.heimar.upload;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

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.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * 1,限制上传文件大小 2,限制上传文件类型 3,设置上传缓存大小
 */
public class UploadServlet extends HttpServlet {
    private String[] allowedSuffix = new String[] { "JPG", "JPEG", "GIF","BMP", "PNG", "ICO" };

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
        // 判断上传的确实是一个包含了文件域的表单
        boolean isMultipart = ServletFileUpload.isMultipartContent(req);
        if (isMultipart) {
            try {
                // 处理有文件的表单内容
                // Create a factory for disk-based file items
                DiskFileItemFactory factory = new DiskFileItemFactory();
                File folder = new File("C:\\uploadtemp");
                if(!folder.exists()){
                    folder.mkdirs();
                }
                // 设置临时文件夹
                factory.setRepository(folder);
                // 设置缓存区大小
                factory.setSizeThreshold(3 * 1024 * 1024);

                // 创建一个上传处理器
                ServletFileUpload upload = new ServletFileUpload(factory);

                // 设置一次请求能够上传的文件总大小
                upload.setSizeMax(10 * 1024 * 1024);

                // 解析请求
                List<FileItem> items = upload.parseRequest(req);

                //FileItem:包装了普通表单域和文件域的统一对象
                for (FileItem item : items) {
                    // isFormField是在判断当前FileItem是不是普通表单域
                    if (item.isFormField()) {
                        // item.getFieldName:表单属性名称
                        String name = item.getFieldName();
                        // item.getString:表单属性值
                        String value = item.getString();

                        System.out.println(name + "            " + value);
                    } else {
                        String fieldName = item.getFieldName();
                        String fileName = item.getName();
                        String contentType = item.getContentType();
                        boolean isInMemory = item.isInMemory();
                        long sizeInBytes = item.getSize();

                        // 判断文件类型是否合法
                        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
                        if (Arrays.asList(allowedSuffix).contains(suffix.toUpperCase())) {
                            String writeFileName = UUID.randomUUID().toString();
                            String fp = this.getServletContext().getRealPath("/upload")
									+ File.separator
									+ writeFileName
									+ "."
			    						+ suffix;
                            item.write(new File(fp));
                        } else {
                            System.out.println("文件类型不合法");
                        }
                    }
                }
            } catch (SizeLimitExceededException fx) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            // 按照普通表单的方法处理
        }
    }
}
package cn.heimar.upload;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

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.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * 1,限制上传文件大小 2,限制上传文件类型 3,设置上传缓存大小
 */
public class UploadServlet extends HttpServlet {
    private String[] allowedSuffix = new String[] { "JPG", "JPEG", "GIF","BMP", "PNG", "ICO" };

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
        // 判断上传的确实是一个包含了文件域的表单
        boolean isMultipart = ServletFileUpload.isMultipartContent(req);
        if (isMultipart) {
            try {
                // 处理有文件的表单内容
                // Create a factory for disk-based file items
                DiskFileItemFactory factory = new DiskFileItemFactory();
                File folder = new File("C:\\uploadtemp");
                if(!folder.exists()){
                    folder.mkdirs();
                }
                // 设置临时文件夹
                factory.setRepository(folder);
                // 设置缓存区大小
                factory.setSizeThreshold(3 * 1024 * 1024);

                // 创建一个上传处理器
                ServletFileUpload upload = new ServletFileUpload(factory);

                // 设置一次请求能够上传的文件总大小
                upload.setSizeMax(10 * 1024 * 1024);

                // 解析请求
                List<FileItem> items = upload.parseRequest(req);

                //FileItem:包装了普通表单域和文件域的统一对象
                for (FileItem item : items) {
                    // isFormField是在判断当前FileItem是不是普通表单域
                    if (item.isFormField()) {
                        // item.getFieldName:表单属性名称
                        String name = item.getFieldName();
                        // item.getString:表单属性值
                        String value = item.getString();

                        System.out.println(name + "            " + value);
                    } else {
                        String fieldName = item.getFieldName();
                        String fileName = item.getName();
                        String contentType = item.getContentType();
                        boolean isInMemory = item.isInMemory();
                        long sizeInBytes = item.getSize();

                        // 判断文件类型是否合法
                        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
                        if (Arrays.asList(allowedSuffix).contains(suffix.toUpperCase())) {
                            String writeFileName = UUID.randomUUID().toString();
                            String fp = this.getServletContext().getRealPath("/upload")
									+ File.separator
									+ writeFileName
									+ "."
			    						+ suffix;
                            item.write(new File(fp));
                        } else {
                            System.out.println("文件类型不合法");
                        }
                    }
                }
            } catch (SizeLimitExceededException fx) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            // 按照普通表单的方法处理
        }
    }
}

upload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!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>
<form action="${pageContext.request.contextPath }/upload" method="POST" enctype="multipart/form-data">
<table>
	<tr>
		<td>文件名称:</td>
		<td><input type="text" name="name" /></td>
	</tr>
	<tr>
		<td>文件:</td>
		<td><input type="file" " name="filepath"/></td>
	</tr>
	<tr>
		<td colspan="2"><input type="submit" value="上传" /></td>
	</tr>
</table>

</form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!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>
<form action="${pageContext.request.contextPath }/upload" method="POST" enctype="multipart/form-data">
<table>
	<tr>
		<td>文件名称:</td>
		<td><input type="text" name="name" /></td>
	</tr>
	<tr>
		<td>文件:</td>
		<td><input type="file" " name="filepath"/></td>
	</tr>
	<tr>
		<td colspan="2"><input type="submit" value="上传" /></td>
	</tr>
</table>

</form>
</body>
</html>

二、文件下载

情景:


在一些网络系统中,需要隐藏下载文件的真实地址,或者下载的文件需要一个程序来动态的确定后在传送给客户端。

解决方案:

利用程序编码实现下载。


可以增加安全访问控制,只对经过授权认证的用户提供下载;


可以从任意位置提供下载的数据。



利用程序实现下载需要设置 2 个报头:

Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件。设置Content-Type 的值为:application/x-msdownload


Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。在设置 Content-Dispostion 之前一定要指定 Content-Type。

示例:

DownloadServlet.java

package cn.heimar.upload;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

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

public class DownloadServlet extends HttpServlet {

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String filename = req.getParameter("file");
		/*
		 * 设置响应报头 contentType: application/x-msdownload -->
		 * 告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件
		 */
		// response.setHeader("content-type", "application/x-msdownload");
		resp.setContentType("application/x-msdownload");
		/*
		 * 设置响应报头 Content-Disposition: attachment Web 服务器希望浏览器不直接处理相应的实体内容,
		 * 而是由用户选择将相应的实体内容保存到一个文件中
		 */
		resp.setHeader("Content-Disposition", "attachment; filename=11111.zip");

		String filePath = this.getServletContext().getRealPath(
				"/WEB-INF/download")
				+ File.separator + filename;
		BufferedInputStream is = new BufferedInputStream(new FileInputStream(
				filePath));
		BufferedOutputStream os = new BufferedOutputStream(
				resp.getOutputStream());

		byte[] buf = new byte[1024];
		int len = 0;

		while ((len = is.read(buf)) != -1) {
			os.write(buf, 0, len);
		}

		is.close();
		os.close();
	}

}
package cn.heimar.upload;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

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

public class DownloadServlet extends HttpServlet {

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String filename = req.getParameter("file");
		/*
		 * 设置响应报头 contentType: application/x-msdownload -->
		 * 告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件
		 */
		// response.setHeader("content-type", "application/x-msdownload");
		resp.setContentType("application/x-msdownload");
		/*
		 * 设置响应报头 Content-Disposition: attachment Web 服务器希望浏览器不直接处理相应的实体内容,
		 * 而是由用户选择将相应的实体内容保存到一个文件中
		 */
		resp.setHeader("Content-Disposition", "attachment; filename=11111.zip");

		String filePath = this.getServletContext().getRealPath(
				"/WEB-INF/download")
				+ File.separator + filename;
		BufferedInputStream is = new BufferedInputStream(new FileInputStream(
				filePath));
		BufferedOutputStream os = new BufferedOutputStream(
				resp.getOutputStream());

		byte[] buf = new byte[1024];
		int len = 0;

		while ((len = is.read(buf)) != -1) {
			os.write(buf, 0, len);
		}

		is.close();
		os.close();
	}

}

download.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>
<a href="${pageContext.request.contextPath }/download?file=test.zip">点击下载</a>
</body>
</html>
<%@ 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>
<a href="${pageContext.request.contextPath }/download?file=test.zip">点击下载</a>
</body>
</html>