文档章节

SpringMVC文件上传接口设计与实现

乒乓狂魔
 乒乓狂魔
发布于 2015/03/02 07:55
字数 3156
阅读 4967
收藏 251
点赞 35
评论 13

#1 前两篇文章的铺垫

#1.1 SpringMVC文件上传源码分析前言

#1.2 apache fileupload源码分析

#2 整体的包结构 首先看下整体的包的结构,如下图

在此输入图片描述

总共分成3大块,分别如下

##2.1 org.springframework.web.multipart

存放Spring定义的文件上传接口以及异常,如

  • MultipartException对用户抛出的解析异常(隐藏底层文件上传解析包所抛出的异常)

    也就指明了,这个体系下只能抛出这种类型的异常,MaxUploadSizeExceededException是MultipartException它的子类,专门用于指定文件大小限制的异常。

    用户不应该看到底层文件上传解析包所抛出的异常,底层采用的文件上传解析包在解析文件上传时也会定义自己的解析异常,这时候就需要在整合这些jar包时,需要对解析包所抛出的异常进行转换成上述已统一定义的面向用户的异常

    源码见证下:

    protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
    	String encoding = determineEncoding(request);
    	FileUpload fileUpload = prepareFileUpload(encoding);
    	try {
    		List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
    		return parseFileItems(fileItems, encoding);
    	}
    	catch (FileUploadBase.SizeLimitExceededException ex) {
    		throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
    	}
    	catch (FileUploadException ex) {
    		throw new MultipartException("Could not parse multipart servlet request", ex);
    	}
    }
    

    FileUploadBase.SizeLimitExceededException、FileUploadException 都是底层解析包apache fileupload解析时抛出的异常,在这里要进行try catch 处理,然后将这些异常转化成SpringMVC自定义的异常MaxUploadSizeExceededException、MultipartException

  • MultipartFile 定义了文件解析的统一结果类型

  • MultipartResolver 定义了文件解析的处理器,不同的处理器不同的解析方式

##2.2 org.springframework.web.multipart.commons

用于整合apache fileupload的解析,对上述定义的接口进行实现,如

  • CommonsMultipartFile实现上述MultipartFile接口,即采用apache fileupload解析的结果为CommonsMultipartFile
  • CommonsMultipartResolver实现上述MultipartResolver,待会详细说明

##2.3 org.springframework.web.multipart.support

用于整合j2ee自带的文件上传的解析,对上述定义的接口进行实现,如

  • StandardMultipartFile实现上述MultipartFile接口,即采用这种方式解析的结果为StandardMultipartFile
  • StandardServletMultipartResolver实现上述MultipartResolver,待会详细说明

接下来详细看看这些源码内容

#3 SpringMVC自己的接口设计

##3.1 MultipartResolver接口的内容:

public interface MultipartResolver {
	//判断当前的HttpServletRequest是否是文件上传类型
	boolean isMultipart(HttpServletRequest request);
	//将HttpServletRequest转化成MultipartHttpServletRequest
	MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;
	//清除产生的临时文件等
	void cleanupMultipart(MultipartHttpServletRequest request);
}

##3.2 MultipartHttpServletRequest接口内容:

MultipartHttpServletRequest 继承了 HttpServletRequest 和 MultipartRequest,然后就具有了下面的两个主要功能

  • 获取文件上传的每一部分的请求头信息

    HttpHeaders getRequestHeaders();
    HttpHeaders getMultipartHeaders(String paramOrFileName);
    

    这里的请求头信息就是如下内容中的 Content-Disposition: form-data; name="myFile"; filename="资产型号规格模板1.xlsx" Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 等信息

    文件上传的请求头信息

  • 获取文件上传的文件内容(每个文件信息都是MultipartFile类型)

    Iterator<String> getFileNames();
    MultipartFile getFile(String name);
    List<MultipartFile> getFiles(String name);
    Map<String, MultipartFile> getFileMap();
    

##3.3 整个处理流程

在SpringMVC的入口类DispatcherServlet中的doDispatch方法中,可以看到是如下的处理流程

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	boolean multipartRequestParsed = false;
	try {
		//略
		//步骤一
		processedRequest = checkMultipart(request);
		multipartRequestParsed = (processedRequest != request);
		//略
	}
	catch (Exception ex) {
		triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
	}
	finally {
		//略
		// Clean up any resources used by a multipart request.
		//步骤二
		if (multipartRequestParsed) {
			cleanupMultipart(processedRequest);
		}
	}
}

