【问题标题】:How to transform files with embedded Jetty before serving them?如何在提供文件之前使用嵌入式 Jetty 转换文件?
【发布时间】:2023-03-17 16:59:01
【问题描述】:

这是我用来从带有嵌入式 Jetty 的文件系统中提供静态文件的代码。

    String directory = "dir";
    final ContextHandler contextHandler = new ContextHandler();
    final ResourceHandler resourceHandler = new ResourceHandler();
    contextHandler.setContextPath("/" + directory);
    resourceHandler.setBaseResource(Resource.newResource(new File(directory,
                directory);
    contextHandler.setHandler(resourceHandler);

如何修改它以在提供文件之前对其进行转换?

我想继续使用 ResourceHandler 和 ContextHandler 以避免在这些类中重新实现有用的逻辑。

谢谢!

【问题讨论】:

    标签: java jetty embedded-jetty


    【解决方案1】:

    ResourceHandler 不支持在流中(即时)修改内容。

    ResourceHandler 进行了优化,以尽可能高效的方式发送文件,通常使用内存映射文件缓冲区,将文件直接从磁盘提供给网络,然后在 Java 内存中不对文件进行缓冲区处理。

    你有两个选择:

    1. 在单独的步骤或过程中修改磁盘上的文件(不是即时)
    2. 编写您自己的文件服务处理程序,可以即时修改文件。

    您可以使用 Jetty git 存储库中的 FastFileServer example 作为选择 #2 的良好起点。

    【讨论】:

    • 感谢您的回答。但是,我找到了一种允许重用 ResourceHandler 和任何类型的资源的方法。我将其发布为答案。
    • 我希望使用 Resource 基础结构类的一个原因是能够使用任何不同种类的资源,尤其是 JarResource。
    • 要知道 Resource 并不是一个可覆盖的类结构,它是一种内部接口,并且可能会发生变化。我们甚至在 今天 就 URLResource 和 JarResource 进行了一次讨论,因为其中存在所有旧的 Java 1.3 hack。甚至 Jetty 9.3 也在用新的 PathResource 替换 FileResource。
    • 谢谢。 Resource 的一大优点是,无论应用程序是部署在 jar 中还是在开发期间从文件系统运行,应用程序都可以以相同的方式与其资源进行交互。
    【解决方案2】:

    我找到了解决方案。 它是用这样的类包装你的代码传递给 ResourceHandler.setBaseResource() 的资源:

    public class TransformingResource extends Resource {
        private Resource resource;
    
        protected TransformingResource(Resource resource) {
            this.resource = resource;   
        }
    
        @Override
        public boolean isContainedIn(Resource r) throws MalformedURLException {
            return resource.isContainedIn(r);
        }
    
        @Override
        public void close() {
            resource.close();
        }
    
        @Override
        public boolean exists() {
            return resource.exists();
        }
    
        @Override
        public boolean isDirectory() {
            return resource.isDirectory();
        }
    
        @Override
        public long lastModified() {
            return resource.lastModified();
        }
    
        @Override
        public long length() {
            transform();
    
            return transformed.length();
        }
    
        @Override
        public URL getURL() {
            return resource.getURL();
        }
    
        @Override
        public File getFile() throws IOException {
            return resource.getFile();
        }
    
        @Override
        public String getName() {
            return resource.getName();
        }
    
        private String transformed;
    
        private void transform() {
            if(transformed != null) {
                return;
            }
    
            transformed = transformInputStream(resource.getInputStream());
        }
    
        @Override
        public InputStream getInputStream() throws IOException {
            transform();
    
            return new ByteArrayInputStream(transformed.getBytes(Charset.forName("UTF-8")));
        }
    
        @Override
        public ReadableByteChannel getReadableByteChannel() throws IOException {
            return null;
        }
    
        @Override
        public boolean delete() throws SecurityException {
            return resource.delete();
        }
    
        @Override
        public boolean renameTo(Resource dest) throws SecurityException {
            return resource.renameTo(dest);
        }
    
        @Override
        public String[] list() {
            return resource.list();
        }
    
        @Override
        public Resource addPath(String path) throws IOException,
                MalformedURLException {
            Resource toReturn = resource.addPath(path);
            if(toReturn == null) {
                return null;
            }
    
            return new TransformingResource(toReturn);
        }
    }
    

    【讨论】:

    • 如果您不想破坏 HTTP 响应缓存和 HTTP 请求安全性,您还需要为 .getReadableByteChannel().getAlias().lastModified().getWeakETag() 提供覆盖. (确保您提供了一个可感知转换的弱 ETag,就像在 Gzip 过滤器中所做的那样)
    • 不要将这些覆盖委托给底层 Resource(它们现在根据您的 TransformingResource 实现无效) - Jetty 没有将这种技术用于 AsyncMiddleManServletMiddleManServlet 它附带的基于变压器。
    • @JoakimErdfelt 感谢您的反馈。如果转换是确定性的,那么我认为该类不需要覆盖.getAlias().lastModified(),因为该数据与基础资源相同。我认为该类不需要覆盖.getWeakETag(),因为Resource.getWeakETag() 调用了TransformingResource 的成员方法,这些方法是正确的。我还认为该类不需要.getReadableByteChannel() 的不同实现,因为许多Resources 只是返回null(例如URLResource 类层次结构。)如果您有,请告诉我
    • URLResource 将来可能会消失,因为它对 Servlet 3.x 非常不利,并且 jar 包含 META-INF/resource/ 条目。
    • @JoakimErdfelt 感谢您提供这些信息。你知道当码头应用程序使用 maven shade 之类的工具打包时,将使用什么方法来提供嵌入在 JAR 中的文件?
    猜你喜欢
    • 2012-05-04
    • 1970-01-01
    • 2011-11-01
    • 2013-12-11
    • 1970-01-01
    • 2014-03-08
    • 2012-09-02
    • 2015-03-18
    • 1970-01-01
    相关资源
    最近更新 更多