使用FileUpload實現Servlet的文件上傳,並通過content-type判斷文件的實際類型

        今天分享一個自己修改過的文件上傳工具類,主要是使用FileUpload來實現Servlet的文件上傳,然後後臺通過請求頭中的content-type字段來判斷實際的文件類型。因爲傳統的方式是通過文件名的後綴來判斷當前文件的類型,但是很明顯這種方法其實很不靠譜,文件上傳者只需要修改文件名就能繞過後臺的判斷。因此這裏通過判斷content-type字段的值來判斷文件的實際類型,但是事實上,content-type也是可以僞造的。查到網上較爲安全的方法是說,使用魔數來判斷文件的實際類型,但是這種方法比較耗費系統資源,所以如果對安全係數要求不是很高的,不建議使用。我這裏,根據自己項目的需求,使用content-type字段判斷文件的類型就已足夠,當然了,用戶在調用文件上傳接口之前,應該先判斷一些是否有相應的權限(即是否已登錄);然後,對存放文件的文件夾父目錄,應該設置只讀權限;另外,前端和後臺最好限制文件的上傳類型,只允許上傳網站允許的文件類型。

      扯了這麼多,直接上源碼:

package fileupload;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSON;

import DateUtil;

/**
 * 
 * @ClassName: PlUploadServlet
 * @Description: TODO(這裏用一句話描述這個類的作用)
 * @author hqq
 *
 */
public class PlUploadServlet extends HttpServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static final Logger logger = Logger.getLogger(PlUploadServlet.class);

	public PlUploadServlet() {
		super();
	}

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request, response);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setCharacterEncoding("UTF-8");

        //業務代碼隱身符,此處省略判斷用戶權限的代碼,每個人的權限控制都不同,故省略

		String orderId = "";// 訂單號
		String type = "";// 類型
		String userId = "";// 用戶id
		String carId = "";// 
		String folder = "";// 文件夾
		String webParentPath = "";// 文件上傳到服務器的真實路徑,不包含文件名
		String retrunPath = "";// 返回給前端的絕對路徑
		String myName = "";// 保存到服務的文件命名
		String fileName = "";// 是否有系統定義的文件名傳來

		String dateFolder = DateUtil.dateToString(new Date(), "yyyy-MM-dd");

		String basePath = "D:/tom/webapps/myProject/file";//硬盤的實際目錄
		String baseReturn = "/file";

		Map<String, Object> m = new HashMap<String, Object>();

		if (ServletFileUpload.isMultipartContent(request)) {
			try {

				DiskFileItemFactory factory = new DiskFileItemFactory();
				// 設置工廠的內存緩衝區大小,默認是1M
				factory.setSizeThreshold(1024 * 1024);
				// 設置工廠的臨時文件目錄:當上傳文件的大小大於緩衝區大小時,將使用臨時文件目錄緩存上傳的文件
				factory.setRepository(new File("D:/Tomcat/webapps/struck2.0/temp"));

				// 文件上傳解析器
				ServletFileUpload upload = new ServletFileUpload(factory);
				// 設置所有上傳數據的最大值,單位字節long 5M
				upload.setSizeMax(1024 * 1024 * 5);
				// 設置單個文件上傳的最大值
				upload.setFileSizeMax(1024 * 1024 * 5);
				// 設置編碼格式
				upload.setHeaderEncoding("UTF-8");

				// 解析請求,將表單中每個輸入項封裝成一個FileItem對象
				List<FileItem> items = upload.parseRequest(request);
				for (FileItem item : items) {

					if (item.isFormField()) { // 是文本域
						switch (item.getFieldName()) {
						case "userId":// 獲取到的userId
							userId = item.getString();
							break;
						case "orderId":
							orderId = item.getString();
							break;
                        case "carId":
							carId = item.getString();
						case "type":
							type = item.getString();
							logger.info("本次上傳的文件類型是:" + type);
							break;
						case "fileName":// 有文件名過來
							fileName = new String(item.getString().getBytes("ISO-8859-1"), "utf-8");// 中文轉碼
							logger.info("客戶端傳了文件名,不需要服務端重新命名。傳來的名字是:" + fileName);
							break;
						}

					} else {// 如果是文件類型

						// 沒有傳入userId是不允許的哦
						if (StringUtils.isBlank(userId)) {
							m.put("status", false);
							m.put("fileUrl", "缺少必要的參數,您無法上傳");
							response.getWriter().write(JSON.toJSONString(m));
							return;
						}

						// 判斷文件類型
						String suffix = FileType.getSuffix(item.getContentType());
						if (suffix == null) {
							m.put("status", false);
							m.put("fileUrl", "暫不支持該類型的文件上傳");
							response.getWriter().write(JSON.toJSONString(m));
							return;
						}

						// 定義文件名
						myName = UUID.randomUUID().toString().replaceAll("-", "");

                        //我主張將不同類型的文件放在不同的文件夾中,因爲調用接口的時候,我們就知道要上傳的是哪種業務的文件了,這樣便於後期管理
						switch (type.toUpperCase()) {
						case "AFILE"://A類文件
							folder = "/order/sofile/" + dateFolder + "/" + userId + "/";
							break;

						case "BPIC":// B類圖片
							folder = "/order/" + dateFolder + "/" + orderId + "/";
							myName = type + "_" + orderId + "_" + carId + "_" + System.currentTimeMillis();
							break;
						case "USERINFO":// 其它文件
							folder = "/user/" + userId + "/";
							if (StringUtils.isBlank(fileName)) {
								myName = "USERIMAGE_" + myName + "_" + System.currentTimeMillis();
							} else {
								myName = fileName;
							}
							break;
						case "ORDEREXCEL":// 其它文件
							folder = "/excel/" + userId + "/" + dateFolder + "/";
							break;
						default:
							folder = "/unknown/" + dateFolder + "/";
							break;
						}

						myName = myName + suffix;

						// 設置圖片路徑
						webParentPath = basePath + folder;// 文件上傳到服務器的真實路徑,不包含文件名
						retrunPath = baseReturn + folder;// 返回給前端的絕對路徑

						File up = new File(webParentPath);
						if (!up.exists()) {
							up.mkdirs();
						}

						File savedFile = new File(webParentPath, myName);
						item.write(savedFile);

					}
				}

				m.put("status", true);
				m.put("fileUrl", retrunPath + myName);

			} catch (Exception e) {
				logger.info("網絡異常:" + e.getMessage());
				m.put("status", false);
				m.put("fileUrl", "網絡異常:" + e.getMessage());
			}
		} else {
			m.put("status", false);
			m.put("fileUrl", "您傳入的不是文件");
		}
		response.getWriter().write(JSON.toJSONString(m));
	}

}

