1. 程式人生 > >Apache FileUpload檔案上傳

Apache FileUpload檔案上傳

Apache 開源組織提供了一個用來處理表單檔案上傳的一個開源元件( Commons-fileupload ),該元件效能優異,並且其API使用極其簡單,可以讓開發人員輕鬆實現web檔案上傳功能,因此在web開發中實現檔案上傳功能,通常使用Commons-fileupload元件實現。

使用Commons-fileupload元件實現檔案上傳,需要匯入該元件相應的支撐jar包:Commons-fileupload和commons-io。commons-io 不屬於檔案上傳元件的開發jar檔案,但Commons-fileupload 元件從1.1 版本開始,它工作時需要commons-io包的支援。

fileupload元件工作流程

核心API—DiskFileItemFactory

DiskFileItemFactory 是建立 FileItem 物件的工廠,這個工廠類常用方法:

(1)建構函式,可設定快取檔案的大小,和關聯快取檔案

public DiskFileItemFactory(int sizeThreshold, java.io.File repository)

(2)設定記憶體緩衝區的大小,預設值為10K。當上傳檔案大於緩衝區大小時, fileupload元件將使用臨時檔案快取上傳檔案。

public void setSizeThreshold(int sizeThreshold)

(3)指定臨時檔案目錄,預設值為System.getProperty("java.io.tmpdir").

public void setRepository(java.io.File repository)

 

核心API—ServletFileUpload

ServletFileUpload 負責處理上傳的檔案資料,並將表單中每個輸入項封裝成一個 FileItem 物件中。

(1)判斷上傳表單是否為multipart/form-data型別,即是否是檔案上傳

static boolean isMultipartContent(HttpServletRequest request)

(2)解析request物件,並把表單中的每一個輸入項包裝成一個fileItem 物件,並返回一個儲存了所有FileItem的list集合。

List<FileItem> parseRequest(HttpServletRequest request)

(3)設定單個上傳檔案的最大值

setFileSizeMax(long fileSizeMax)

(4)設定上傳檔案總量的最大值

setSizeMax(long sizeMax)

(5)設定編碼格式,解決上傳檔名亂碼問題

setHeaderEncoding(java.lang.String encoding)

(6)實時監聽檔案上傳狀態

setProgressListener(ProgressListener pListener)

 

核心API—FileItem

FileItem 用來表示檔案上傳表單中的一個上傳檔案物件或者普通表單物件

boolean  isFormField() 判斷FileItem是一個檔案上傳物件還是普通表單物件

(1)如果判斷是一個普通表單物件

String  getFieldName()  獲得普通表單物件的name屬性

String  getString(String encoding) 獲得普通表單物件的value屬性,可以用encoding進行編碼設定

 

(2)如果判斷是一個檔案上傳物件

String  getName() 獲得上傳檔案的檔名(有些瀏覽器會攜帶客戶端路徑)

InputStream getInputStream()  獲得上傳檔案的輸入流

delete()  在關閉FileItem輸入流後,刪除臨時檔案

 

上傳檔案的存放問題

檔案存放位置

(1)為保證伺服器安全,上傳檔案應儲存在應用程式的WEB-INF目錄下,或者不受WEB伺服器管理的目錄。

(2)為防止多使用者上傳相同檔名的檔案,而導致檔案覆蓋的情況發生,檔案上傳程式應保證上傳檔案具有唯一檔名。

(3)為防止單個目錄下檔案過多,影響檔案讀寫速度,處理上傳檔案的程式應根據可能的檔案上傳總量,選擇合適的目錄結構生成演算法,將上傳檔案分散儲存。

 

程式碼演示

檔案上傳基類

package blog.csdn.net.web.servlet;

import java.io.File;
import java.io.IOException;
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;

import net.sf.json.JSONObject;
/**
 * 檔案上傳基類
 * @author mChenys
 *
 */
public abstract class BaseUploadServlet extends HttpServlet {

	// 上傳配置
	private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3; // 記憶體3MB
	private static final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 硬碟40MB
	private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html;charset=utf-8");
		// 檢測是否為多媒體上傳
		if (!ServletFileUpload.isMultipartContent(req)) {
			resp.getWriter().println("Error:表單必須包含 enctype=multipart/form-data");
		}

