spring boot 的多文件上传

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_34083066/article/details/80652183

这个个人觉得就比较简单了。


首先前台页面:

    <div>
	    <form action="/fileUpload.do" enctype="multipart/form-data" method="post" id="fileUploadForm">
	    	<input type="file" name="file"/><br/>
	    	<input type="file" name="file"/><br/>
	    	<input type="file" name="file"/><br/>
	    	<input type="button" value="上传" id="upLoadButton"/>
	    </form>
    </div>

这里不适用submit是想要实现ajax异步提交,不跳转页面。所以使用button。

jq代码:

                var uploading = false; //上传状态
		//文件上传
		$("#upLoadButton").click(function(){
			if(uploading){
		        alert("文件正在上传中,请稍候...");
		        return false;
		    }
		    $.ajax({
		        url: "/fileUpload.do",
		        type: 'POST',
		        cache: false, //上传不使用缓存
		        data: new FormData($('#fileUploadForm')[0]), //form表单内容使用formData组装
		        processData: false,
		        contentType: false,
		        dataType:"json",  //返回值类型
		        beforeSend : function(){
		            uploading = true;
		        },
		        success : function(data) {
		        	alert(data.remindMsg);
		            uploading = false;
		        }
		    });
		});

逻辑不多说了,很简单。

先设一个变量,作为上传标志,防止用户多次点击。

然后ajax就不多说了。只说一下data。我们使用formdata来进行文件的二进制上传。

这里需要配合:

form表单:

enctype="multipart/form-data"
以及取值写法是:

new FormData($('#fileUploadForm')[0])

很奇怪为什么是使用jq选择器选择dom节点后还要加一个数组[0],这个找了很多资料,也没看明白为什么这么写。留个坑吧,以后填,还有如果有人知道,希望可以帮我解答一哈~

后台代码:

配置类:

package com.wm.springboot.conf;

import javax.servlet.MultipartConfigElement;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 文件上传配置类
 * 使用multipartFile实现
 * @author maybe
 */
@Configuration
public class FileUploadConfig {
	
	@Value("${upload.file.size}")
	private String fileSize;

	@Bean
	public MultipartConfigElement configElement() {
		MultipartConfigFactory factory = new MultipartConfigFactory();
		factory.setMaxFileSize(fileSize); //文件大小限制
		//factory.setLocation("d:\\wmUpload\\");  //这个配置的话,路径一定要存在,不然会报错。
		return factory.createMultipartConfig();
	}
}

说明:我发现一个问题,网上找资料,说在spring boot的配置文件中配置:

upload.file.size = 1024KB
#网上找的,发现4个都不生效。。。
#multipart.maxFileSize=1Kb
#multipart.maxRequestSize=1kb
#spring.http.multipart.maxFileSize=1Kb
#spring.http.multipart.maxRequestSize=1kb

即可实现控制上传文件大小。但是我试了这四个,并不可以。也没找到相关的原因...

然后我查阅资料通过上述的配置类进行配置,即可实现。

但是,这里面有一个setLocation,这个我配置了之后发现,他不会自动生成这个文件夹。如果没有,就会报错。

而且,即使我创建了也上传不到这个文件夹。干脆自己写一个吧。

所以配置类的作用就是限制文件上传大小的。


上传代码:    

package com.wm.springboot.utils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.wm.springboot.modelUtils.GetKey;
import com.wm.springboot.modelUtils.RespResult;
import com.wm.springboot.modelUtils.RespResultEnum;
import com.wm.springboot.modelUtils.RespResultUtil;
import com.wm.springboot.sc.entity.User;

import lombok.extern.slf4j.Slf4j;

/**
 * 文件上传工具类
 * 支持多文件上传
 * @author maybe
 */
@Slf4j
@Controller
public class FileUpload {

