实现web开发中的文件上传功能,需完成如下二步操作:
(1)在web页面中添加上传输入项;
(2)在servlet中读取上传文件的数据,并保存到本地硬盘中。
如何在web页面中添加上传输入项?标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:
(1)必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据;
(2)必须把form的enctype属值设为multipart/form-data.设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
如何在Servlet中读取文件上传数据,并保存到本地硬盘中?
(1)Request对象提供了一个getInputStream方法,通过这个方法可以读取到客户端提交过来的数据。但由于用户可能会同时上传多个文件,在servlet端编程直接读取上传数据,并分别解析出相应的文件数据是一项非常麻烦的工作;
(2)为方便用户处理文件上传数据,Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。
使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:Commons-fileupload和commons-io。commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。
对请求正文是multipart/form-data类型的数据进行解析。
DiskFileItemFactory 是创建 FileItem 对象的工厂,这个工厂类常用方法:
ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中。常用方法有:
FileItem类负责处理表单提交的字段,常用方法有:
1、处理普通字段方法
2、处理上传字段方法
1、中文文件乱码问题
文件名中文乱码问题,可调用ServletUpLoader的setHeaderEncoding方法,或者设置request的setCharacterEncoding属性
2、临时文件的删除问题
3、文件存放位置
4、显示上传进度
(1)ProgressListener显示上传进度
ProgressListener progressListener = new ProgressListener() {
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("到现在为止, " + pBytesRead + " 字节已上传,总大小为 "
+ pContentLength);
}
};
upload.setProgressListener(progressListener);
(2)以KB为单位显示上传进度
long temp = -1; //temp注意设置为类变量
long ctemp = pBytesRead /1024;
if (mBytes == ctemp)
return;
temp = mBytes;
使用工具类:GUIDUtils.java、DirectoryUtils.java
package com.study.java.utils;
import java.math.BigInteger;
import java.util.Random;
/**
* @Name: GUIDUtils
* @Description: 创建GUID唯一字符串工具类,参考struts2源码
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class GUIDUtils {
public static String generateGUID() {
return new BigInteger(165, new Random()).toString().toUpperCase() ;
}
}
package com.study.java.utils;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Name: DirectoryUtils
* @Description: 目录创建工具类
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class DirectoryUtils {
/**
* @Name: getChildDirectoryByDate
* @Description: 根据当前日期创建子目录
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param storeDirectory 文件存储主目录
* @Return: String
*/
public static String getChildDirectoryByDate(String storeDirectory) {
Date now = new Date() ;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
String childDirectory = sdf.format(now) ;
File file = new File(storeDirectory, childDirectory) ;
if(!file.exists()) {
file.mkdirs() ;
}
return childDirectory ;
}
/**
* @Name: getChildDirectoryByHashCode
* @Description: 根据文件名称字符串的hash码创建二级子目录
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param storeDirectory 文件存储主目录
* @Parameters: @param filename 文件名称
* @Return: String
*/
public static String getChildDirectoryByHashCode(String storeDirectory, String filename) {
int hashCode = filename.hashCode() ;
int dir1 = hashCode & 0xf ;
int dir2 = (hashCode & 0xf0) >> 4 ;
String childDirectory = dir1 + "/" + dir2 ;
File file = new File(storeDirectory, childDirectory) ;
if(!file.exists()) {
file.mkdirs() ;
}
return childDirectory ;
}
}
实现文件上传代码:UploadServlet.java
package com.study.java.servlet;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
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.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.study.java.utils.DirectoryUtils;
import com.study.java.utils.GUIDUtils;
/**
* @Name: UploadServlet
* @Description: 文件上传
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class UploadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8") ;
PrintWriter out = response.getWriter() ;
//设置请求参数编码,适用于post提交,用于处理上传文件中文编码问题
request.setCharacterEncoding("UTF-8") ;
//1、创建FileItem工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory() ;
//设置缓冲区大小1M,默认10kb
factory.setSizeThreshold(1*1024*1024) ;
//设置临时文件存放目录,默认为当前用户系统临时文件存放目录
String template = getServletContext().getRealPath("/WEB-INF/template") ;
File templatePath = new File(template) ;
if(!templatePath.exists()) {
templatePath.mkdirs() ;
}
factory.setRepository(templatePath) ;
//2、创建上传文件核心解析器对象
ServletFileUpload sfu = new ServletFileUpload(factory) ;
//判断表单提交类型是否为multipart/form-data
boolean isMultipartContent = sfu.isMultipartContent(request) ;
if(!isMultipartContent) {
out.write("表单提交类型不正确!!!") ;
}
//限制单文件上传大小
sfu.setFileSizeMax(3*1024*1024) ;
//限制一次上传总文件大小
sfu.setSizeMax(10*1024*1024) ;
//3、解析表单提交参数
try {
List items = sfu.parseRequest(request) ;
//获取表单提交参数信息对象
for (FileItem item : items) {
if(item.isFormField()) {
//普通字段
processFormField(item) ;
} else {
//上传字段
processUploadField(item) ;
}
}
out.write("上传成功!!!") ;
response.setHeader("Refresh", "2;URL=" + request.getContextPath()) ;
} catch (FileUploadBase.FileSizeLimitExceededException e) {
out.write("单文件上传大小超出限制3M.") ;
} catch (FileUploadBase.SizeLimitExceededException e) {
out.write("总文件上传大小超出限制10M.") ;
} catch (FileUploadException e) {
out.write("表单提交参数解析失败!!!") ;
}
}
/**
* @Name: processUploadField
* @Description: 上传表单提交的文件
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param item
* @Return: void
*/
private void processUploadField(FileItem item) {
//获取文件上传主目录
String storeDirectory = getServletContext().getRealPath("/WEB-INF/upload") ;
File file = new File(storeDirectory) ;
if(!file.exists()) {
file.mkdirs() ;
}
//获取上传文件名
String filename = item.getName() ;
if(filename != null && !"".equals(filename.toString().trim())) {
filename = GUIDUtils.generateGUID() + "_" + filename.substring(filename.lastIndexOf("//") + 1) ;
}
//获取文件上传子目录
String childDirectory = DirectoryUtils.getChildDirectoryByHashCode(storeDirectory, filename) ;
File f = new File(storeDirectory + "/" + childDirectory, filename) ;
//上传
try {
item.write(f) ;
} catch (Exception e) {
throw new RuntimeException("文件上传失败!!!") ;
}
}
/**
* @Name: processFormField
* @Description: 处理表单提交普通字段,打印到控制台
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param item
* @Return: void
*/
private void processFormField(FileItem item) {
try {
String fieldName = item.getFieldName() ;
String fieldValue = item.getString("UTF-8") ;
System.out.println(fieldName + "->" + fieldValue);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
超链接直接指向下载资源;
程序实现下载:程序实现下载需设置两个响应头,
(1)设置Content-Type 的值为:application/x-msdownload。Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件;
(2)Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。在设置 Content-Dispostion 之前一定要指定 Content-Type。
(3)因为要下载的文件可以是各种类型的文件,所以要将文件传送给客户端,其相应内容应该被当做二进制来处理,所以应该调用response.getOutputStream()方法返回 ServeltOutputStream 对象来向客户端写入文件内容。
response.setContentType("application/x-msdownload") ;
String str = "attachment;filename=" + java.net.URLEncoder.encode(fileName, "UTF-8") ;
response.setHeader("Content-Disposition", str) ;
InputStream is = new FileInputStream(file) ;
OutputStream os = response.getOutputStream() ;
int len = -1 ;
byte[] buf = new byte[1024] ;
while((len = is.read(buf)) != -1) {
os.write(buf, 0, len) ;
os.flush() ;
}
is.close() ;
os.close() ;
遍历上传目录下的所有文件显示给用户,并允许用户完成下载。
(1)ShowAllUploadFiles.java
package com.study.java.servlet;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Name: ShowAllUploadFiles
* @Description: 显示所有已经上传的文件
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class ShowAllUploadFiles extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Map map = new HashMap() ;
//获取文件上传主目录
String storeDirectory = getServletContext().getRealPath("/WEB-INF/upload") ;
File file = new File(storeDirectory) ;
treeWalk(file, map) ;
request.setAttribute("map", map) ;
request.getRequestDispatcher("/listFiles.jsp").forward(request, response) ;
}
/**
* @Name: treeWalk
* @Description: 将文件上传主目录中的所有文件名称放入到Map集合中
* @Author: XXX
* @Version: V1.0
* @CreateDate: XXX
* @Parameters: @param file
* @Parameters: @param map
* @Return: void
*/
private void treeWalk(File file, Map map) {
if(file.isDirectory()) {
File[] list = file.listFiles() ;
for (File f : list) {
treeWalk(f, map) ;
}
} else {
String guidFileName = file.getName() ;
String oldFileName = guidFileName.substring(guidFileName.indexOf("_") + 1) ;
map.put(guidFileName, oldFileName) ;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)JSP视图页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
head>
<body>
<c:forEach items="${map}" var="me">
<c:url value="/servlet/DownloadServlet" var="url">
<c:param name="guidFileName" value="${me.key}"/>
c:url><hr/>
${me.value}
<a href="${url}">下载a><hr/>
c:forEach>
body>
html>
(3)DownloadServlet.java
package com.study.java.servlet;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.study.java.utils.DirectoryUtils;
/**
* @Name: DownloadServlet
* @Description: 文件下载
* @Author: XXX
* @CreateDate: XXX
* @Version: V1.0
*/
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取请求链接中的参数,并进行编码,解决文件名中带中文的情况
String guidFileName = request.getParameter("guidFileName") ;
guidFileName = new String(guidFileName.getBytes("ISO-8859-1"), "utf-8") ;
//获取文件储存目录
String storeDirectory = getServletContext().getRealPath("/WEB-INF/upload") ;
String childDirectory = DirectoryUtils.getChildDirectoryByHashCode(storeDirectory, guidFileName) ;
File file = new File(storeDirectory + "/" + childDirectory, guidFileName) ;
if(file.exists()) {
//通知客户端下载
String oldFileName = guidFileName.substring(guidFileName.indexOf("_") + 1) ;
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(oldFileName, "UTF-8")) ;
//下载
InputStream is = new FileInputStream(file) ;
OutputStream os = response.getOutputStream() ;
int len = -1 ;
byte[] buf = new byte[1024] ;
while((len = is.read(buf)) != -1) {
os.write(buf, 0, len) ;
os.flush() ;
}
is.close() ;
os.close() ;
} else {
throw new RuntimeException("下载文件不存在!!!") ;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}