SpringBoot設(shè)置靜態(tài)資源訪問(wèn)控制和封裝集成方案
背景
最近在著手公司框架優(yōu)化及項(xiàng)目實(shí)際應(yīng)用,原先方案是springboot+html前后端分離單獨(dú)部署,后端人員兼職前端開(kāi)發(fā),后續(xù)產(chǎn)品線業(yè)務(wù)進(jìn)行優(yōu)化,面向企業(yè)使用部分由移動(dòng)網(wǎng)站人員負(fù)責(zé)設(shè)計(jì)開(kāi)發(fā),內(nèi)部配置后臺(tái)管理還是由后端負(fù)責(zé),隨著框架不停迭代與使用的項(xiàng)目越來(lái)越多,項(xiàng)目升級(jí)框架變得十分麻煩,后端部分可以通過(guò)maven私服進(jìn)行版本迭代,后臺(tái)管理頁(yè)面升級(jí)則需要進(jìn)行各個(gè)項(xiàng)目拷貝,所以決定對(duì)框架進(jìn)行整合,將后臺(tái)管理頁(yè)面與框架后端代碼進(jìn)行整合發(fā)布。
結(jié)構(gòu)設(shè)計(jì)
- 框架打包后臺(tái)管理相關(guān)標(biāo)準(zhǔn)資源及頁(yè)面(框架public文件夾)
- 項(xiàng)目使用框架,開(kāi)發(fā)具體業(yè)務(wù)配置管理頁(yè)面(項(xiàng)目static文件夾)
- 項(xiàng)目需要個(gè)性化框架頁(yè)面時(shí),在項(xiàng)目static文件夾建立與框架同目錄同名稱資源文件進(jìn)行覆蓋,訪問(wèn)時(shí)優(yōu)先級(jí)高于框架目錄

