Jetty在索引2 处发现非法字符 <:>

前端开发 2026-07-09

我在用最新版的Jetty(12.1.9)提供静态资源时遇到了困难。 当我使用 "file:///C:/some/path/to/html/" 时,就会得到
java.nio.file.InvalidPathException: Illegal char <:> at index 2: /C:/some/path/to/html/index.html
但当我使用 "file://C:/some/path/to/html/" 时,我会得到
Resource is not a readable directory
以及一个警告
[main] WARN org.eclipse.jetty.server.handler.ResourceHandler - Base Resource should be a directory: file://C:/some/path/to/html/

我已经再次确认该目录和index.html确实存在。
我的完整代码如下:

public static void main(String[] args) throws Exception {
        int port = 80;
        var server = new Server();
        HttpConfiguration httpConf = new HttpConfiguration();
        HttpConnectionFactory httpFac = new HttpConnectionFactory(httpConf);
        ServerConnector connector = new ServerConnector(server, httpFac);
        connector.setPort(port);
        server.addConnector(connector);
        ContextHandlerCollection contextCollection = new ContextHandlerCollection();

        ResourceHandler handler = new ResourceHandler();
        URI baseDir = URI.create("file://C:/some/path/to/html/");
        Resource baseResource = ResourceFactory.of(handler).newResource(baseDir);
        if (!Resources.isReadableDirectory(baseResource)) {
            throw new RuntimeException("Resource is not a readable directory");
        }

        handler.setBaseResource(baseResource);
        handler.setDirAllowed(true);
        handler.setWelcomeFiles(List.of("index.html"));
        contextCollection.addHandler(new ContextHandler(handler, "/file"));

        server.setHandler(contextCollection);

        server.start();
        server.join();

}

解决方案

我对 Resource 类的源代码做了一番研究。Jetty的实现实在是漏洞百出,简直难以置信。我写了自己的实现,现在已经可以正常工作了。

public static class ResourcePath extends Resource {
    final Path p;

    public ResourcePath(Path p) {
        this.p = p;
    }

    @Override
    public Path getPath() {
        return p;
    }

    @Override
    public long length() {
        return p.toFile().length();
    }

    @Override
    public Instant lastModified() {
        try {
            return Files.getLastModifiedTime(p).toInstant();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<Resource> list() {
        try {
            return Files.list(p).map(ResourcePath::new).collect(Collectors.toList());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean isDirectory() {
        return Files.isDirectory(p);
    }

    @Override
    public boolean isReadable() {
        return Files.isReadable(p);
    }

    @Override
    public URI getURI() {
        return p.toUri();
    }

    @Override
    public String getName() {
        return p.getFileName().toString();
    }

    @Override
    public String getFileName() {
        return p.getFileName().toString();
    }

    @Override
    public Resource resolve(String s) {
        if(s.startsWith("/")){
            s = s.substring(1);
        }
        return new ResourcePath(p.resolve(s));
    }
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章