可以看到这里主要有两个步骤

  • 步骤一 检查是否是文件上传类型,如果是则进行解析,然后将HttpServletRequest request封装成MultipartHttpServletRequest
  • 步骤二 如果是文件上传,则进行资源清理,如删除上传的临时文件等

下面分别来说

###3.3.1 判断并解析HttpServletRequest成MultipartHttpServletRequest:

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
	if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
		if (request instanceof MultipartHttpServletRequest) {
			logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
					"this typically results from an additional MultipartFilter in web.xml");
		}
		else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) {
			logger.debug("Multipart resolution failed for current request before - " +
					"skipping re-resolution for undisturbed error rendering");
		}
		else {
			return this.multipartResolver.resolveMultipart(request);
		}
	}
	// If not returned before: return original request.
	return request;
}
  • 首先看看DispatcherServlet的multipartResolver属性是否有值,而我们在xml文件中如下的配置就是向DispatcherServlet注入multipartResolver属性

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
    	<property name="defaultEncoding" value="UTF-8" />  
    </bean>
    

    DispatcherServlet在初始化的时候,会去寻找id为"multipartResolver"并且类型为MultipartResolver的bean,所以id必须为MULTIPART_RESOLVER_BEAN_NAME即"multipartResolver",如下

    private void initMultipartResolver(ApplicationContext context) {
    try {
    	this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
    	if (logger.isDebugEnabled()) {
    		logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
    	}
    }
    catch (NoSuchBeanDefinitionException ex) {
    	// Default is no multipart resolver.
    	this.multipartResolver = null;
    	if (logger.isDebugEnabled()) {
    		logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
    				"': no multipart request handling provided");
    	}
    }
    

    }

  • 当multipartResolver属性有值的时候,先调用它的boolean isMultipart(HttpServletRequest request)方法,判断当前的request是否是符合文件上传类型,如果符合则调用它的MultipartHttpServletRequest resolveMultipart(HttpServletRequest request)方法将当前的request进行解析并且封装成MultipartHttpServletRequest类型。有了MultipartHttpServletRequest,我们就能获取上传的文件信息了。

    然后我们就可以通过2中途径来获取上传的文件。

    • 途径1 直接使用MultipartHttpServletRequest request作为参数,如下

      @RequestMapping(value="/test/file",method=RequestMethod.POST)
      @ResponseBody
      public String fileUpload(MultipartHttpServletRequest request){
      	Map<String, MultipartFile> files=request.getFileMap();
      	//使用files
      	return "success";
      }
      
    • 途径2 使用@RequestParam("myFile") 来获取文件(RequestParam里面的"myFile"是input标签的name的值而不是文件名),如下

      @RequestMapping(value="/test/file",method=RequestMethod.POST)
      @ResponseBody
      public String fileUpload(@RequestParam("myFile") MultipartFile file){
      	//使用file
      	return "success";
      }
      

    对于途径1很好理解,对于途径2,为什么呢?

    这里简单提下,对于@RequestParam注解是由RequestParamMethodArgumentResolver来进行处理的,是它进行了特殊处理,当@RequestParam修饰的类型为MultipartFile或者javax.servlet.http.Part(后面再详细说此Part)时进行特殊处理,如下

    @Override
    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) 
    throws Exception {
    	Object arg;
    
    	HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
    	MultipartHttpServletRequest multipartRequest =
    		WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
    
    	if (MultipartFile.class.equals(parameter.getParameterType())) {
    		assertIsMultipartRequest(servletRequest);
    		Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: 
    			is a MultipartResolver configured?");
    		arg = multipartRequest.getFile(name);
    	}
    	else if (isMultipartFileCollection(parameter)) {
    		assertIsMultipartRequest(servletRequest);
    		Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: 
    			is a MultipartResolver configured?");
    		arg = multipartRequest.getFiles(name);
    	}
    	else if(isMultipartFileArray(parameter)) {
    		assertIsMultipartRequest(servletRequest);
    		Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: 
    			is a MultipartResolver configured?");
    		arg = multipartRequest.getFiles(name).toArray(new MultipartFile[0]);
    	}
    	else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
    		assertIsMultipartRequest(servletRequest);
    		arg = servletRequest.getPart(name);
    	}
    	else if (isPartCollection(parameter)) {
    		assertIsMultipartRequest(servletRequest);
    		arg = new ArrayList<Object>(servletRequest.getParts());
    	}
    	else if (isPartArray(parameter)) {
    		assertIsMultipartRequest(servletRequest);
    		arg = RequestPartResolver.resolvePart(servletRequest);
    	}
    	else {
    		arg = null;
    		if (multipartRequest != null) {
    			List<MultipartFile> files = multipartRequest.getFiles(name);
    			if (!files.isEmpty()) {
    				arg = (files.size() == 1 ? files.get(0) : files);
    			}
    		}
    		if (arg == null) {
    			String[] paramValues = webRequest.getParameterValues(name);
    			if (paramValues != null) {
    				arg = paramValues.length == 1 ? paramValues[0] : paramValues;
    			}
    		}
    	}
    
    	return arg;
    }
    

    我们这里可以看到,其实也是通过MultipartHttpServletRequest的getFile等方法来获取的,同时支持数组、集合形式的参数

