一、先說默認(rèn)的靜態(tài)資源路徑
下面截取了一段ResourceProperties類的源碼,可以看到定義了一個(gè)final數(shù)組CLASSPATH_RESOURCE_LOCATIONS并初始化了一些值,這些值就是默認(rèn)的靜態(tài)資源路徑,這些文件夾下的文件可以直接訪問。
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/].
*/
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
}
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
這幾個(gè)路徑分別對應(yīng)項(xiàng)目中的如下文件夾

二、覆蓋默認(rèn)配置 or 增加靜態(tài)資源路徑
目前我只知道兩種覆蓋默認(rèn)配置的方式
Ⅰ、使用 spring.resources.static-locations配置
其中的classpath:/my-path/是自定義的路徑,其他的是SpringBoot默認(rèn)的路徑,當(dāng)然也可以不加默認(rèn)路徑。
application.properties如下:
# 項(xiàng)目路徑
server.servlet.context-path=/test-demo
# 靜態(tài)資源配置
spring.resources.static-locations=
classpath:/META-INF/resources/,
classpath:/resources/,
classpath:/static/,
classpath:/public/,
classpath:/templates/,
classpath:/my-path/
這樣配置,除了可以直接訪問spring默認(rèn)的路徑下的靜態(tài)資源,也可以直接訪問classpath:my-path下的靜態(tài)資源。
Ⅱ、使用spring.mvc.static-path-pattern配置
aplication.properties如下:
# 項(xiàng)目路徑
server.servlet.context-path=/test-demo
# 靜態(tài)資源配置
spring.mvc.static-path-pattern=/static/**
敲黑板:
當(dāng)使用上面第Ⅱ種配置方式時(shí),只能指定一個(gè)靜態(tài)資源的路徑,且訪問時(shí)url必須含有路徑名稱如static,比如http://ip:port/test-demo/static/xxx.txt。而當(dāng)使用上面第Ⅰ種配置方式時(shí),可以配置多個(gè)靜態(tài)資源路徑,不能加路徑名稱,正常url應(yīng)該例如http://ip:port/test-demo/xxx.txt。否則都不能達(dá)到預(yù)期結(jié)果。那你可能要問第Ⅰ中方式既然不能指定要訪問那個(gè)靜態(tài)資源路徑,那么怎么去找到文件,springboot會(huì)按照配置的路徑順序依次檢索,找到了就返回。
三、自定義靜態(tài)資源映射
在實(shí)際的開發(fā)中,有可能我們會(huì)上傳文件到服務(wù)器,然后返回一個(gè)url直接訪問這個(gè)文件。但是當(dāng)文件比較多,體積也比較大時(shí),不可能將這些文件全部存放在jar服務(wù)中。這個(gè)時(shí)候,這種方式就特別有用,因?yàn)槲覀兛梢园汛疟P上的一個(gè)位置映射為靜態(tài)資源訪問路徑,通過訪問靜態(tài)資源路徑就可以直接訪問到磁盤上的資源,當(dāng)文件保存在磁盤上時(shí),就可以給jar服務(wù)減輕很大的壓力。
那么。。。怎么做?
如下創(chuàng)建一個(gè)配置類并且重寫addResourceHandlers方法。
- addResourceHandler("/customer-path/**")是添加url匹配規(guī)則,只要是以http://ip:port/test-demo/customer-path/開頭的url都會(huì)被認(rèn)為是訪問靜態(tài)資源的url。
- addResourceLocations("file:"+"D:/data/")是指定靜態(tài)資源映射的位置,file表示這是磁盤路徑,如果把file改成classpath那就是類路徑,但是我們就是不能存到j(luò)ar服務(wù)中去了,所有這里用file而不用classpath,D:/data/就是路徑了。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author chenzhiyuan
* @date 2020-01-03 18:00
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 自定義靜態(tài)資源映射
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/customer-path/**")
.addResourceLocations("file:"+"D:/data/");
}
}
如圖2,我在D:/data下放了一張圖片12345.jpg作為靜態(tài)資源。

如圖3,項(xiàng)目啟動(dòng)后訪問 http://localhost:9000/test-demo/customer-path/12345.jpg,就訪問到了靜態(tài)資源。

下面記一下項(xiàng)目中的實(shí)際使用,方便日后再次遇到使用CV大法。
需求:上傳圖片并返回url
需要考慮到的問題:映射路徑,磁盤路徑什么的都別寫死了,需要支持可配置;另外需要考慮到在windows和非window(linux,mac)下都順利啟動(dòng)應(yīng)用,所以搞了一個(gè)WebAppDataProperties類,其中WebAppData是有業(yè)務(wù)含義的,并不是指當(dāng)前應(yīng)用是個(gè)web應(yīng)用。
1.WebAppDataProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.File;
/**
* @author chenzhiyuan
* @date 2020-01-03 14:08
* 作用: 用于配置靜態(tài)資源映射的一些屬性
* accessPath: 訪問靜態(tài)資源時(shí)的路徑,如訪問ip:port/visual/webappdata/xxx/xxx,其中visual是項(xiàng)目路徑,webappdata便是accessPath
* mappingLocationWindows: 指明在windows下運(yùn)行時(shí)accessPath映射到服務(wù)器磁盤的絕對路徑,比如映射到D:/data
* mappingLocationNotWindows: 指明在Linux或Mac下運(yùn)行時(shí)accessPath映射到服務(wù)器磁盤的絕對路徑,比如映射到/usr/data
* pathPattern: 配置靜態(tài)資源映射時(shí)的參數(shù),表示路徑匹配規(guī)則,如/webappdata/**表示以ip:port/visual/webappdata/開頭的url
* 都會(huì)認(rèn)為是訪問靜態(tài)資源,然后到mappingLocation指定的路徑下去尋找資源
* resourceLocation: 配置靜態(tài)資源映射時(shí)的參數(shù),表示磁盤的絕對路徑,如file:/usr/webappdata/,必須加上file。
*/
@Data
@Component
@ConfigurationProperties(prefix = "webapp.data")
public class WebAppDataProperties {
private String accessPath;
private String pathPattern;
private String mappingLocationWindows;
private String mapperLocationNotWindows;
public String getMappingLocation() {
String os = System.getProperty("os.name");
return (os.toLowerCase().startsWith("win")) ? mappingLocationWindows : mapperLocationNotWindows;
}
public String getResourceLocation() {
String os = System.getProperty("os.name");
if (os.toLowerCase().startsWith("win")) {
File file = new File(mappingLocationWindows);
if (!file.exists()) {
file.mkdirs();
}
return "file:" + mappingLocationWindows;
} else {
File file = new File(mapperLocationNotWindows);
if (!file.exists()) {
file.mkdirs();
}
return "file:" + mapperLocationNotWindows;
}
}
}
2.具體配置內(nèi)容
webapp:
data:
access-path: /webappdata
path-pattern: ${webapp.data.access-path}/**
mapping-location-windows: D:/visual_webappdata/
mapping-location-not-windows: /usr/visual_webappdata/
3.WebMvcConfig配置
@Slf4j
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private WebAppDataProperties webAppDataProperties;
/**
* 自定義靜態(tài)資源映射
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(webAppDataProperties.getPathPattern())
.addResourceLocations(webAppDataProperties.getResourceLocation());
}
}
4.最后貼一下上傳圖片的代碼,controler如下
/**
* 為某個(gè)WebApp上傳圖片
*
* @param srcImage
* @param appId
* @return 返回圖片的url
*/
@PostMapping("/uploadImage")
public String uploadImageForWebApp(@RequestParam("file") MultipartFile srcImage,
@RequestParam(name = "appId") String appId) {
log.info("upload image for webapp start: {}", appId);
String url = webAppService.uploadImage(srcImage, appId);
log.info("upload image for webapp end: {}", url);
return url;
}
5.service實(shí)現(xiàn)
@Override
public String uploadImage(MultipartFile srcImage, String appId) {
// 圖片的存儲(chǔ)路徑
File imagePath = UploadUtils.getWebAppImagePath(webAppDataProperties.getMappingLocation(), appId);
// 獲取上傳時(shí)的文件名
String imageName = srcImage.getOriginalFilename();
// 截取后綴
String imageSuffix = imageName.substring(imageName.lastIndexOf("."));
// 使用UUID作為保存時(shí)的文件名
String newImageName = UUID.randomUUID().toString().replaceAll("-", "") + imageSuffix;
File destImage = new File(imagePath, newImageName);
try {
srcImage.transferTo(destImage);
} catch (IOException e) {
e.printStackTrace();
}
StringBuilder imageUrl = new StringBuilder().append(serverConfig.getEndpoint())
.append("/visual")
.append(webAppDataProperties.getAccessPath())
.append("/")
.append(appId)
.append("/images/")
.append(newImageName);
return imageUrl.toString();
}
6.其中g(shù)etWebAppImagePath方法如下,其實(shí)也就是構(gòu)建一個(gè)文件夾路徑,不存在就mkdirs
public static File getWebAppImagePath(String root, String appId) {
String imageDir = root + "/" + appId + "/images";
File imageDirFile = getOrCreate(imageDir);
return imageDirFile;
}
private static File getOrCreate(String path) {
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
return file;
}