SpringBoot靜態(tài)資源訪問(wèn)
自定義訪問(wèn)路徑
自定義WebConfig實(shí)現(xiàn)WebMvcConfigurer,重寫addResourceHandlers方法
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Value("${system.projectName}")
private String projectName;
/**
* 添加靜態(tài)資源文件,外部可以直接訪問(wèn)地址
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//第一個(gè)方法設(shè)置訪問(wèn)路徑前綴,第二個(gè)方法設(shè)置資源路徑
registry.addResourceHandler("/" + projectName + "/**").addResourceLocations("classpath:/static/","classpath:/public/","file:static/");
}
}
圖標(biāo)與字體文件夾訪問(wèn)失敗問(wèn)題
將靜態(tài)文件拷貝到static/public/resource文件夾下訪問(wèn)時(shí),圖標(biāo)與字體文件會(huì)進(jìn)行過(guò)濾導(dǎo)致?lián)p壞,需要在pom文件中進(jìn)行設(shè)置
<build>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.woff</exclude>
<exclude>**/*.ttf</exclude>
<exclude>**/*.ico</exclude>
</excludes>
</resource>
<resource>
<filtering>false</filtering>
<directory>src/main/resources</directory>
<includes>
<include>**/*.woff</include>
<include>**/*.ttf</include>
<include>**/*.ico</include>
</includes>
</resource>
</resources>
</build>
自定義歡迎頁(yè)面
在對(duì)靜態(tài)內(nèi)目錄設(shè)置自定義訪問(wèn)路徑替換原有的/**后,無(wú)法找到目錄下的index頁(yè)面,需要建立攔截器手動(dòng)進(jìn)行判斷,效果為訪問(wèn)http://localhost:port/projectName 會(huì)自動(dòng)跳轉(zhuǎn)到 http://localhost:port/projectName/index.html
@Component
public class PageRedirectInterceptor implements HandlerInterceptor {
@Value("${system.projectName}")
private String projectName;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURL = request.getRequestURL().toString();
String scheme = request.getScheme();
String servaerName = request.getServerName();
int port = request.getServerPort();
String rootPageURL = scheme + ":" + "http://" + servaerName + ":" + port + "/" + projectName;
if (requestURL.equals(rootPageURL)) {
response.sendRedirect(request.getContextPath() + "/"+projectName + "/index.html");
return false;
}
return true;
}
}
自定義頁(yè)面圖標(biāo)
在對(duì)靜態(tài)內(nèi)目錄設(shè)置自定義訪問(wèn)路徑替換原有的/**后,無(wú)法找到目錄下的favcion.ico圖標(biāo),需要在頁(yè)面引用統(tǒng)一js統(tǒng)一設(shè)置,同時(shí)需要在配置文件中關(guān)閉默認(rèn)圖標(biāo),替換spring的小葉子
spring:
mvc:
favicon:
enabled: false
function GetRootPath() {
var loc = window.location,
host = loc.hostname,
protocol = loc.protocol,
port = loc.port ? (':' + loc.port) : '';
var path = location.pathname;
if (path.indexOf('/') === 0) {
path = path.substring(1);
}
var mypath = '/' + path.split('/')[0];
path = (mypath != undefined ? mypath : ('/' + loc.pathname.split('/')[1])) + '/';
var rootPath = protocol + '//' + host + port + path;
return rootPath;
}
var iconurl = GetRootPath()+"favicon.ico"
document.write('<link rel="shortcut icon" href= ' + iconurl + ' ></link>');
項(xiàng)目訪問(wèn)框架靜態(tài)資源
框架靜態(tài)資源文件獲取
項(xiàng)目啟動(dòng)時(shí),因?yàn)槭且每蚣艿膉ar包,我們需要先找到指定jar包,再將jar包進(jìn)行解壓,找到對(duì)應(yīng)目錄將資源拷貝到我們需要的地方便于訪問(wèn)
掃描jar包
public static void copyFrameStaticFile() {
String packageName = "com.haopan.frame";
// 獲取包的名字 并進(jìn)行替換
String packageDirName = packageName.replace('.', '/');
// 定義一個(gè)枚舉的集合 并進(jìn)行循環(huán)來(lái)處理這個(gè)目錄下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循環(huán)迭代下去
while (dirs.hasMoreElements()) {
// 獲取下一個(gè)元素
URL url = dirs.nextElement();
// 得到協(xié)議的名稱
String protocol = url.getProtocol();
if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定義一個(gè)JarFile
JarFile jar;
try {
// 獲取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
String templateDecompressPath = "tempfiles/decompress/" + CommonUtil.getNewGuid() + "/";
File targetFile = new File(templateDecompressPath);
if (!targetFile.exists()) {
targetFile.mkdirs();
}
decompressJarFile(jar, templateDecompressPath);
String frameStaticPath = templateDecompressPath + "public/";
File frameStaticFile = new File(frameStaticPath);
if (frameStaticFile.exists()) {
String copyTargetPath = "static/";
File copyTargetFolder = new File(copyTargetPath);
if (copyTargetFolder.exists()) {
FileUtil.deleteDirectory(copyTargetPath);
}
copyTargetFolder.mkdirs();
FileUtil.copyFileFolder(frameStaticPath, copyTargetPath);
}
FileUtil.deleteDirectory(templateDecompressPath);
System.out.println("框架靜態(tài)文件復(fù)制完畢!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
解壓jar包
對(duì)JarFile中的JarEntry對(duì)象進(jìn)行遍歷,判斷是文件還是目錄分類處理
public static synchronized void decompressJarFile(JarFile jf,String outputPath){
if (!outputPath.endsWith(File.separator)) {
outputPath += File.separator;
}
File dir = new File(outputPath);
if (!dir.exists()) {
dir.mkdirs();
}
try{
for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements();) {
JarEntry je = (JarEntry) e.nextElement();
String outFileName = outputPath + je.getName();
File f = new File(outFileName);
if(je.isDirectory()){
if(!f.exists()){
f.mkdirs();
}
}else{
File pf = f.getParentFile();
if(!pf.exists()){
pf.mkdirs();
}
InputStream in = jf.getInputStream(je);
OutputStream out = new BufferedOutputStream(
new FileOutputStream(f));
byte[] buffer = new byte[2048];
int nBytes = 0;
while ((nBytes = in.read(buffer)) > 0) {
out.write(buffer, 0, nBytes);
}
out.flush();
out.close();
in.close();
}
}
}catch(Exception e){
System.out.println("解壓"+jf.getName()+"出錯(cuò)---"+e.getMessage());
}finally{
if(jf!=null){
try {
jf.close();
File jar = new File(jf.getName());
if(jar.exists()){
jar.delete();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
拷貝目錄到指定位置
public class FileUtil {
private static void copy(String f1, String f2) throws IOException {
File file1=new File(f1);
/* File file2=new File(f2);*/
File[] flist=file1.listFiles();
for (File f : flist) {
if(f.isFile()){
copyFile(f.getPath(),f2+"/"+f.getName()); //調(diào)用復(fù)制文件的方法
//System.out.println("原路徑["+f.getPath()+"] 被復(fù)制路徑["+f2+"/"+f.getName()+"]");
}else if(f.isDirectory()){
copyFileFolder(f.getPath(),f2+"/"+f.getName()); //調(diào)用復(fù)制文件夾的方法
//System.out.println("原路徑["+f.getPath()+"] 被復(fù)制路徑["+f2+"/"+f.getName()+"]");
}
}
}
/**
* 復(fù)制文件夾
* @throws IOException
*/
public static void copyFileFolder(String sourceFolderPath,String targetFolderPath) throws IOException {
//創(chuàng)建文件夾
File file=new File(targetFolderPath);
if(!file.exists()){
file.mkdirs();
}
copy(sourceFolderPath,targetFolderPath);
}
/**
* 復(fù)制文件
* @throws IOException
*/
public static void copyFile(String sourceFilePath, String tagretFilePath) throws IOException {
try {
InputStream in = new FileInputStream(sourceFilePath);
OutputStream out = new FileOutputStream(tagretFilePath);
byte[] buffer = new byte[2048];
int nBytes = 0;
while ((nBytes = in.read(buffer)) > 0) {
out.write(buffer, 0, nBytes);
}
out.flush();
out.close();
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static boolean delete(String fileName) {
File file =new File(fileName);
if (!file.exists()) {
//System.out.println("刪除文件失敗:" + fileName +"不存在!");
return false;
}else {
if (file.isFile())
return deleteFile(fileName);
else
return deleteDirectory(fileName);
}
}
/**
* 刪除單個(gè)文件
*
* @param fileName:要?jiǎng)h除的文件的文件名
* @return 單個(gè)文件刪除成功返回true,否則返回false
*/
public static boolean deleteFile(String fileName) {
File file =new File(fileName);
// 如果文件路徑所對(duì)應(yīng)的文件存在,并且是一個(gè)文件,則直接刪除
if (file.exists() && file.isFile()) {
if (file.delete()) {
//System.out.println("刪除單個(gè)文件" + fileName +"成功!");
return true;
}else {
//System.out.println("刪除單個(gè)文件" + fileName +"失??!");
return false;
}
}else {
//System.out.println("刪除單個(gè)文件失敗:" + fileName +"不存在!");
return false;
}
}
/**
* 刪除目錄及目錄下的文件
*
* @param dir:要?jiǎng)h除的目錄的文件路徑
* @return 目錄刪除成功返回true,否則返回false
*/
public static boolean deleteDirectory(String dir) {
// 如果dir不以文件分隔符結(jié)尾,自動(dòng)添加文件分隔符
if (!dir.endsWith(File.separator))
dir = dir + File.separator;
File dirFile =new File(dir);
// 如果dir對(duì)應(yīng)的文件不存在,或者不是一個(gè)目錄,則退出
if ((!dirFile.exists()) || (!dirFile.isDirectory())) {
System.out.println("刪除目錄失敗:" + dir +"不存在!");
return false;
}
boolean flag =true;
// 刪除文件夾中的所有文件包括子目錄
File[] files = dirFile.listFiles();
for (int i =0; i < files.length; i++) {
// 刪除子文件
if (files[i].isFile()) {
flag = deleteFile(files[i].getAbsolutePath());
if (!flag)
break;
}
// 刪除子目錄
else if (files[i].isDirectory()) {
flag = deleteDirectory(files[i].getAbsolutePath());
if (!flag)
break;
}
}
if (!flag) {
//System.out.println("刪除目錄失?。?);
return false;
}
// 刪除當(dāng)前目錄
if (dirFile.delete()) {
//System.out.println("刪除目錄" + dir +"成功!");
return true;
}else {
return false;
}
}
}
外部靜態(tài)資源訪問(wèn)與優(yōu)先級(jí)設(shè)置
設(shè)置yml文件中的static-locations配置項(xiàng),多個(gè)使用,隔開(kāi),同時(shí)指定順序?yàn)樵L問(wèn)的優(yōu)先級(jí)
spring:
resources:
static-locations: classpath:static/,classpath:public/,file:static/
最終目錄結(jié)構(gòu)圖如下,框架部分完全是項(xiàng)目啟動(dòng)時(shí)自動(dòng)解壓拷貝的,項(xiàng)目部分則是由具體項(xiàng)目進(jìn)行開(kāi)發(fā),項(xiàng)目部分也可以很方便的進(jìn)行框架部分功能重構(gòu),例如登錄頁(yè),主頁(yè)面修改等,本方式支持jar包和war包兩種打包方式