###3.3.2 清理占用的资源,如临时文件

protected void cleanupMultipart(HttpServletRequest servletRequest) {
	MultipartHttpServletRequest req = WebUtils.getNativeRequest(
		servletRequest, MultipartHttpServletRequest.class);
	if (req != null) {
		this.multipartResolver.cleanupMultipart(req);
	}
}

这里其实就是调用MultipartResolver接口的void cleanupMultipart(MultipartHttpServletRequest request)方法

至此SpringMVC已经完成了自己的文件上传框架体系,即底层不管采用何种文件解析包都是走这样的一个流程。这样的一个流程其实就是对实际业务的抽象过程。我们在写代码的时候,经常就缺少抽象的能力,即很少抽象出各种业务逻辑的共同点。

#4 整合apache fileupload对文件上传的解析 刚才说了整个文件上传的处理流程,然后我们就来看下apache fileupload是如何整合进来的。即CommonsMultipartResolver是如何实现的

##4.1 判断一个request是否是multipart形式的

	@Override
	public boolean isMultipart(HttpServletRequest request) {
		return (request != null && ServletFileUpload.isMultipartContent(request));
	}

这里就是使用apache fileupload自己的ServletFileUpload.isMultipartContent判断方法,上一篇文章已经讲述了,这里不再说明了。

这里我们可以再多想一下,功能的职责划分问题(虽然问题很简单,主要是想引导大家在写代码的时候多去思考)。

因为目前判断一个request是否是multipart形式,都是一样的,不管你是哪种解析包,为什么SpringMVC不统一进行判断,而是采用解析包的判断?

如果SpringMVC自己进行统一的判断,似乎也没什么问题。站在apache fileupload的角度来说,判断request是否是multipart形式 的确应该是它的一个功能,而不是等待外界来判断。

SpringMVC既然采用第三方的解析包,就要遵守人家解析包的判断逻辑,而不是自行判断,虽然他们目前的判断逻辑是一样的。万一后来又出来一个解析包,判断逻辑不一样呢?如果流程体系还是采用SpringMVC自己的判断,可能就没法正常解析了

##4.2 将HttpServletRequest解析成DefaultMultipartHttpServletRequest

一旦上述判断通过了,则就需要执行解析过程(可以立即解析,也可以延迟解析),看下具体的解析过程

public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
	Assert.notNull(request, "Request must not be null");
	if (this.resolveLazily) {
		return new DefaultMultipartHttpServletRequest(request) {
			@Override
			protected void initializeMultipart() {
				MultipartParsingResult parsingResult = parseRequest(request);
				setMultipartFiles(parsingResult.getMultipartFiles());
				setMultipartParameters(parsingResult.getMultipartParameters());
				setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
			}
		};
	}
	else {
		MultipartParsingResult parsingResult = parseRequest(request);
		return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),
				parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
	}
}

