使用SpringBoot时关于静态资源的访问问题

一、先说默认的静态资源路径

下面截取了一段ResourceProperties类的源码,可以看到定义了一个final数组CLASSPATH_RESOURCE_LOCATIONS并初始化了一些值,这些值就是默认的静态资源路径,这些文件夹下的文件可以直接访问。

@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/

这几个路径分别对应项目中的如下文件夹


图1

二、覆盖默认配置 or 增加静态资源路径

目前我只知道两种覆盖默认配置的方式

Ⅰ、使用 spring.resources.static-locations配置

其中的classpath:/my-path/是自定义的路径,其他的是SpringBoot默认的路径,当然也可以不加默认路径。
application.properties如下:

# 项目路径
server.servlet.context-path=/test-demo
# 静态资源配置
spring.resources.static-locations=
  classpath:/META-INF/resources/,
  classpath:/resources/,
  classpath:/static/,
  classpath:/public/,
  classpath:/templates/,
  classpath:/my-path/

这样配置,除了可以直接访问spring默认的路径下的静态资源,也可以直接访问classpath:my-path下的静态资源。

Ⅱ、使用spring.mvc.static-path-pattern配置

aplication.properties如下:

# 项目路径
server.servlet.context-path=/test-demo
# 静态资源配置
spring.mvc.static-path-pattern=/static/**

敲黑板
当使用上面第Ⅱ种配置方式时,只能指定一个静态资源的路径,且访问时url必须含有路径名称如static,比如http://ip:port/test-demo/static/xxx.txt。而当使用上面第Ⅰ种配置方式时,可以配置多个静态资源路径,不能加路径名称,正常url应该例如http://ip:port/test-demo/xxx.txt。否则都不能达到预期结果。那你可能要问第Ⅰ中方式既然不能指定要访问那个静态资源路径,那么怎么去找到文件,springboot会按照配置的路径顺序依次检索,找到了就返回。

三、自定义静态资源映射

在实际的开发中,有可能我们会上传文件到服务器,然后返回一个url直接访问这个文件。但是当文件比较多,体积也比较大时,不可能将这些文件全部存放在jar服务中。这个时候,这种方式就特别有用,因为我们可以把磁盘上的一个位置映射为静态资源访问路径,通过访问静态资源路径就可以直接访问到磁盘上的资源,当文件保存在磁盘上时,就可以给jar服务减轻很大的压力。
那么。。。怎么做?
如下创建一个配置类并且重写addResourceHandlers方法。

  • addResourceHandler("/customer-path/**")是添加url匹配规则,只要是以http://ip:port/test-demo/customer-path/开头的url都会被认为是访问静态资源的url。
  • addResourceLocations("file:"+"D:/data/")是指定静态资源映射的位置,file表示这是磁盘路径,如果把file改成classpath那就是类路径,但是我们就是不能存到jar服务中去了,所有这里用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 {
   /**
    * 自定义静态资源映射
    */
   @Override
   public void addResourceHandlers(ResourceHandlerRegistry registry) {
       registry.addResourceHandler("/customer-path/**")
               .addResourceLocations("file:"+"D:/data/");
   }
}

如图2,我在D:/data下放了一张图片12345.jpg作为静态资源。

图2

如图3,项目启动后访问 http://localhost:9000/test-demo/customer-path/12345.jpg,就访问到了静态资源。
图3



下面记一下项目中的实际使用,方便日后再次遇到使用CV大法。
需求:上传图片并返回url
需要考虑到的问题:映射路径,磁盘路径什么的都别写死了,需要支持可配置;另外需要考虑到在windows和非window(linux,mac)下都顺利启动应用,所以搞了一个WebAppDataProperties类,其中WebAppData是有业务含义的,并不是指当前应用是个web应用。

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
 * 作用: 用于配置静态资源映射的一些属性
 * accessPath: 访问静态资源时的路径,如访问ip:port/visual/webappdata/xxx/xxx,其中visual是项目路径,webappdata便是accessPath
 * mappingLocationWindows: 指明在windows下运行时accessPath映射到服务器磁盘的绝对路径,比如映射到D:/data
 * mappingLocationNotWindows: 指明在Linux或Mac下运行时accessPath映射到服务器磁盘的绝对路径,比如映射到/usr/data
 * pathPattern: 配置静态资源映射时的参数,表示路径匹配规则,如/webappdata/**表示以ip:port/visual/webappdata/开头的url
 * 都会认为是访问静态资源,然后到mappingLocation指定的路径下去寻找资源
 * resourceLocation: 配置静态资源映射时的参数,表示磁盘的绝对路径,如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.具体配置内容

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;

    /**
     * 自定义静态资源映射
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(webAppDataProperties.getPathPattern())
                .addResourceLocations(webAppDataProperties.getResourceLocation());
    }
}

4.最后贴一下上传图片的代码,controler如下

   /**
     * 为某个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实现

    @Override
    public String uploadImage(MultipartFile srcImage, String appId) {
        // 图片的存储路径
        File imagePath = UploadUtils.getWebAppImagePath(webAppDataProperties.getMappingLocation(), appId);
        // 获取上传时的文件名
        String imageName = srcImage.getOriginalFilename();
        // 截取后缀
        String imageSuffix = imageName.substring(imageName.lastIndexOf("."));
        // 使用UUID作为保存时的文件名
        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.其中getWebAppImagePath方法如下,其实也就是构建一个文件夹路径,不存在就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;
    }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容