163 Star 1.2K Fork 426

StarBlues / spring-brick

 / 详情

SpringBoot 2.4.2 构建的插件 在生产模式下 无法访问 resources/static 下的静态资源文件

Backlog
Opened this issue  
2023-03-14 17:04

插件已添加配置:
resources:
static-locations: classpath:static/

输入图片说明

当主程序 以dev模式启动插件时, 可以正常通过 /static-plugin/插件id/xxx 访问插件中的静态资源:
但是在 prod 模式启动后, 通过上述路径访问提示 资源未找到

Comments (2)

david_gabriel created任务
david_gabriel changed description
Expand operation logs

我看dev模式下是好的, prod模式下就是不行, 只能临时用下面的方法解决了~
@DY
@Api("生产模式-静态资源")
@RequestMapping("/")
public class CommonResourceController {

private static final Logger logger = LoggerFactory.getLogger(CommonResourceController.class);

@GetMapping("/static/{file}")
@ApiOperation("获取静态资源")
public String resource(@PathVariable(name = "file", required = true) String file) {
	ServletRequestAttributes requestConext = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
	try {
		ServletOutputStream sos = requestConext.getResponse().getOutputStream();
		requestConext.getResponse().setCharacterEncoding(StandardCharsets.UTF_8.name());
		final String path = "static/" + file;
		//类加载器不同,配置和静态资源都被隔离
		InputStream ins = PluginContextHolder.getPluginClassLoader().getResourceAsStream(path);
        IOUtils.copy(ins, sos);
        ins.close();
	} catch (Exception e) {
		e.printStackTrace();
		logger.error("读取静态资源失败: /static/" + file);
	}
	//返回404
	requestConext.getResponse().setStatus(HttpServletResponse.SC_NOT_FOUND);
	return null;
}

}

访问效果:
输入图片说明

我也碰到了不能访问静态文件的问题,我的原因是加了自定义的server.servlet.context-path,但是这个框架在PluginStaticResourceResolver中没处理自定义context-path的情况,导致pluginId获取错误。
我的解决方式是使用我自定义PluginStaticResourceResolver,处理下这个情况就行。
改动如下:

  1. 添加配置文件
@Configuration
public class PluginConfig {
   @Bean
   public PluginStaticResourceWebMvcConfigurer pluginWebResourceResolver(PluginStaticResourceConfig resourceConfig) {
      return new PluginStaticResourceWebMvcConfigurer(resourceConfig);
   }

   @Bean
   public PluginLaunchInvolved pluginLaunchInvolved() {
      return new PluginLaunchInvolved() {
         @Override
         public void after(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader, SpringPluginHook pluginHook)
            throws Exception
         {
            InsidePluginDescriptor descriptor = pluginInsideInfo.getPluginDescriptor();
            // 使用自定义的PluginStaticResourceResolver的静态方法
            PluginStaticResourceResolver.parse(descriptor, classLoader, pluginHook.getWebConfig());
         }

         @Override
         public void close(PluginInsideInfo pluginInsideInfo, ClassLoader classLoader) throws Exception {
            InsidePluginDescriptor descriptor = pluginInsideInfo.getPluginDescriptor();
            String pluginId = descriptor.getPluginId();
             // 使用自定义的PluginStaticResourceResolver的静态方法
            PluginStaticResourceResolver.remove(pluginId);
         }
      };
   }

   public static class PluginStaticResourceWebMvcConfigurer
      extends com.gitee.starblues.spring.web.PluginStaticResourceWebMvcConfigurer
   {
      private final PluginStaticResourceConfig resourceConfig;

      public PluginStaticResourceWebMvcConfigurer(PluginStaticResourceConfig resourceConfig) {
         super(resourceConfig);

         this.resourceConfig = resourceConfig;
      }

      @Override
      public void addResourceHandlers(ResourceHandlerRegistry registry) {
         String pathPattern = "/" + resourceConfig.getPathPrefix() + "/**";
         ResourceHandlerRegistration resourceHandlerRegistration = registry.addResourceHandler(pathPattern);
         CacheControl cacheControl = resourceConfig.getCacheControl();

         if(cacheControl != null) {
            resourceHandlerRegistration.setCacheControl(cacheControl);
         }
         else {
            resourceHandlerRegistration.setCacheControl(CacheControl.noStore());
         }
         
         // 此处的PluginStaticResourceResolver使用的是自定义的
         resourceHandlerRegistration
            .resourceChain(false)
            .addResolver(new PluginStaticResourceResolver(resourceConfig)); 
      }
   }
}
  1. 自定义PluginStaticResourceResolver