这里大致说下过程,详细的内容去看源代码。

  • 使用apache fileupload的ServletFileUpload对request进行解析,解析结果为List<FileItem>,代码如下:

    List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
    
  • FileItem为apache fileupload自己的解析结果,需要转化为SpringMVC自己定义的MultipartFile

    protected MultipartParsingResult parseFileItems(List<FileItem> fileItems, String encoding) {
    	MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<String,MultipartFile>();
    	Map<String, String[]> multipartParameters = new HashMap<String, String[]>();
    	Map<String, String> multipartParameterContentTypes = new HashMap<String, String>();
    
    	// Extract multipart files and multipart parameters.
    	for (FileItem fileItem : fileItems) {
    		if (fileItem.isFormField()) {
    			String value;
    			String partEncoding = determineEncoding(fileItem.getContentType(), encoding);
    			if (partEncoding != null) {
    				try {
    					value = fileItem.getString(partEncoding);
    				}
    				catch (UnsupportedEncodingException ex) {
    					value = fileItem.getString();
    				}
    			}
    			else {
    				value = fileItem.getString();
    			}
    			String[] curParam = multipartParameters.get(fileItem.getFieldName());
    			if (curParam == null) {
    				// simple form field
    				multipartParameters.put(fileItem.getFieldName(), new String[] {value});
    			}
    			else {
    				// array of simple form fields
    				String[] newParam = StringUtils.addStringToArray(curParam, value);
    				multipartParameters.put(fileItem.getFieldName(), newParam);
    			}
    			multipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());
    		}
    		else {
    			// multipart file field
    			CommonsMultipartFile file = new CommonsMultipartFile(fileItem);
    			multipartFiles.add(file.getName(), file);
    		}
    	}
    	return new MultipartParsingResult(multipartFiles, multipartParameters, 
    				multipartParameterContentTypes);
    }
    

这里有普通字段的处理和文件字段的处理。还记得上文讲的org.springframework.web.multipart.commons包的CommonsMultipartFile吗?可以看到通过new CommonsMultipartFile(fileItem),就将FileItem结果转化为了MultipartFile结果。

至此就将HttpServletRequest解析成了DefaultMultipartHttpServletRequest,所以我们在使用request时,它的类型其实就是DefaultMultipartHttpServletRequest类型,我们可以通过它来获取各种上传的文件信息。

##4.3 清理临时文件

其实就是对所有的CommonsMultipartFile中的FileItem进行删除临时文件的操作,这个删除操作是apache fileupload自己定义的,如下

protected void cleanupFileItems(MultiValueMap<String, MultipartFile> multipartFiles) {
	for (List<MultipartFile> files : multipartFiles.values()) {
		for (MultipartFile file : files) {
			if (file instanceof CommonsMultipartFile) {
				CommonsMultipartFile cmf = (CommonsMultipartFile) file;
				cmf.getFileItem().delete();
			}
		}
	}
}

至此,SpringMVC与apache fileupload的整合完成了,其他的整合也是类似的操作。

#5 整合j2ee自带的文件上传的解析

这个不再详细说明,主要引出来 javax.servlet.http.Part 这个对象是j2ee内置的文件上传解析结果,类似apache fileupload的FileItem解析结果,从Servlet3.0才加入进来的。

和apache fileupload一样的步骤,来看下具体源码内容:

##5.1 判断一个request是否是multipart形式的

@Override
public boolean isMultipart(HttpServletRequest request) {
	// Same check as in Commons FileUpload...
	if (!"post".equals(request.getMethod().toLowerCase())) {
		return false;
	}
	String contentType = request.getContentType();
	return (contentType != null && contentType.toLowerCase().startsWith("multipart/"));
}

同样是这两个条件,post和"multipart/"开头。

##5.2 将HttpServletRequest解析成StandardMultipartHttpServletRequest

@Override
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
	return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}

在创建StandardMultipartHttpServletRequest的时候进行解析,解析过程和apache fileupload非常类似,只不过用Part替代了apache fileupload的FileItem,如下

private void parseRequest(HttpServletRequest request) {
	try {
		Collection<Part> parts = request.getParts();
		this.multipartParameterNames = new LinkedHashSet<String>(parts.size());
		MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<String, MultipartFile>(parts.size());
		for (Part part : parts) {
			String filename = extractFilename(part.getHeader(CONTENT_DISPOSITION));
			if (filename != null) {
				files.add(part.getName(), new StandardMultipartFile(part, filename));
			}
			else {
				this.multipartParameterNames.add(part.getName());
			}
		}
		setMultipartFiles(files);
	}
	catch (Exception ex) {
		throw new MultipartException("Could not parse multipart servlet request", ex);
	}
}

遍历所有的Part,把每一个Part转化成StandardMultipartFile,而apache fileupload则是转化成CommonsMultipartFile。不再详细说明,具体的可以去看源码。

