今天分享一个自己修改过的文件上传工具类,主要是使用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
至此,分享结束。