在使用java开发文件上传时,网络上有很多不一样的工具。但是每次如果都需要开发一次,对于使用者来说,这个过程是浪费时间的。所以我们有必要选取其中一个适合自己的文件上传组件,然后对其进一步分装,形成自己的开发工具类。下面我针对自己的工程,对fileupload进行了封装。
fileupload是apache的一个组件,要想使用fileupload,需要用到下面两个jar:commons-fileupload-1.3.1.jar,commons-io-1.1.jar(版本不一样,可能相关jar包不一致,大家根据最新来下载即可)。
下载好相关的jar包,使用eclipse新建一个Dynamic Web Project,然后将jar添加到WEB-INF/lib下面。
由于fileupload组件使用非常简单,所以大家可以参考官网的api,这是学习fileupload最后的一种方式。相关链接:fileupload
下面给出我自己的类和代码:
package net.itaem.servlet;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* 文件上传的基类,该类可以被继承
* 在配置文件,必须要配置相关的上传属性
* 这是一个父类,主要处理文件的保存,但是对于普通的表单域,由子类来实现
* 每个子类必须实现processNormalField(FileItem fileItem)方法
* 这里采用了模板设计模式...
*
* */
public abstract class FileuploadServletBase extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
//保存文件文件夹
private static String saveDir = null;
//缓存大小
private static int tempCachedSize = 20480;
//允许上传的文件类型
private static String[] permittedFileType = null;
//单个文件的大小
private static int fileSize = 20480;
//最大允许上传的表达大小
private static int maxSize = 204800;
//保存上传文件的文件夹
private String savePath;
//保存上传文件的map
private Map<String, String> uploadedMap = new HashMap<String, String>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
@SuppressWarnings("rawtypes")
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
//创建一个DiskFileItemFactory
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(savePath)); //配置保存路径
factory.setSizeThreshold(tempCachedSize); //配置缓存大小
//创建一个ServletFileUpload
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setSizeMax(maxSize); //设置总体表单大小
upload.setFileSizeMax(fileSize); //设置单个文件大小
//处理表单
try {
List<FileItem> items = upload.parseRequest(req);
Iterator itemsIterator = items.iterator();
while(itemsIterator.hasNext()){
FileItem fileItem = (FileItem)itemsIterator.next();
if(!fileItem.isFormField()){ //文件域
//去掉不支持上传的文件类型
boolean uploadedFlag = false;
for(int i=0; i<permittedFileType.length; i++){
if(permittedFileType[i].contains(fileItem.getContentType())){
uploadedFlag = true;
break;
}
}
//如果文件允许上传,那么保存文件到指定上传文件夹中
if(uploadedFlag){
String type = fileItem.getName().substring(fileItem.getName().lastIndexOf("."));
File uploadedFile = newFile(type);
//save the file
fileItem.write(uploadedFile);
//put the file name in a map
uploadedMap.put(fileItem.getFieldName(), uploadedFile.getCanonicalPath());
}
}else{ //普通表单域
processNormalField(fileItem);
}
}
//上传完毕,进行相关的操作,比如关联数据库,或者将上传文件名保存在xml等文件中
finishedProcess();
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获得一个随机字符串,作为上传文件的文件名,防止文件被复写了
* @return 返回一个唯一的随机字符串
* */
public String getRandomFileName(){
UUID fileNameUUID = UUID.randomUUID();
return fileNameUUID.toString();
}
@Override
public void init() throws ServletException {
super.init();
}
/**
* 初始化servlet,读取配置文件的参数
* */
@Override
public void init(ServletConfig config) throws ServletException {
if(config.getInitParameter("saveDir") == null){
throw new RuntimeException("参数saveDir不能为空");
}else{
saveDir = config.getInitParameter("saveDir").trim();
}
if(config.getInitParameter("permittedFileType") == null){
throw new RuntimeException("参数permittedFileType不能为空");
}else{
permittedFileType = config.getInitParameter("permittedFileType").trim().split(",");
}
if(config.getInitParameter("fileSize") == null){
throw new RuntimeException("参数fileSize不能为空");
}else{
fileSize = Integer.parseInt(config.getInitParameter("fileSize").trim());
}
if(config.getInitParameter("maxSize") == null){
throw new RuntimeException("参数maxSize不能为空");
}else{
maxSize = Integer.parseInt(config.getInitParameter("maxSize").trim());
}
if(config.getInitParameter("tempCachedSize") == null){
throw new RuntimeException("参数tempCachedSize不能为空");
}else{
tempCachedSize = Integer.parseInt(config.getInitParameter("tempCachedSize").trim());
}
//新建文件夹,如果文件夹不存在
saveDir = createSaveDir(config.getServletContext(), saveDir);
}
/**
* 创建文件夹
* @param context 请求上下文
* @param dirName 文件夹名字
* @return 返回文件夹在当前工程中的路径
* */
private String createSaveDir(ServletContext context, String dirName) {
String contextPath = context.getRealPath("/"); //获得工程的
savePath = contextPath + dirName;
File dir = new File(savePath);
if(!dir.exists()){ //如果文件不存在,创建文件夹
dir.mkdir();
return savePath;
}else{
if(dir.isDirectory()){ //文件夹存在,并且已经是文件夹了,直接返回
return savePath;
}else{
throw new RuntimeException("该文件" + savePath + "已经存在,请重新配置保存上传文件的文件夹名称");
}
}
}
/**
*
* 这是一个抽象类,由基类实现,这个方法主要用来处理普通的表单
* 使用模板设计模式...
* 处理普通的表单域
* @param fileItem 普通表单域
* */
public abstract void processNormalField(FileItem fileItem);
/**
* @param type 文件类型
* 穿件一个随机文件,文件的后缀有type决定
* @return 随机文件
* */
public File newFile(String type){
return new File(savePath + File.separator + getRandomFileName() + type);
}
/**
* 将所有的文件名作为一个map返回
* key:表单域名
* value:上传文件名
* @return 返回所有上传文件的map
* */
public Map<String, String> getUploadedMap(){
return uploadedMap;
}
/**
* 通过表单域找到上传文件之后的文件名
* @param filedName 表单域名
* @return 上传之后的文件名
* */
public String getUploadedName(String fieldName){
if(fieldName == null) return null;
return uploadedMap.get(fieldName);
}
/**
* 表单处理完毕,接下来处理相关操作,比如将上传之后的文件名保存到数据库库之类的操作d
* 通常情况下都会进行相关关联
* */
public abstract void finishedProcess();
}
从类的定义中,我们可以看到,这个类是一个抽象类,使用了方法模板设计模式。
下面给出一个简单实现类:
package net.itaem.servlet;
import java.util.Map;
import org.apache.commons.fileupload.FileItem;
/**
* 文件上传的一个类
* 这里处理了普通表单域
*
*
* */
public class GenericFileUploadServlet extends FileuploadServletBase{
/**
*
*/
private static final long serialVersionUID = 155452356367458695L;
@Override
public void processNormalField(FileItem fileItem) {
System.out.println(fileItem.getFieldName() + "--->" + fileItem.getString());
// do something here
}
/**
* 表单处理完毕,接下来处理相关操作,比如将上传之后的文件名保存到数据库库之类的操作d
* */
@Override
public void finishedProcess() {
Map<String, String> uploadedMap = super.getUploadedMap();
//打印出每个表单域对应的上传文件名
for(String fieldName: uploadedMap.keySet()){
System.out.println(fieldName + "--->" + uploadedMap.get(fieldName));
}
}
}
总结:
在实现类GenericFileuploadServlet中,我们可以对普通的表单域进行处理,也可以对上传之后的文件作出想过处理。所以有了这个设计之后,每次我们可以手动在配置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">
<display-name></display-name>
<servlet>
<servlet-name>fileupload</servlet-name>
<servlet-class>net.itaem.servlet.GenericFileUploadServlet</servlet-class>
<init-param>
<param-name>tempSaveDir</param-name>
<param-value>temp_upload</param-value>
</init-param>
<init-param>
<param-name>saveDir</param-name>
<param-value>upload</param-value>
</init-param>
<init-param>
<param-name>tempCachedSize</param-name>
<param-value>2048000</param-value>
</init-param>
<init-param>
<param-name>permittedFileType</param-name>
<param-value>text/html,application/octet-stream,video/mp4</param-value>
</init-param>
<init-param>
<param-name>fileSize</param-name>
<param-value>204800000</param-value>
</init-param>
<init-param>
<param-name>maxSize</param-name>
<param-value>20480000</param-value>
</init-param>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>fileupload</servlet-name>
<url-pattern>/fileupload.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
恩,最近一段时间,开始看了会FileUpload组件的源代码,下面贴上相关类图,大家可以参考下
通过类图,我们可以发现,这里面使用了几个设计模式:包装设计模式(将HttpServletRequest包装成ServletRequestContext),工厂设计模式,迭代器设计模式,以及策略设计模式。
在源码中,我们可以发现,工具的抽象能力是非常值得我们学习的。比如说,直接将每个表单域抽象为FileItem,然后定义好统一的接口,这种方式简便了我们客户端的调用,可以使用统一的方式来处理普通表单域以及文件域。
好吧,我觉得自己写的代码确实没啥好的,大家还是直接看看官方文档好些,那里面的介绍更加具备一般性以及通用性。
注意点:
上传之后的文件,保存文件时,注意文件名要具备唯一性。这里使用UUID工具类来生成,哈哈,这个类在java.util包中。