##5.3 遇到的一些问题 这里还有很多小插曲。

  • 我之前导入的一直是

    <dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>servlet-api</artifactId>
    	<version>2.5</version>
    	<scope>provided</scope>
    </dependency>
    

    之后把它换成3点多的版本,还是没找到javax.servlet.http.Part,最后才发现导入的是下面的形式

    <dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>javax.servlet-api</artifactId>
    	<version>3.0.1</version>
    	<scope>provided</scope>
    </dependency>
    

    这里的scope是provided,即不再加入运行环境,直接使用tomcat容器自身的servlet-api。目前我的tomcat7中servlet-api.jar包是含有这个javax.servlet.http.Part对象的,所以是可以的

  • 然后我就替换掉apache fileupload,使用Servlet3自带的Part功能,来使用文件上传,发现不行,没有得到解析结果,就想尝试调试下,然而运行到Collection<Part> parts = request.getParts()这里的时候,就不能查看源文件了,这里的request是org.apache.catalina.connector.RequestFacade类型,没有关联到源文件,经过一番寻找,最终找到tomcat的maven依赖

    <dependency>
    	<groupId>org.apache.tomcat</groupId>
    	<artifactId>tomcat-catalina</artifactId>
    	<version>7.0.55</version>
    	<scope>provided</scope>
    </dependency>
    

    有了它,我们就可以在调试的时候,查看tomcat内部的运行情况了

  • 然后一路跟踪,定位到结果为 需要将org.apache.catalina.core.StandardContext的allowCasualMultipartParsing属性设置为true,即允许进行文件解析,默认为false。需要在server.xml中修改工程配置,然后就大功告成了。

    <Context ... allowCasualMultipartParsing="true"/>
    

欢迎关注微信公众号:乒乓狂魔

微信公众号

© 著作权归作者所有

共有 人打赏支持
乒乓狂魔
粉丝 971
博文 105
码字总数 271356
作品 0
长宁
程序员
加载中

评论(13)

井井_
井井_
16mark
ericzgy
ericzgy
不错
发霉的辣条
发霉的辣条
Mark
yytf
yytf
不错
瓜杰
瓜杰
mark
龙行
龙行
mark
seno
seno
mark
流亡青年
收藏下,最近会用到tfs来实现。
云颖
云颖
必须顶,79
yangdong2014
yangdong2014
结合源码分析,不错.
ZHENFENGSHISAN/perfect-ssm

Quick Start 项目简介 ssm系列 ssm-demo:Spring+SpringMVC+Mybatis+easyUI整合 perfect-ssm:RESTful API+redis缓存 ssm-cluster:前后端分离+集群部署 ssm-dubbo:dubbo服务化 ssm-micro-se......

ZHENFENGSHISAN ⋅ 2017/09/18 ⋅ 0

spring mvc文件上传实现进度条

文件上传应该大部分人都接触过,一般都是基于commons-fileupload组件来实现,SpringMVC的文件上传功能也是在commons-fileupload组件提供的功能上面做了一些包装功能,使文件上传开发更容易方...

小天120 ⋅ 2014/02/10 ⋅ 20

SpringMVC源码剖析(二)- DispatcherServlet的前世今生

上一篇文章《SpringMVC源码剖析(一)- 从抽象和接口说起》中,我介绍了一次典型的SpringMVC请求处理过程中,相继粉墨登场的各种核心类和接口。我刻意忽略了源码中的处理细节,只列出最简单的...

相见欢 ⋅ 2013/01/05 ⋅ 25

SpringMVC MultipartFile转换成java.io.File类型

SpringMVC实现文件上传,看网上的文章基本都是通过MultipartFile实现,因为不想改服务层的接口,所以想问一下有什么方法可以将MultipartFile转换成File,或者Spring直接以File类型作为参数提...

kevinpan45 ⋅ 2014/05/03 ⋅ 1

Video-No.01 李守宏_SpringMVC基础与应用视频教程

1、SpringMVC基本配置 1)引入spring Jar包 2)配置web.xm文件,加载SpringMVC配置 <?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instanc......

shawntime ⋅ 2015/03/22 ⋅ 0

spring MVC入门示例(hello world demo)

Spring MVC介绍 Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应...

闵开慧 ⋅ 2014/07/29 ⋅ 0

springmvc - 文件上传

