我是靠谱客的博主 深情水池,最近开发中收集的这篇文章主要介绍引入jar包里的css样式_以Jar形式为Web项目提供资源文件(JS、CSS与图片),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、背景

最近正在编写TagLib,在开发的过程中遇到一个资源文件引用问题。因为我开发的TagLib最终是以Jar包的形式提供给项目来用的,所以Jar包中必须包含我开发TagLib所需的JS、CSS与图片等资源。问题就是Tag是在项目的Web工程中运行,如何访问到jar中的资源。

二、分析

我想了一下,应该有两种方式:

1、把我需要的JS、CSS与图片等资源copy到Web工程中。

好处:

通过原生的Web服务器来访问,速度与性能上讲会好一些。

缺点:

Web工程必须以目录方式部署。(非war)

存放资源的目录名需要与Web工程明确约定。(防止对原Web项目文件进行覆盖)

2、通过程序采用流的方式读取Jar中的资源流再输出到页面流。

好处:

不依赖Web工程的部署方式。

不会复制文件到Web工程。

缺点:

以流的方式实时从Jar中读取。(速度与性能上讲并非最优)

页面流输出时需要指定内容类型Content-Type。(前者会由Web服务器来维护)

三、分析结果

最终我准备将1、2两种情况接合使用,默认会采用1复制文件到Web工程的方式。如果发现Web工程无法复制文件则采用2流读取方式。

四、核心代码开发(Jar端)

为了进行两种方式的切换定义一个Filter非常适合,可以拦截请求干扰行为,又可以在init初始化时进行资源文件的复制。

从下面的目录结构可以看出主程序就是一个Filter类,org.noahx.jarresource.resource包下的内容就是我需要的资源目录。Filter会自动判断Web工程的部署方式(目录与War)来决定复制资源目录还是直接流读取。

1、org.noahx.jarresource.TagLibResourceFilter(程序内逻辑详见注释)

package org.noahx.jarresource;

import org.apache.commons.io.FileUtils;

import org.apache.commons.io.FilenameUtils;

import org.apache.commons.io.IOUtils;

import org.apache.commons.lang.StringUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import sun.net.www.protocol.file.FileURLConnection;

import javax.servlet.*;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.File;

import java.io.IOException;

import java.io.InputStream;

import java.net.JarURLConnection;

import java.net.URL;

import java.net.URLConnection;

import java.util.Enumeration;

import java.util.HashMap;

import java.util.Map;

import java.util.jar.JarEntry;

import java.util.jar.JarFile;

/**

* Created with IntelliJ IDEA.

* User: noah

* Date: 6/24/13

* Time: 8:18 PM

* To change this template use File | Settings | File Templates.

*/

