javascript
以Jar形式为Web项目提供资源文件(JS、CSS与图片)
- 一、背景
- 二、分析
- 1、把我需要的JS、CSS與圖片等資源copy到Web工程中。
- 2、通過程序采用流的方式讀取Jar中的資源流再輸出到頁(yè)面流。
- 三、分析結(jié)果
- 四、核心代碼開發(fā)(Jar端)
- 1、org.noahx.jarresource.TagLibResourceFilter(程序內(nèi)邏輯詳見注釋)
- 2、pom.xml
- 五、核心代碼開發(fā)(Web端)
- 1、web.xml
- 2、index.jsp(資源使用樣例,JS、CSS與圖片)
- 3、pom.xml
- 六、Web工程兩種模式的Filter日志
- 1、目錄部署方式
- 2、War包部署方式
- 七、總結(jié)
- 八、源程序下載
一、背景
最近正在編寫TagLib,在開發(fā)的過程中遇到一個(gè)資源文件引用問題。因?yàn)槲议_發(fā)的TagLib最終是以Jar包的形式提供給項(xiàng)目來用的,所以Jar包中必須包含我開發(fā)TagLib所需的JS、CSS與圖片等資源。問題就是Tag是在項(xiàng)目的Web工程中運(yùn)行,如何訪問到j(luò)ar中的資源。
二、分析
我想了一下,應(yīng)該有兩種方式:
1、把我需要的JS、CSS與圖片等資源copy到Web工程中。
? ? 好處:
- 通過原生的Web服務(wù)器來訪問,速度與性能上講會(huì)好一些。
? ? 缺點(diǎn):
- Web工程必須以目錄方式部署。(非war)
- 存放資源的目錄名需要與Web工程明確約定。(防止對(duì)原Web項(xiàng)目文件進(jìn)行覆蓋)
2、通過程序采用流的方式讀取Jar中的資源流再輸出到頁(yè)面流。
? ? 好處:
- 不依賴Web工程的部署方式。
- 不會(huì)復(fù)制文件到Web工程。
? ? 缺點(diǎn):
- 以流的方式實(shí)時(shí)從Jar中讀取。(速度與性能上講并非最優(yōu))
- 頁(yè)面流輸出時(shí)需要指定內(nèi)容類型Content-Type。(前者會(huì)由Web服務(wù)器來維護(hù))
三、分析結(jié)果
最終我準(zhǔn)備將1、2兩種情況接合使用,默認(rèn)會(huì)采用1復(fù)制文件到Web工程的方式。如果發(fā)現(xiàn)Web工程無法復(fù)制文件則采用2流讀取方式。
四、核心代碼開發(fā)(Jar端)
為了進(jìn)行兩種方式的切換定義一個(gè)Filter非常適合,可以攔截請(qǐng)求干擾行為,又可以在init初始化時(shí)進(jìn)行資源文件的復(fù)制。
從下面的目錄結(jié)構(gòu)可以看出主程序就是一個(gè)Filter類,org.noahx.jarresource.resource包下的內(nèi)容就是我需要的資源目錄。Filter會(huì)自動(dòng)判斷Web工程的部署方式(目錄與War)來決定復(fù)制資源目錄還是直接流讀取。
1、org.noahx.jarresource.TagLibResourceFilter(程序內(nèi)邏輯詳見注釋)
?| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 | 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<String, String> MINE_TYPE_MAP;????static {????????MINE_TYPE_MAP = new HashMap<String, String>();????????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工程是目錄方式運(yùn)行????????????String dirPath = filterConfig.getServletContext().getRealPath(resPath);????????????File dir = null;????????????try {????????????????dir = new File(dirPath);????????????????FileUtils.deleteQuietly(dir); //清除老資源????????????????FileUtils.forceMkdir(dir);?? //重新創(chuàng)建資源目錄????????????????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); //復(fù)制classpath中的資源到目錄????????????} catch (Exception e) {????????????????logger.error(e.getMessage(), e);????????????}????????????resourceMode = ResourceMode.Dir;?? //設(shè)置為目錄模式????????} else {????????????resourceMode = ResourceMode.Jar;??? //設(shè)置為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說明已經(jīng)讀到j(luò)ar中內(nèi)容????????????????????????????????String ext = FilenameUtils.getExtension(path).toLowerCase();????????????????????????????????String contentType = MINE_TYPE_MAP.get(ext);????????????????????????????????if (contentType == null) {????????????????????????????????????contentType = DEFAULT_MINE_TYPE;????????????????????????????????}????????????????????????????????rep.setContentType(contentType);??? //設(shè)置內(nèi)容類型????????????????????????????????ServletOutputStream outputStream = rep.getOutputStream();????????????????????????????????try {????????????????????????????????????int size = IOUtils.copy(inputStream, outputStream);? //向輸出流輸出內(nèi)容????????????????????????????????????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中內(nèi)容為目錄則不返回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<JarEntry> entries = jarFile.entries();????????while (entries.hasMoreElements()) {??? //遍歷jar內(nèi)容逐個(gè)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()+"'");????????????????????}????????????????}????????????}????????}????}} |
補(bǔ)充:Filter中提供了靜態(tài)方法getResourcePath()來獲得當(dāng)前的資源路徑,我的TagLib中就可以通過該方法獲得資源URI。
2、pom.xml
?| 1234567891011121314151617181920212223242526272829303132333435363738394041 | <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"?????????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">????<modelVersion>4.0.0</modelVersion>????<groupId>org.noahx.jarresource</groupId>????<artifactId>resource</artifactId>????<version>1.0-SNAPSHOT</version>????<packaging>jar</packaging>????<dependencies>????????<dependency>????????????<groupId>commons-io</groupId>????????????<artifactId>commons-io</artifactId>????????????<version>2.4</version>????????</dependency>????????<dependency>????????????<groupId>org.slf4j</groupId>????????????<artifactId>slf4j-api</artifactId>????????????<version>1.7.5</version>????????</dependency>????????<dependency>????????????<groupId>commons-lang</groupId>????????????<artifactId>commons-lang</artifactId>????????????<version>2.6</version>????????</dependency>????????<dependency>????????????<groupId>javax.servlet</groupId>????????????<artifactId>servlet-api</artifactId>????????????<version>2.5</version>????????????<scope>provided</scope>????????</dependency>????</dependencies></project> |
使用了commons-io與commons-lang第三方類包
五、核心代碼開發(fā)(Web端)
作為Jar文件的使用端,只需要在web.xml中配置一個(gè)filter,就可以訪問到Jar中的資源。
1、web.xml
?| 12345678910111213141516171819202122232425 | <web-app xmlns="http://java.sun.com/xml/ns/javaee"?????????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">????<display-name>jar resource web</display-name>????<filter>????????<filter-name>tagLibResourceFilter</filter-name>????????<filter-class>org.noahx.jarresource.TagLibResourceFilter</filter-class>????????<init-param>????????????<param-name>resourcePath</param-name>????????????<param-value>/tagres</param-value>????????</init-param>????</filter>????<filter-mapping>????????<filter-name>tagLibResourceFilter</filter-name>????????<url-pattern>/tagres/*</url-pattern>????</filter-mapping>????<welcome-file-list>????????<welcome-file>index.jsp</welcome-file>????</welcome-file-list></web-app> |
2、index.jsp(資源使用樣例,JS、CSS與圖片)
?| 12345678910111213 | <%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>????<title></title>????<link rel="stylesheet" type="text/css" href="tagres/css.css" />????<script? type="text/javascript" src="tagres/example.js"></script></head><body>?????<img src="tagres/imgs/star-hover4.png" />star-hover4.png<br/>?????<button onclick="example();" >example.js (example)</button><br/>?????<div class="redbox">css.css redbox</div></body></html> |
tagres/中的內(nèi)容就是Jar工程中所提供的資源。(下圖為顯示效果)
3、pom.xml
?| 1234567891011121314151617181920212223242526272829 | <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"?????????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">????<modelVersion>4.0.0</modelVersion>????<groupId>org.noahx.jarresource</groupId>????<artifactId>web</artifactId>????<version>1.0-SNAPSHOT</version>????<packaging>war</packaging>????<dependencies>????????<dependency>????????????<groupId>org.noahx.jarresource</groupId>????????????<artifactId>resource</artifactId>????????????<version>1.0-SNAPSHOT</version>????????</dependency>????????<dependency>????????????<groupId>org.slf4j</groupId>????????????<artifactId>slf4j-log4j12</artifactId>????????????<version>1.7.5</version>????????????<scope>runtime</scope>????????</dependency>????</dependencies></project> |
六、Web工程兩種模式的Filter日志
1、目錄部署方式
?| 1234567 | [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包部署方式
?| 1 | [JAR-RES] 2013-06-26 13:12:25,287 DEBUG [org.noahx.jarresource.TagLibResourceFilter] - ResourceMode:Jar (TagLibResourceFilter.java:111) |
七、總結(jié)
這個(gè)Filter很好的解決了我在開發(fā)TagLib時(shí)遇到的資源引用問題,對(duì)我來說應(yīng)該夠用了。
我們項(xiàng)目中一般采用目錄方式部署,我也更希望通過Web服務(wù)器來直接訪問資源。
并沒有采用maven來組織資源,因?yàn)槲倚枰峁┙o非maven工程使用。
一些流行的mvc中也有類似的手法,可能是采用流方式讀取(猜測(cè)),感興趣的朋友可以查看這些mvc的代碼。
八、源程序下載
下載包中提供了源代碼以及打包后(target目錄)的工程。
http://sdrv.ms/11Mp5gF
總結(jié)
以上是生活随笔為你收集整理的以Jar形式为Web项目提供资源文件(JS、CSS与图片)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js blob 下载到本地文件
- 下一篇: 【JEECG Docker安装】Cent