问题:
今天简单的将springboot项目搭配起来后,想配置一下静态图片的访问。
springboot静态内容默认是/static (or /public or /resources or /META-INF/resources)这四个目录。
如果需要添加映射而不是完全修改的话,可以添加一个WebMvcConfigurer类并重写addResourceHandlers这个方法,如下:
@Configuration
public class ResourceHandler implements WebMvcConfigurer {
@Value("${upload}")
private String uploadPattern;
@Value("${upload.location}")
private String uploadLocation;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(uploadPattern).addResourceLocations("file:" + uploadLocation);
}
}
upload.location=D:/uploadFile
upload=/upload/**
配置如上,但是问题来了,通过localhost:8080/upload/xx.png访问后得到的是404的错误页。
解决方案如下:
将upload.location=D:/uploadFile修改为upload.location=D:/uploadFile/
分析
每个请求都会经过DispatcherServlet的doService方法,继而调用doDispatch。
于是断点进入doDispatch:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
继续下一步发现是调用了HttpRequestHandlerAdapter的handle方法,这个方法又调用了ResourceHttpRequestHandler.handleRequest方法。
看到这里大概明白了,先获取资源,获取不到就404,于是继续跟踪下去。查看getResource方法:
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// For very general mappings (e.g. "/") we need to check 404 first
Resource resource = getResource(request);
//...
@Nullable
protected Resource getResource(HttpServletRequest request) throws IOException {
//...
Resource resource = this.resolverChain.resolveResource(request, path, getLocations());
//...
}
在这里资源为null,继续跟踪。
善于运用这个可以反复进来方法,不用每次都刷新页面
一路向下debug,到了PathResourceResolver.getResource方法,这里返回的resource为空
@Nullable
private Resource getResource(String resourcePath, @Nullable HttpServletRequest request,
List<? extends Resource> locations) {
//...
Resource resource = getResource(pathToUse, location);
//...
进入getResource方法,发现调用是location.createRelative(resourcePath);
实际是FileUrlResource.createRelative
public Resource createRelative(String relativePath) throws MalformedURLException {
return new FileUrlResource(this.createRelativeURL(relativePath));
}
在这里createRelativeURL这个方法返回的数据不正常:
命名映射的是D:/uploadFile/xxx.jpg,怎么就变成了D:/xx.jpg呢,怪不得获取不到资源
进入方法查看:
protected URL createRelativeURL(String relativePath) throws MalformedURLException {
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}
relativePath = StringUtils.replace(relativePath, "#", "%23");
return new URL(this.url, relativePath);
}
URL构造函数:
public URL(URL context, String spec, URLStreamHandler handler)
throws MalformedURLException
{
//...
handler.parseURL
}
实际调用URLStreamHandler.parseURL
if (start < limit) {
if (spec.charAt(start) == '/') {
path = spec.substring(start, limit);
} else if (path != null && path.length() > 0) {
isRelPath = true;
int ind = path.lastIndexOf('/');
String seperator = "";
if (ind == -1 && authority != null)
seperator = "/";
path = path.substring(0, ind + 1) + seperator +
spec.substring(start, limit);
} else {
String seperator = (authority != null) ? "/" : "";
path = seperator + spec.substring(start, limit);
}
}
看到这里才明白,int ind = path.lastIndexOf(’/’);这里把我的D:/uploadFile截取为D:/了,怪不得最后返回的是D:/xx.png,这路径都被改了,还怎么能找到资源。至此,发现了问题,于是把upload.location=D:/uploadFile修改为upload.location=D:/uploadFile/即可。