public class TagLibResourceFilter implements Filter {

private static final String RESOURCE_PACKAGE_PATH = "/org/noahx/jarresource/resource";

private static final String RESOURCE_CHARSET = "UTF-8";

private static final String DEFAULT_MINE_TYPE = "application/octet-stream";

private static String resourcePath;

private final Logger logger = LoggerFactory.getLogger(this.getClass());

private ResourceMode resourceMode;

private static enum ResourceMode {

Dir, Jar

}

private static final Map MINE_TYPE_MAP;

static {

MINE_TYPE_MAP = new HashMap();

MINE_TYPE_MAP.put("js", "application/javascript;charset=" + RESOURCE_CHARSET);

MINE_TYPE_MAP.put("css", "text/css;charset=" + RESOURCE_CHARSET);

MINE_TYPE_MAP.put("gif", "image/gif");

MINE_TYPE_MAP.put("jpg", "image/jpeg");

MINE_TYPE_MAP.put("jpeg", "image/jpeg");

MINE_TYPE_MAP.put("png", "image/png");

}

public static String getResourcePath() {

return TagLibResourceFilter.resourcePath;

}

private static void setResourcePath(String resourcePath) {

TagLibResourceFilter.resourcePath = resourcePath;

}

@Override

public void init(FilterConfig filterConfig) throws ServletException {

String resPath = filterConfig.getInitParameter("resourcePath");

if (!resPath.startsWith("/")) {

resPath = "/" + resPath;

}

setResourcePath(resPath);

String rootPath = filterConfig.getServletContext().getRealPath("/");

if (rootPath != null) { //如果web工程是目录方式运行

String dirPath = filterConfig.getServletContext().getRealPath(resPath);

File dir = null;

try {

dir = new File(dirPath);

FileUtils.deleteQuietly(dir); //清除老资源

FileUtils.forceMkdir(dir); //重新创建资源目录

if(logger.isDebugEnabled()){

logger.debug("create dir '"+dirPath+"'");

}

} catch (Exception e) {

logger.error("Error creating TagLib Resource dir", e);

}

try {

copyResourcesRecursively(this.getClass().getResource(RESOURCE_PACKAGE_PATH), dir); //复制classpath中的资源到目录

} catch (Exception e) {

logger.error(e.getMessage(), e);

}

resourceMode = ResourceMode.Dir; //设置为目录模式

} else {

resourceMode = ResourceMode.Jar; //设置为jar包模式

}

if(logger.isDebugEnabled()){

logger.debug("ResourceMode:"+resourceMode);

}

}

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

switch (resourceMode) {

case Dir:

chain.doFilter(request, response);

break;

case Jar: {

HttpServletRequest req = (HttpServletRequest) request;

String path = req.getRequestURI().substring(req.getContextPath().length()); //uri去掉web上下文

HttpServletResponse rep = (HttpServletResponse) response;

if (path.startsWith(getResourcePath() + "/")) { //resourcePath必须与url-pattern一致

path = path.substring(getResourcePath().length()); //uri去掉resourcePath

try {

URL resource = this.getClass().getResource(RESOURCE_PACKAGE_PATH + path); //可能存在潜在安全问题

if (resource == null) { //如果在类路径中没有找到资源->404

rep.sendError(HttpServletResponse.SC_NOT_FOUND);

} else {

InputStream inputStream = readResource(resource);

if (inputStream != null) { //有inputstream说明已经读到jar中内容

String ext = FilenameUtils.getExtension(path).toLowerCase();

String contentType = MINE_TYPE_MAP.get(ext);

if (contentType == null) {

contentType = DEFAULT_MINE_TYPE;

}

rep.setContentType(contentType); //设置内容类型

ServletOutputStream outputStream = rep.getOutputStream();

try {

int size = IOUtils.copy(inputStream, outputStream); //向输出流输出内容

rep.setContentLength(size);

} finally {

IOUtils.closeQuietly(inputStream);

IOUtils.closeQuietly(outputStream);

}

} else { //没有inputstream->404

rep.sendError(HttpServletResponse.SC_NOT_FOUND);

}

}

} catch (Exception e) {

logger.error(e.getMessage(), e);

rep.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

}

} else {

logger.error("MUST set url-pattern="" + resourcePath + "/*"!!");

rep.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

}

}

break;

}

}

@Override

public void destroy() {

}

private InputStream readResource(URL originUrl) throws Exception {

InputStream inputStream = null;

URLConnection urlConnection = originUrl.openConnection();

if (urlConnection instanceof JarURLConnection) {

inputStream = readJarResource((JarURLConnection) urlConnection);

} else if (urlConnection instanceof FileURLConnection) {

File originFile = new File(originUrl.getPath());

if (originFile.isFile()) {

inputStream = originUrl.openStream();

}

} else {

throw new Exception("URLConnection[" + urlConnection.getClass().getSimpleName() +

"] is not a recognized/implemented connection type.");

}

return inputStream;

}

private InputStream readJarResource(JarURLConnection jarConnection) throws Exception {

InputStream inputStream = null;

JarFile jarFile = jarConnection.getJarFile();

if (!jarConnection.getJarEntry().isDirectory()) { //如果jar中内容为目录则不返回inputstream

inputStream = jarFile.getInputStream(jarConnection.getJarEntry());

}

return inputStream;

}

private void copyResourcesRecursively(URL originUrl, File destination) throws Exception {

URLConnection urlConnection = originUrl.openConnection();

if (urlConnection instanceof JarURLConnection) {

copyJarResourcesRecursively(destination, (JarURLConnection) urlConnection);

} else if (urlConnection instanceof FileURLConnection) {

FileUtils.copyDirectory(new File(originUrl.getPath()), destination); //如果不是jar则采用目录copy

if(logger.isDebugEnabled()){

logger.debug("copy dir '"+originUrl.getPath()+"' --> '"+destination.getPath()+"'");

}

} else {

throw new Exception("URLConnection[" + urlConnection.getClass().getSimpleName() +

"] is not a recognized/implemented connection type.");

}

}

private void copyJarResourcesRecursively(File destination, JarURLConnection jarConnection) throws IOException {

JarFile jarFile = jarConnection.getJarFile();

Enumeration entries = jarFile.entries();

while (entries.hasMoreElements()) { //遍历jar内容逐个copy

JarEntry entry = entries.nextElement();

if (entry.getName().startsWith(jarConnection.getEntryName())) {

String fileName = StringUtils.removeStart(entry.getName(), jarConnection.getEntryName());

File destFile = new File(destination, fileName);

if (!entry.isDirectory()) {

InputStream entryInputStream = jarFile.getInputStream(entry);

FileUtils.copyInputStreamToFile(entryInputStream, destFile);

if(logger.isDebugEnabled()){

logger.debug("copy jarfile to file '"+entry.getName()+"' --> '"+destination.getPath()+"'");

}

} else {

FileUtils.forceMkdir(destFile);

if(logger.isDebugEnabled()){

logger.debug("create dir '"+destFile.getPath()+"'");

}

}

}

}

}

}

补充:Filter中提供了静态方法getResourcePath()来获得当前的资源路径,我的TagLib中就可以通过该方法获得资源URI。

2、pom.xml

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.noahx.jarresource

resource

1.0-SNAPSHOT

jar

commons-io

commons-io

2.4

org.slf4j

slf4j-api

1.7.5

commons-lang

commons-lang

2.6

javax.servlet

servlet-api

2.5

provided

使用了commons-io与commons-lang第三方类包

五、核心代码开发(Web端)

作为Jar文件的使用端,只需要在web.xml中配置一个filter,就可以访问到Jar中的资源。

1、web.xml

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

version="2.5">

jar resource web

tagLibResourceFilter

org.noahx.jarresource.TagLibResourceFilter

resourcePath

/tagres

tagLibResourceFilter

/tagres/*

index.jsp

注意:由于Servlet 3.0以下无法通过程序获得url-pattern,所以在filter的参数中指定了一个同名路径来使用。filter会用这个路径名称在Web工程下创建资源目录(目录部署)。

2、index.jsp(资源使用样例,JS、CSS与图片)

star-hover4.png

example.js (example)

css.css redbox

tagres/中的内容就是Jar工程中所提供的资源。(下图为显示效果)

3、pom.xml

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.noahx.jarresource

web

1.0-SNAPSHOT

war

org.noahx.jarresource

resource

1.0-SNAPSHOT

org.slf4j

slf4j-log4j12

1.7.5

runtime

六、Web工程两种模式的Filter日志

1、目录部署方式

[JAR-RES] 2013-06-26 13:11:13,132 DEBUG [org.noahx.jarresource.TagLibResourceFilter] - create dir '/nautilus/develop/jar-resource/web/target/web-1.0-SNAPSHOT/tagres' (TagLibResourceFilter.java:93)

[JAR-RES] 2013-06-26 13:11:13,146 DEBUG [org.noahx.jarresource.TagLibResourceFilter] - create dir '/nautilus/develop/jar-resource/web/target/web-1.0-SNAPSHOT/tagres/' (TagLibResourceFilter.java:240)

[JAR-RES] 2013-06-26 13:11:13,147 DEBUG [org.noahx.jarresource.TagLibResourceFilter] - create dir '/nautilus/develop/jar-resource/web/target/web-1.0-SNAPSHOT/tagres/imgs' (TagLibResourceFilter.java:240)

[JAR-RES] 2013-06-26 13:11:13,152 DEBUG [org.noahx.jarresource.TagLibResourceFilter] - copy jarfile to file 'org/noahx/jarresource/resource/imgs/star-hover4.png' --> '/nautilus/develop/jar-resource/web/target/web-1.0-SNAPSHOT/tagres' (TagLibResourceFilter.java:235)

[JAR-RES] 2013-06-26 13:11:13,153 DEBUG [org.noahx.jarresource.TagLibResourceFilter] - copy jarfile to file 'org/noahx/jarresource/resource/example.js' --> '/nautilus/develop/jar-resource/web/target/web-1.0-SNAPSHOT/tagres' (TagLibResourceFilter.java:235)

[JAR-RES] 2013-06-26 13:11:13,154 DEBUG [org.noahx.jarresource.TagLibResourceFilter] - copy jarfile to file 'org/noahx/jarresource/resource/css.css' --> '/nautilus/develop/jar-resource/web/target/web-1.0-SNAPSHOT/tagres' (TagLibResourceFilter.java:235)

[JAR-RES] 2013-06-26 13:11:13,154 DEBUG [org.noahx.jarresource.TagLibResourceFilter] - ResourceMode:Dir (TagLibResourceFilter.java:111)

可以看到copy资源文件的过程

2、War包部署方式

[JAR-RES] 2013-06-26 13:12:25,287 DEBUG [org.noahx.jarresource.TagLibResourceFilter] - ResourceMode:Jar (TagLibResourceFilter.java:111) 从Jar中直接读取,并没有copy资源的过程。

七、总结

这个Filter很好的解决了我在开发TagLib时遇到的资源引用问题,对我来说应该够用了。

我们项目中一般采用目录方式部署,我也更希望通过Web服务器来直接访问资源。

并没有采用maven来组织资源,因为我需要提供给非maven工程使用。

一些流行的mvc中也有类似的手法,可能是采用流方式读取(猜测),感兴趣的朋友可以查看这些mvc的代码。

八、源程序下载

下载包中提供了源代码以及打包后(target目录)的工程。

最后

以上就是深情水池为你收集整理的引入jar包里的css样式_以Jar形式为Web项目提供资源文件(JS、CSS与图片)的全部内容,希望文章能够帮你解决引入jar包里的css样式_以Jar形式为Web项目提供资源文件(JS、CSS与图片)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部