`
huangpengpeng
  • 浏览: 32438 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

springmvc 扩展实现类似于淘宝CDN 资源文件处理

阅读更多

1. SpringMVC XML 配置

       <!-- 简单URLaction映射 -->

<bean id="simpleUrlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<!-- 静态资源处理器 -->
<entry key="/r/**/**">
<!-- 自己扩展的SpingMVC ResourceHttpRequestHandler 类,增加了类是与淘宝CDN通过逗号(,)隔开 
访问多个js的效果 此功能不能压缩JS 如果想实现压缩功能可通过maven 或者 ant 在编译的时候进行压缩 -->
<bean class="com.yoro.core.springmvc.ResourceHttpRequestHandler">
<property name="locations">
<list>
<!-- 只有相同目录的JS文件才能实现淘宝CDN通过逗号(,)隔开 访问多个js的效果 如 /r/ 目录下的js|css 文件 
就能实现 /r/js/ 目录下的js文件也可以有同样的效果 但 /a/css 下面的文件 和 /r/css 就不能实现 有点小遗憾,因为时间关系待以后升级 -->
<value>/r/</value>
</list>
</property>
<!-- 启用静态资源浏览器缓存一个月 -->
<!-- 通过浏览器进行的缓存 根据可浏览器实现方式不同有所差异 按刷新按扭缓存不会起 当是页面跳转或者地址栏输入则缓存会起作用 -->
<!-- 更过浏览器缓存的资料和特效 可 搜索 cachecontrol 设置   cacheSeconds 缓存时间单位是秒  2592000 表示缓存30天 因为我自己每次新版本发布都会都js css 文件增加版本号 所以缓存时间我设置的比较长 -->
<property name="cacheSeconds" value="2592000"></property>
<property name="useExpiresHeader" value="true"></property>
<property name="useCacheControlNoStore" value="true"></property>
</bean>
</entry>
<entry key="/thirdparty/**/**">
<bean class="com.yoro.core.springmvc.ResourceHttpRequestHandler">
<property name="locations">
<list>
<value>/thirdparty/</value>
</list>
</property>
<!-- 启用静态资源浏览器缓存一个月 -->
<property name="cacheSeconds" value="2592000"></property>
<property name="useExpiresHeader" value="true"></property>
<property name="useCacheControlNoStore" value="true"></property>
</bean>
</entry>
</map>
</property>
<property name="order" value="1"></property>
</bean>

 

2. 资源文件目录结构

3. 浏览器缓存效果


 4.ResourceHttpRequestHandler  扩展类代码

package com.yoro.core.springmvc;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.support.WebContentGenerator;

public class ResourceHttpRequestHandler 
extends WebContentGenerator implements HttpRequestHandler, InitializingBean  {

	
	private static final boolean jafPresent =
			ClassUtils.isPresent("javax.activation.FileTypeMap", ResourceHttpRequestHandler.class.getClassLoader());

	private final static Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class);


	private List<Resource> locations;


	public ResourceHttpRequestHandler() {
		super(METHOD_GET, METHOD_HEAD);
	}

	/**
	 * Set a {@code List} of {@code Resource} paths to use as sources
	 * for serving static resources.
	 */
	public void setLocations(List<Resource> locations) {
		Assert.notEmpty(locations, "Locations list must not be empty");
		this.locations = locations;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) {
			logger.warn("Locations list is empty. No resources will be served");
		}
	}

	@Override
	public void handleRequest(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		checkAndPrepare(request, response, true);

		// check whether a matching resource exists
		List<Resource> resources = getResources(request);
		if (resources == null || resources.isEmpty()) {
			logger.debug("No matching resource found - returning 404");
			response.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
		}

		// check the resource's media type
		MediaType mediaType = getMediaType((String)request
				.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE));
		if (mediaType != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Determined media type '" + mediaType + "' for "
						+ resources.get(0));
			}
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("No media type found for " + resources.get(0)
						+ " - not sending a content-type header");
			}
		}

		for (Resource resource : resources) {
			// header phase
			if (!new ServletWebRequest(request, response)
					.checkNotModified(resource.lastModified())) {
				logger.debug("Resource not modified - returning 304");
				break;
			}
			return;
		}

		setHeaders(response, resources, mediaType);

		// content phase
		if (METHOD_HEAD.equals(request.getMethod())) {
			logger.trace("HEAD request - skipping content");
			return;
		}
		writeContent(response, resources);
	}
	
	protected MediaType getMediaType(String filename) {
		MediaType mediaType = null;
		String mimeType = getServletContext().getMimeType(filename);
		if (StringUtils.hasText(mimeType)) {
			mediaType = MediaType.parseMediaType(mimeType);
		}
		if (jafPresent && (mediaType == null || MediaType.APPLICATION_OCTET_STREAM.equals(mediaType))) {
			MediaType jafMediaType = ActivationMediaTypeFactory.getMediaType(filename);
			if (jafMediaType != null && !MediaType.APPLICATION_OCTET_STREAM.equals(jafMediaType)) {
				mediaType = jafMediaType;
			}
		}
		return mediaType;
	}

	protected void setHeaders(HttpServletResponse response,
			List<Resource> resources, MediaType mediaType) throws IOException {
		long length = 0;
		//Calculation of multiple file length
		Iterator<Resource> iter = resources.iterator();
		while (iter.hasNext()) {
			length += iter.next().contentLength();
		}
		if (length > Integer.MAX_VALUE) {
			throw new IOException(
					"Resource content too long (beyond Integer.MAX_VALUE)");
		}
		response.setContentLength((int) length);
		if (mediaType != null) {
			response.setContentType(mediaType.toString());
		}
	}

	protected void writeContent(HttpServletResponse response,
			List<Resource> resourcess) throws IOException {
		OutputStream out = response.getOutputStream();
		InputStream in = null;
		try {
			for (Resource resource : resourcess) {
				try {
					in = resource.getInputStream();
					StreamUtils.copy(in, out);
				} finally {
					try {
						in.close();
					} catch (IOException ex) {
					}
				}
			}
		} finally {
			try {
				out.close();
			} catch (IOException ex) {
			}
		}
	}

	protected List<Resource> getResources(HttpServletRequest request) {
		String path = (String) request
				.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
		if (path == null) {
			throw new IllegalStateException("Required request attribute '"
					+ HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
					+ "' is not set");
		}

		if (!StringUtils.hasText(path) || isInvalidPath(path)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Ignoring invalid resource path [" + path + "]");
			}
			return null;
		}

		for (Resource location : this.locations) {
			try {
				if (logger.isDebugEnabled()) {
					logger.debug("Trying relative path [" + path
							+ "] against base location: " + location);
				}
				List<Resource> rs = new ArrayList<Resource>();
				String[] paths = path.split(",");
				for (String url : paths) {
					Resource resource = location.createRelative(url);
					if (resource.exists() && resource.isReadable()) {
						rs.add(resource);
					}
				}
				return rs;
			} catch (IOException ex) {
				logger.debug(
						"Failed to create relative resource - trying next resource location",
						ex);
			}
		}
		return null;
	}
	
	/**
	 * Validates the given path: returns {@code true} if the given path is not a valid resource path.
	 * <p>The default implementation rejects paths containing "WEB-INF" or "META-INF" as well as paths
	 * with relative paths ("../") that result in access of a parent directory.
	 * @param path the path to validate
	 * @return {@code true} if the path has been recognized as invalid, {@code false} otherwise
	 */
	protected boolean isInvalidPath(String path) {
		return (path.contains("WEB-INF") || path.contains("META-INF") || StringUtils.cleanPath(path).startsWith(".."));
	}

	/**
	 * Inner class to avoid hard-coded JAF dependency.
	 */
	private static class ActivationMediaTypeFactory {

		private static final FileTypeMap fileTypeMap;

		static {
			fileTypeMap = loadFileTypeMapFromContextSupportModule();
		}

		private static FileTypeMap loadFileTypeMapFromContextSupportModule() {
			// see if we can find the extended mime.types from the context-support module
			Resource mappingLocation = new ClassPathResource("org/springframework/mail/javamail/mime.types");
			if (mappingLocation.exists()) {
				InputStream inputStream = null;
				try {
					inputStream = mappingLocation.getInputStream();
					return new MimetypesFileTypeMap(inputStream);
				}
				catch (IOException ex) {
					// ignore
				}
				finally {
					if (inputStream != null) {
						try {
							inputStream.close();
						}
						catch (IOException ex) {
							// ignore
						}
					}
				}
			}
			return FileTypeMap.getDefaultFileTypeMap();
		}

		public static MediaType getMediaType(String filename) {
			String mediaType = fileTypeMap.getContentType(filename);
			return (StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null);
		}
	}
}

 5.实现淘宝CDN JS 请求例子

JS例子:http://127.0.0.1/r/js/alert.js,/js/application.js,/js/bootstrap.js

CSS例子:http://127.0.1.1/r/css/activity_style.css,/css/bootstrap_responsive.css

通过逗号(,)分割他们现在就实现了通过一个请求加载多个资源文件的效果了

 

 

 

  • 大小: 114.2 KB
  • 大小: 258.2 KB
分享到:
评论

相关推荐

    防止SpringMVC拦截器拦截js等静态资源文件的解决方法

    通过在web.xml文件中对默认Servlet进行映射(通常是url-pattern为/*),可以让默认Servlet先于SpringMVC的DispatcherServlet处理静态资源的请求。这种方法的优点是不需要在SpringMVC的拦截器中进行繁琐的配置,而且...

    IDEA SpringMVC 实现文件的上传下载

    在SpringMVC框架中,实现文件的上传与下载是常见的需求。IntelliJ IDEA作为流行的Java开发集成环境,为开发者提供了高效便捷的开发体验。在这个项目中,我们使用IDEA来构建一个基于SpringMVC的系统,实现了文件的...

    SpringMVC框架实现文件的上传和下载

    在SpringMVC框架中,文件的上传和下载是常见的功能需求,主要用于处理用户的数据交互,例如用户上传图片、文档等,或者系统提供文件下载服务。本文将深入探讨如何使用SpringMVC来实现这一功能。 首先,我们需要理解...

    springMVC+cdn(qiniu+bootcss)

    SpringMVC的核心组件包括DispatcherServlet、ModelAndView、Controllers等,它们协同工作以实现高效的请求处理。 接下来,CDN(Content Delivery Network)是一种分布式网络服务,通过在网络的多个节点上缓存静态...

    基于springmvc实现文件上传下载 基于AOP的日志功能

    基于springmvc实现文件上传下载 基于AOP的日志功能基于springmvc实现文件上传下载 基于AOP的日志功能基于springmvc实现文件上传下载 基于AOP的日志功能基于springmvc实现文件上传下载 基于AOP的日志功能基于...

    springmvc+hadoop+maven实现文件上传至hdfs

    在本项目中,我们结合了SpringMVC、Hadoop和Maven这三个技术,构建了一个能够实现文件从Web端上传到HDFS(Hadoop Distributed File System)的系统。下面将详细阐述这三个技术及其在项目中的应用。 首先,SpringMVC...

    SpringMVC+Ajax异步文件上传

    `SpringMVC`作为Java后端的一个强大框架,提供了处理文件上传的能力。而`Ajax`技术则使得页面可以在不刷新的情况下与服务器进行交互,实现异步上传,极大地提升了用户体验。在本教程中,我们将探讨如何结合`...

    SpringMVC文件上传,多文件上传实例

    在这个“SpringMVC文件上传,多文件上传实例”中,我们将深入探讨如何在SpringMVC环境中实现文件上传功能,包括单个文件上传以及多个文件的批量上传。 1. **文件上传原理**: 文件上传是通过HTTP协议的POST请求来...

    SpringMVC单文件上传、多文件上传、文件列表显示、文件下载

    以下将详细讲解SpringMVC如何实现单文件上传、多文件上传、文件列表显示以及文件下载。 1. **单文件上传** 在SpringMVC中,我们可以使用`@RequestParam("file") MultipartFile file`注解来接收前端提交的单个文件...

    netty整合SpringMVC实现下载

    Netty 是一个高性能、异步事件驱动的网络应用程序框架,常用于开发高并发、低延迟的网络服务。SpringMVC 是 Spring 框架的一部分...这种整合方式尤其适用于大文件下载或高并发场景,能充分利用硬件资源,提升用户体验。

    springmvc实现上传文件到七牛云

    在本项目中,我们利用 SpringMVC 实现了文件上传的功能,并将这些文件存储到七牛云存储服务上。 首先,我们需要了解 SpringMVC 中的文件上传机制。在 SpringMVC 中,文件上传是通过 `CommonsMultipartResolver` ...

    SpringMVC是实现登陆

    总结起来,使用SpringMVC实现登录功能涉及的知识点包括:SpringMVC的MVC架构、Controller的请求处理、模型绑定、服务层的业务逻辑、Session管理、Spring Security的安全控制、视图解析和错误处理。掌握这些知识点,...

    SpringMVC 实现文件上传下载、国际化等

    SpringMVC提供了一种优雅的方式实现国际化,主要通过消息资源文件和`LocaleResolver`实现。 1. **创建消息资源文件**:在项目的`src/main/resources`目录下,创建对应不同语言的.properties文件,如`messages_en....

    基于easyui+springmvc实现的文件管理系统

    【标题】:“基于EasyUI+SpringMVC实现的文件管理系统” 在现代企业信息化管理中,文件管理系统扮演着至关重要的角色,它能够有效地组织、存储、检索和管理企业内部的各类文档,提高工作效率,保证信息的安全性。本...

    springMvc单文件多文件上传

    springMvc 文件上传,springMvc 支持单文件和多文件上传,

    springMVC整合MyBatis+sql文件

    总的来说,"springMVC整合MyBatis+sql文件"是一个包含用户登录、分页查询等功能的Web应用示例,它演示了如何将SpringMVC和MyBatis结合使用,通过SQL文件进行数据操作。开发者可以在这个基础上学习如何搭建类似的系统...

    SpringMVC一些配置文件的demo

    SpringMVC是Spring框架的一部分,专门用于处理Web应用程序的请求-响应模型。它是一个轻量级的、基于注解的模型-视图-控制器(MVC)架构,为Java开发者提供了一种简单而强大的方式来构建可维护的Web应用。在...

    springmvc文件下载

    本篇将详细探讨如何利用SpringMVC结合EasyUI实现文件下载功能,并着重讲解SpringMVC中的注解技术。 首先,SpringMVC是Spring框架的一部分,它提供了模型-视图-控制器(MVC)架构模式的实现,简化了Java Web应用程序...

    SpringMVC文件上传Demo代码

    在SpringMVC中实现文件上传是一项常见的任务,它允许用户通过表单将本地文件发送到服务器进行存储或处理。这个"SpringMVC文件上传Demo代码"是一个实例,演示了如何配置和使用SpringMVC来实现这一功能。 首先,我们...

    SpringMVC 拦截器 实现 登录退出功能

    SpringMVC的拦截器是基于Servlet的Filter机制扩展而来的,它通过`HandlerInterceptor`接口实现。这个接口定义了三个方法:`preHandle()`、`postHandle()` 和 `afterCompletion()`。当一个请求到达时,SpringMVC会...

Global site tag (gtag.js) - Google Analytics