import com.gitee.starblues.core.descriptor.PluginDescriptor;
import com.gitee.starblues.spring.WebConfig;
import com.gitee.starblues.spring.web.PluginResource;
import com.gitee.starblues.spring.web.PluginStaticResourceConfig;
import com.gitee.starblues.utils.MsgUtils;
import com.gitee.starblues.utils.ObjectUtils;
import com.gitee.starblues.utils.UrlUtils;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.resource.AbstractResourceResolver;
import org.springframework.web.servlet.resource.ResourceResolverChain;

public class PluginStaticResourceResolver extends AbstractResourceResolver {

   private final static Logger logger = LoggerFactory.getLogger(PluginStaticResourceResolver.class);

   private final static String RESOLVED_RESOURCE_CACHE_KEY_PREFIX = "resolvedPluginResource:";

   private final static Map<String, PluginStaticResource> PLUGIN_RESOURCE_MAP = new ConcurrentHashMap<>();

   private final PluginStaticResourceConfig config;

   public PluginStaticResourceResolver(PluginStaticResourceConfig config) {
      this.config = config;
   }

   @Override
   protected Resource resolveResourceInternal(HttpServletRequest request,
                                              String requestPath, List<? extends Resource> locations,
                                              ResourceResolverChain chain)
   {
      if(request != null) {
         String requestUri = request.getRequestURI();
         String formatUri = UrlUtils.format(requestUri);
         
         // 此处添加处理contextPath的逻辑
         if(!Objects.equals("/", request.getContextPath())) {
            formatUri = formatUri.replaceFirst(request.getContextPath().replaceFirst("/", ""), "");
         }

         // fix https://gitee.com/starblues/springboot-plugin-framework-parent/issues/I53T9W
         requestPath = UrlUtils.format(formatUri.replaceFirst(config.getPathPrefix(), ""));
      }
      int startOffset = requestPath.indexOf("/");
      String pluginId = null;
      String partialPath = null;
      if(startOffset == -1) {
         pluginId = requestPath;
         partialPath = config.getIndexPageName();
      }
      else {
         pluginId = requestPath.substring(0, startOffset);
         partialPath = requestPath.substring(startOffset + 1);
      }

      PluginStaticResource pluginResource = PLUGIN_RESOURCE_MAP.get(pluginId);

      if(pluginResource == null) {
         return chain.resolveResource(request, requestPath, locations);
      }

      String key = computeKey(request, requestPath);
      // 先判断缓存中是否存在。
      Resource resource = pluginResource.getCacheResource(key);
      if(resource != null) {
         return resource;
      }
      resource = findResource(pluginResource, partialPath);
      if(resource != null) {
         pluginResource.putCacheResource(key, resource);
         return resource;
      }
      else {
         // 尝试获取首页页面
         String indexPageName = config.getIndexPageName();
         if(ObjectUtils.isEmpty(indexPageName)) {
            indexPageName = PluginStaticResourceConfig.DEFAULT_INDEX_PAGE_NAME;
         }
         if(partialPath.lastIndexOf(".") > -1) {
            // 存在后缀
            return null;
         }

         // 查找第一级节点,找不到则读取根index.html
         if(partialPath.contains(UrlUtils.PATH_SEPARATOR)) {
            partialPath = partialPath.substring(0, partialPath.indexOf(UrlUtils.PATH_SEPARATOR));
         }
         // 第一级节点
         resource = findResource(pluginResource, UrlUtils.joiningUrlPath(partialPath, indexPageName));
         if(resource != null) {
            return resource;
         }
         // 根节点
         return findResource(pluginResource, UrlUtils.joiningUrlPath(UrlUtils.PATH_SEPARATOR, indexPageName));
      }
   }
    
   // 其余代码拷贝框架自带的PluginStaticResourceResolver的方法即可
   // 由于框架自带的PluginStaticResourceResolver很多方法都是私有方法,因此没采用继承,使用直接复制的方式

Sign in to comment

Status
Assignees
Milestones
Pull Requests
Successfully merging a pull request will close this issue.
Branches
Planed to start   -   Planed to end
-
Top level
Priority
参与者(2)
9766116 david gabriel 1632292240 7977473 ihearty 1604151815
Java
1
https://gitee.com/starblues/springboot-plugin-framework-parent.git
git@gitee.com:starblues/springboot-plugin-framework-parent.git
starblues
springboot-plugin-framework-parent
spring-brick

Search