今天分享一個自己修改過的文件上傳工具類,主要是使用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
至此,分享結束。