fileupload文件上传

       在使用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组件的源代码,下面贴上相关类图,大家可以参考下

 fileupload文件上传_第1张图片

通过类图,我们可以发现,这里面使用了几个设计模式:包装设计模式(将HttpServletRequest包装成ServletRequestContext),工厂设计模式,迭代器设计模式,以及策略设计模式。

在源码中,我们可以发现,工具的抽象能力是非常值得我们学习的。比如说,直接将每个表单域抽象为FileItem,然后定义好统一的接口,这种方式简便了我们客户端的调用,可以使用统一的方式来处理普通表单域以及文件域。

好吧,我觉得自己写的代码确实没啥好的,大家还是直接看看官方文档好些,那里面的介绍更加具备一般性以及通用性。

使用手册

注意点:

       上传之后的文件,保存文件时,注意文件名要具备唯一性。这里使用UUID工具类来生成,哈哈,这个类在java.util包中。

你可能感兴趣的:(java,设计模式,继承,类,开发工具)