	/**
	 * (多)文件上传入口
	 * @param files 文件数组
	 * @param request
	 * @return
	 */
	@PostMapping("/fileUpload")
	public @ResponseBody RespResult<String> fileUpload(@RequestParam("file")MultipartFile[] files,HttpServletRequest request) {
		return upload(files,request);
	}
	
	
	/**
	 * 上传操作,支持多文件上传
	 * @param files 文件数组
	 * @param request 
	 */
	private RespResult<String> upload(MultipartFile[] files,HttpServletRequest request) {
		log.info("IP:{},进行文件上传——开始",request.getRemoteAddr());
		int count = 0; //文件数统计
		for(int i=0;i<files.length;i++) {
			if (!files[i].isEmpty()) {
				try {
					log.info("第{}个文件开始上传",i+1);
					// 使用UUID确保上传文件不重复
					BufferedOutputStream out = new BufferedOutputStream(
							new FileOutputStream(new File(getPath(request) + UUID.randomUUID() + "-" +files[i].getOriginalFilename())));
					out.write(files[i].getBytes()); 
					out.flush();
					out.close();
					log.info("第{}个文件上传成功",i+1);
					count++;
				} catch (FileNotFoundException e) {
					log.error("IP:{},{}",request.getRemoteAddr(),RespResultEnum.FILENOTFOUND.getMsg());
					e.printStackTrace();
					return RespResultUtil.error(RespResultEnum.FILENOTFOUND);
				} catch (IOException e) {
					log.error("IP:{},{}",request.getRemoteAddr(),RespResultEnum.FILEWRITEORCLOSEFAIL.getMsg());
					e.printStackTrace();
					return RespResultUtil.error(RespResultEnum.FILEWRITEORCLOSEFAIL);
				} catch (NullPointerException e) {
					log.error("IP:{},上传文件时获取根路径失败!",request.getRemoteAddr());
					e.printStackTrace();
					return RespResultUtil.error(RespResultEnum.ROOTPATHISNULL);
				}
			}
		}
		if(0==count) return RespResultUtil.error(RespResultEnum.FILELISTISNULL);
		log.info("IP:{},共{}个文件成功上传——结束",request.getRemoteAddr(),count);
		return RespResultUtil.success(count+"");
	}

	/**
	 * 一次上传访问,取一次上传路径
	 * @param request
	 * @return
	 */
	private String getPath(HttpServletRequest request) {
		String path = "";
		@SuppressWarnings("unchecked")
		String rootPath = ((Map<String, String>) request.getServletContext().getAttribute(GetKey.configMap.toString()))
				.get(GetKey.uploadpath.toString()); // 取根目录
		if (null == rootPath || "".equals(rootPath)) throw new NullPointerException(RespResultEnum.ROOTPATHISNULL.getMsg());
		User user = (User) request.getSession().getAttribute(GetKey.user.toString()); // 取用户名,做中间目录
		if (user != null) path = rootPath + user + "\\";
		else path = rootPath + "other\\";
		File file2 = new File(path);
		if (!file2.exists()) file2.mkdirs();
		log.info("文件存储路径path:{}", path);
		return path;
	}
}

参数列表中的MultipartFile类:表示接收到的上传文件。使用数组形式,表示上传的是多文件。

私有方法upload的返回值RespResult<String>:这个是我写的一个返回值包装类。包括,返回码,后台日志打印信息,前台提示信息,返回数据。

这里不多展开了。还有待优化。回来可能会单独写一篇吧。


关于getpath方法,我写了一个监听,在启动项目时,会查询config表,把一些配置,加载到系统中。这里,我把他的根目录写到了里面。这里直接从servletcontext中取相应的key即可。后期可以试着做刷新功能。从而实现很多的动态配置,修改数据库,刷新,即可。

哦,对了,这里有一个问题,因为我们的上传文件大小限制是配置在了上传的配置类中。这时候还没有进入我们的文件上传类。

这时候如果报错,提示会很不友好。这里就需要使用权与异常处理类来帮个忙了。

全局的异常处理类处理大小超限:

package com.wm.springboot.exception;

import javax.servlet.http.HttpServletRequest;

import org.apache.tomcat.util.http.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartException;

import com.wm.springboot.modelUtils.BusinessException;
import com.wm.springboot.modelUtils.RespResult;
import com.wm.springboot.modelUtils.RespResultEnum;
import com.wm.springboot.modelUtils.RespResultUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 全局异常处理
 * @author maybe
 */
@Slf4j
@ControllerAdvice
public class GlobalDefultExceptionHandler {
	
	
	@ExceptionHandler(MultipartException.class)
	@ResponseBody
	public <T> RespResult<T> FileSizeLimitExceededExceptionHandler(HttpServletRequest request,MultipartException e){
		if(e.getRootCause() instanceof FileSizeLimitExceededException) {
			log.info("文件大小超过限制!");
			return RespResultUtil.error(RespResultEnum.FILESIZETOOMORE);
		}
		//未知错误
		log.error("未知错误:{}",e.getMessage());
		return RespResultUtil.error(RespResultEnum.UNKONW_ERROR,e);
	}
	
	//声明要捕获的异常
	@ExceptionHandler(Exception.class)
	@ResponseBody
	public <T> RespResult<?> defultExcepitonHandler(HttpServletRequest request,Exception e) {
		e.printStackTrace();
		if(e instanceof BusinessException) {
			log.error("业务异常:{}",e.getMessage());
			BusinessException businessException = (BusinessException)e;
			return RespResultUtil.error(businessException);
		}else {
			//未知错误
			log.error("未知错误:{}",e.getMessage());
			return RespResultUtil.error(RespResultEnum.UNKONW_ERROR,e);
		}
	}
}

嗯,这下应该就完活了。

哦,下次找机会填坑logback。现在我已经把系统内的日志替换了logback,新的日志框架。填之前的坑。

展开阅读全文

没有更多推荐了,返回首页