使用SpringBoot時(shí)關(guān)于靜態(tài)資源的訪問問題

一、先說默認(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)目中的如下文件夾


圖1

二、覆蓋默認(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)資源。

圖2

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



下面記一下項(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;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容