使用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

 

至此,分享结束。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值