		// 配置上傳引數
		File repository = new File(getServletContext().getRealPath("/temp"));
		if (!repository.exists()) {
			repository.mkdirs();
		}
		// 配置記憶體快取大小和硬碟快取位置
		DiskFileItemFactory factory = new DiskFileItemFactory(MEMORY_THRESHOLD, repository);

		// 配置ServletFileUpload
		ServletFileUpload upload = new ServletFileUpload(factory);
		// 設定最大檔案上傳值
		upload.setFileSizeMax(MAX_FILE_SIZE);
		// 設定最大請求值 (包含檔案和表單資料)
		upload.setSizeMax(MAX_REQUEST_SIZE);
		// 中文處理
		upload.setHeaderEncoding("utf-8");
		// 解析請求的內容提取檔案資料
		try {
			List<FileItem> formItems = upload.parseRequest(req);
			onFileUpload(formItems,req,resp);
		} catch (Exception e) {
			JSONObject json = new JSONObject();
			json.put("status", -1);
			json.put("msg", "上傳失敗");
			resp.getWriter().println(json);
		}
	}

	public abstract void onFileUpload(List<FileItem> formItems,HttpServletRequest req,HttpServletResponse resp) throws  Exception;
}

實現類

package blog.csdn.net.web.servlet;

import java.io.File;
import java.util.List;

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

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.fileupload.FileItem;
import blog.csdn.net.web.domain.Product;
import blog.csdn.net.web.service.ProductService;
import blog.csdn.net.web.util.UploadUtils;

/**
 * 新增商品
 */
public class ProductAddServlet extends BaseUploadServlet {
	private static final long serialVersionUID = 1L;

	@Override
	public void onFileUpload(List<FileItem> formItems, HttpServletRequest req, HttpServletResponse resp)
			throws Exception {

		// 建立實體,儲存資料到資料庫
		Product product = new Product();
		// 迭代表單資料
		for (FileItem item : formItems) {
			if (!item.isFormField()) {
				// 處理檔案上傳

				String fileName = UploadUtils.getUUIDName(item.getName());
				String dirName = UploadUtils.getDir(fileName);

				// 設定最終儲存的根目錄,需要被覽器端訪問,放置在WebRoot下
				String uploadPath = req.getServletContext().getRealPath("/image" + dirName);
				// 儲存檔案到硬碟
				File uploadDir = new File(uploadPath);
				if (!uploadDir.exists()) {
					uploadDir.mkdirs();
				}
				File storeFile = new File(uploadPath, fileName);
				item.write(storeFile);
				
				product.setImage("image" + dirName + fileName);
			} else {
				// 處理其他表單項
				BeanUtils.setProperty(product, item.getFieldName(), item.getString("utf-8"));
			}

		}

		int success = new ProductService().addProduct(product);

		if (success != -1) { // 重定向到productList
			resp.sendRedirect(req.getContextPath() + "/productList");
		} else {
			resp.getWriter().println("新增商品失敗");
		}
	}
}

 檔名和資料夾處理工具類

package blog.csdn.net.web.util;

import java.util.UUID;

public class UploadUtils {
	/**
	 * 獲取隨機名稱
	 * @param realName 真實名稱
	 * @return uuid
	 */
	public static String getUUIDName(String realName){
		//realname  可能是  1.jpg   也可能是  1
		//獲取字尾名
		int index = realName.lastIndexOf(".");
		if(index==-1){
			return UUID.randomUUID().toString().replace("-", "").toUpperCase();
		}else{
			return UUID.randomUUID().toString().replace("-", "").toUpperCase()+realName.substring(index);
		}
	}

	/**
	 * 獲取檔案目錄
	 * @param name 檔名稱
	 * @return 目錄
	 */
	public static String getDir(String uuidFileName){
		int hashcode  = uuidFileName.hashCode();
		System.out.println("hashcode: "+hashcode );
		String hex = Integer.toHexString(hashcode );
		int j=hex.length();
		for(int k=0;k<8-j;k++){
			hex="0"+hex;
		}
		System.out.println("hex:"+hex);
		return "/"+hex.charAt(0)+"/"+hex.charAt(1)+"/";
	}
	
}