Springmvc 文件上传简介(https://linesh.gitbooks.io/spring-mvc-documentation-linesh-translation/content/publish/21-10/springs-multipart-file-upload-support.html) 使用servlet3 的 ......

中柠檬 ⋅ 2016/11/20 ⋅ 0

spring-mvc 增删查改和登陆demo

第一步: 配置ecipse的maven支持,新建一个maven-webapp项目,添加jar包。 可以直接在http://mvnrepository.com/搜索,主要依赖的jar包有:spring web mvc ,spring jdbc,mysql,log4j,asp...

数学家 ⋅ 2016/03/02 ⋅ 0

springMVC笔记系列(21)——springMVC自带的上传文件功能实现

springMVC为我们提供了上传文件的内部支持,我们只需要一些配置,然后就可以借助于sprinngMVC提供给我们的接口完成文件上传的工作。 首选找到与springMVC的前端控制器DispatchServlet相关的上...

HappyBKs ⋅ 2016/07/05 ⋅ 0

Maven搭建SpringMVC+Hibernate项目详解

前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这篇主要搭建SpringMVC4.1.4和H...

Airship ⋅ 2016/12/12 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

从零开始搭建Risc-v Rocket环境---(1)

为了搭建Rocke环境,我买了一个2T的移动硬盘,安装的ubuntu-16.04 LTS版。没有java8,gcc是5.4.0 joe@joe-Inspiron-7460:~$ java -version程序 'java' 已包含在下列软件包中: * default-...

whoisliang ⋅ 6分钟前 ⋅ 0

大数据学习路线(自己制定的,从零开始学习大数据)

大数据已经火了很久了,一直想了解它学习它结果没时间,过年后终于有时间了,了解了一些资料,结合我自己的情况,初步整理了一个学习路线,有问题的希望大神指点。 学习路线 Linux(shell,高并...

董黎明 ⋅ 12分钟前 ⋅ 0

systemd编写服务

一、开机启动 对于那些支持 Systemd 的软件,安装的时候,会自动在/usr/lib/systemd/system目录添加一个配置文件。 如果你想让该软件开机启动,就执行下面的命令(以httpd.service为例)。 ...

勇敢的飞石 ⋅ 14分钟前 ⋅ 0

mysql 基本sql

CREATE TABLE `BBB_build_info` ( `community_id` varchar(50) NOT NULL COMMENT '小区ID', `layer` int(11) NOT NULL COMMENT '地址层数', `id` int(11) NOT NULL COMMENT '地址id', `full_......

zaolonglei ⋅ 23分钟前 ⋅ 0

安装chrome的vue插件

参看文档:https://www.cnblogs.com/yulingjia/p/7904138.html

xiaoge2016 ⋅ 25分钟前 ⋅ 0

用SQL命令查看Mysql数据库大小

要想知道每个数据库的大小的话,步骤如下: 1、进入information_schema 数据库(存放了其他的数据库的信息) use information_schema; 2、查询所有数据的大小: select concat(round(sum(da...

源哥L ⋅ 48分钟前 ⋅ 0

两个小实验简单介绍@Scope("prototype")

实验一 首先有如下代码(其中@RestController的作用相当于@Controller+@Responsebody,可忽略) @RestController//@Scope("prototype")public class TestController { @RequestMap...

kalnkaya ⋅ 53分钟前 ⋅ 0

php-fpm的pool&php-fpm慢执行日志&open_basedir&php-fpm进程管理

12.21 php-fpm的pool pool是PHP-fpm的资源池,如果多个站点共用一个pool,则可能造成资源池中的资源耗尽,最终访问网站时出现502。 为了解决上述问题,我们可以配置多个pool,不同的站点使用...

影夜Linux ⋅ 今天 ⋅ 0

微服务 WildFly Swarm 管理

Expose Application Metrics and Information 要公开关于我们的微服务的有用信息,我们需要做的就是将监视器模块添加到我们的pom.xml中: 这将使在管理和监视功能得到实现。从监控角度来看,...

woshixin ⋅ 今天 ⋅ 0

java连接 mongo伪集群部署遇到的坑

部署mongo伪集群 #创建mongo数据存放文件地址mkdir -p /usr/local/config1/datamkdir -p /usr/local/config2/data mkdir -p /usr/local/config3/data mkdir -p /usr/local/config1/l......

努力爬坑人 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部