到此這篇關(guān)于SpringBoot靜態(tài)資源訪問(wèn)控制和封裝集成方案的文章就介紹到這了,更多相關(guān)SpringBoot靜態(tài)資源訪問(wèn)控制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot應(yīng)用中靜態(tài)資源訪問(wèn)與接口請(qǐng)求沖突問(wèn)題解決
- SpringBoot深入探究四種靜態(tài)資源訪問(wèn)的方式
- SpringBoot中的yaml語(yǔ)法及靜態(tài)資源訪問(wèn)問(wèn)題
- SpringBoot中的靜態(tài)資源訪問(wèn)的實(shí)現(xiàn)
- Springboot靜態(tài)資源訪問(wèn)實(shí)現(xiàn)代碼解析
- 在SpringBoot中靜態(tài)資源訪問(wèn)方法
- SpringBoot中的static靜態(tài)資源訪問(wèn)、參數(shù)配置、代碼自定義訪問(wèn)規(guī)則詳解
相關(guān)文章
Java Thread中start()和run()的區(qū)別_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
start() : 它的作用是啟動(dòng)一個(gè)新線程,新線程會(huì)執(zhí)行相應(yīng)的run()方法。start()不能被重復(fù)調(diào)用。而run() : run()就和普通的成員方法一樣,可以被重復(fù)調(diào)用。下面通過(guò)示例代碼給大家介紹了Java Thread中start()和run()的區(qū)別,感興趣的朋友一起看看吧2017-05-05
Java項(xiàng)目中添加外部jar包的兩種方式(收藏版)
這篇文章主要介紹了java項(xiàng)目中添加外部jar包的兩種方式,第二種方式是將外部jar包引入到本地maven倉(cāng)庫(kù)中,本文給大家講解的非常詳細(xì),需要的朋友可以參考下2023-03-03
Mybatis中mapper.xml實(shí)現(xiàn)熱加載介紹
大家好,本篇文章主要講的是Mybatis中mapper.xml實(shí)現(xiàn)熱加載介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01
JAVA環(huán)境搭建之MyEclipse10+jdk1.8+tomcat8環(huán)境搭建詳解
本文詳細(xì)講解了MyEclipse10+jdk1.8+tomcat8的JAVA環(huán)境搭建方法,希望能幫助到大家2018-10-10
Spring運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)bean的方法
這篇文章主要介紹了Spring運(yùn)行時(shí)動(dòng)態(tài)注冊(cè)bean的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
SpringBoot中yml多環(huán)境配置的3種方法
這篇文章主要給大家介紹了SpringBoot中yml多環(huán)境配置的3種方法,文中有詳細(xì)的代碼示例供大家參考,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-10-10
Java實(shí)現(xiàn)百度AOI數(shù)據(jù)的解析與轉(zhuǎn)換
Java作為一種成熟且廣泛應(yīng)用的編程語(yǔ)言,具有跨平臺(tái)、面向?qū)ο?、安全性高等特點(diǎn),非常適合用于開(kāi)發(fā)各種類型的應(yīng)用程序,本文為大家整理了基于Java的AOI數(shù)據(jù)解析與轉(zhuǎn)換的實(shí)現(xiàn)方法,需要的可以參考下2025-02-02