當然了,還需要在web.xml文件中配置對應的servlet映射,此處省略。

依賴的jar包:

更詳細的fileUpload使用方法,請參考博客:https://blog.csdn.net/belalds/article/details/82469887 

另外還有一個簡單的日期工具類:


import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtil {

	/**
	 * 日期轉字符串
	 * 
	 * @param date
	 *            日期
	 * @param pattern
	 *            格式
	 * @return
	 */
	public static String dateToString(Date date, String pattern) {
		if (date != null) {
			SimpleDateFormat sdf = new SimpleDateFormat(pattern);
			return sdf.format(date);
		}
		return "";
	}

}

 

以及枚舉類FileType.java:


/**    
* @Title: FileType.java  
* @Package sy.util.fileupload  
* @Description: TODO(用一句話描述該文件做什麼)  
* @author Administrator  
* @version V1.0    
*/

package fileupload;

import org.apache.commons.lang.StringUtils;

/**
 * @ClassName: FileType
 * @Description: TODO(這裏用一句話描述這個類的作用)
 * @author hqq
 * 
 */

public enum FileType {
	JPG("image/jpeg", ".jpg"),
	PNG("image/png",".png"),
	PDF("application/pdf",".pdf"),
	TXT("text/plain",".txt"),
	DOC("application/msword",".doc"),
	DOCX("application/vnd.openxmlformats-officedocument.wordprocessingml.document",".docx"),
	XLS("application/vnd.ms-excel",".xls"),//wps的表格,是這個
	XLS2("application/octet-stream",".xls"),//如果Micro office的表格文件,對應content-type是這個
	XLSM("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".xlsm"),
	XLSX("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".xlsx");
	
	//屬性
	private String contentType;
	private String suffix;

	// 構造方法
	private FileType(String contentType,String suffix ){
		this.contentType=contentType;
		this.suffix=suffix;
	}

	/**
	 * 傳入contentType,獲取後綴名
	 * @param contentType 
	 * @return 返回對應的後綴名,沒有值就返回null
	 */
	public static String getSuffix(String contentType){
		if(StringUtils.isBlank(contentType)){
			return null;
		}
		for(FileType tf:FileType.values()){
			if(contentType.equals(tf.getContentType())){
				return tf.getSuffix();
			}
		}
		return null;
	}
	
	public String getContentType() {
		return contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}

	public String getSuffix() {
		return suffix;
	}

	public void setSuffix(String suffix) {
		this.suffix = suffix;
	}

}

 

更多content-type類型,請參照這篇博客https://blog.csdn.net/houbin0912/article/details/78037768

 

至此,分享結束。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章