使用jersey mvc和jsp,所有对html或js文件的请求都是通过@Template或Viewable完成的。
例;

  @GET
  @Path(JS_URL + "{type}")
  @Template(name = "grid")
  @Produces("application/javascript")
  public Response buildJSGrid(@DefaultValue("") @PathParam("type") String type) {
     Grid grid = new Grid(type);
....
     return Response.ok(grid).build();
  }


其中grid是带有纯javascript的grid.jsp文件

<%@ page contentType="application/javascript;charset=UTF-8" language="java" %>


.....

例如html和js的其他可能变体;

@GET
  @Path(FORM_URL + "{type}")
  @Template(name = "form")
  @Produces(MediaType.TEXT_HTML)
  public Response buildAccountForm(@DefaultValue("") @PathParam("type") String type) {
     Form form = new Form(type);
....
     return Response.ok(form).build();
  }


其中form是form.jsp,其中<script> .. </script>中带有html和js

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


...

我需要将结果js和html / js最小化,然后再发送给客户端,我尝试使用https://code.google.com/archive/p/htmlcompressor/ lib,但是需要将String传递给htmlCompressor.compress(input);

尝试使用WriterInterceptor

public class MinifyJsInterceptor implements WriterInterceptor {
  @Override
  public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
final OutputStream outputStream = context.getOutputStream();
// here need to convert outputStream to InputStream and after to String ?
// result string to htmlCompressor.compress(resultString);
// after that convert result minify string back to resultOutputStream and set to context ?
context.setOutputStream(new GZIPOutputStream(resultOutputStream));


这是正确的方法吗?我不能将输出流转换为字符串
谢谢

-更新

回答问题;
html + js意味着在某些jsp中是html标记和js代码



    <div id="form" style="width: 500px; display: none">
      <div class="w2ui-page page-0">
        <div class="w2ui-field">
    </div>....

    <script type="text/javascript">
      var uiElement = (function () {
        var config = {
            onOpen: function (event) {
      event.onComplete = function () {
        $('#formContainer').w2render('form');
      }
     ...
    }());
    </script>


在客户端上请求的文件

         $('#tempContainer').load('that file name - also dynamic', function (data, status, xhr) {
           uiElement.init();
           w2ui[layout].content(layout_main, w2ui[uiElement.name]);
         });


您是否真的在资源方法中返回了js文件?

        some js and html + js files are dynamic build, example;
        grid.jsp contains inside

<%@ page contentType="application/javascript;charset=UTF-8" language="java" %>

          var uiElement = (function () {
            var config = {
              grid: {
                name: ${it.name},
                listUrl:'${it.entityListUrl}',
                formUrl:'${it.entityFormUrl}',
                columns: ${it.columns},
                records: ${it.records},
}}


资源方法中有来自el表达式和设置的$ {it ..}值

@GET
      @Path(JS_URL + "{type}")
      @Template(name = "grid")
      @Produces("application/javascript")
      public Response buildJSGrid(@DefaultValue("") @PathParam("type") String type) {
         Grid grid = new Grid(type);
    ....
         return Response.ok(grid).build();
      }}


并从客户端调用js“文件”

         $.getScript('dynamic js file name' - it is dynamic too).done(function (script, status, xhr) {
           //console.log(xhr.responseText);
           uiElement.init();
           w2ui[layout].content(layout_main, w2ui[uiElement.name]);
         });



       also some html blocks build dynamic


{
     <c:if test="${it.recid != 0}">
          <div class="w2ui-field">
            <label>active:</label>
            <div>
              <input name="active" type="checkbox"/>
            </div>
          </div>
            </c:if>
}


-更新说明,
 网格生成器;

一种资源和一个模板来构建任何网格,

  @GET
  @Path(GRID + "{type}")
  @Template(name = W2UI_VIEW_PREFIX + "grid/grid")
  @Produces(MEDIA_TYPE_APPLICATION_JAVASCRIPT)
  public Response buildGrid(@DefaultValue("") @PathParam("type") String type) {
    for (W2UI ui : W2UI.values()) {
      if (type.equals(ui.getName())) {
        W2UIElement grid = ui.getUI();
        return Response.ok(grid).build();
      }
    }
    return Response.noContent().build();
  }


也可以通过Viewable(模板,模型)使用不同的模板(jsp文件)

menu.jsp模板的菜单构建器中的某处

List<MenuItem> items..
MenuItem item1 = new MenuItem(W2UI.TASK_GRID, W2UIService.GRID);
items.add(item1);


哪里

W2UIService.GRID is string url for client js request and for server method resource @Path() anno.




public enum W2UI {
  TASK_GRID("task_grid", "tasks", Type.SCRIPT){
    @Override
    public W2UIElement getUI() {
      return new TaskGrid(getName());
    }
  },
.....
}


TaskGrid使用js代码填充了grid.jsp模板的模型,因此可以轻松添加具有不同数据集和按钮集的任何类型的网格。

$ .getScript()在客户端上处理的组件(Type.SCRIPT)的类型,$('#tempContainer')。load()在Type.HTML上进行处理

---更新工厂和供应商;

@Provider
@Priority(200)
@HtmlMinify
public class HtmlMinifyInterceptor implements WriterInterceptor {
  @Inject private HtmlCompressor compressor;


...

public class HtmlMinifierFactory implements Factory<HtmlCompressor> {
  private HtmlCompressor compressor;

  @Override
  public HtmlCompressor provide() {
    if (null == compressor) compressor = new HtmlCompressor();
    ClosureJavaScriptCompressor jsCompressor = new ClosureJavaScriptCompressor();
    jsCompressor.setCompilationLevel(CompilationLevel.SIMPLE_OPTIMIZATIONS);


..

@ApplicationPath("/")
public class MainRsConfig extends ResourceConfig {
  public MainRsConfig() {
..
    register(new AbstractBinder() {
      @Override
      protected void configure() {
        bindFactory(HtmlMinifierFactory.class).to(HtmlCompressor.class).in(Singleton.class);
      }
    });
..

最佳答案

您可以使用ByteArrayOutputStream的自定义实现作为OutputStreamWriterInterceptorContext的包装:

import com.googlecode.htmlcompressor.compressor.Compressor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class HtmlMinifyOutputStream extends ByteArrayOutputStream {

    private OutputStream origOut;
    private Compressor compressor;

    public HtmlMinifyOutputStream(OutputStream origOut, Compressor compressor) {
        this.origOut = origOut;
        this.compressor = compressor;
    }

    public void close() throws IOException {
        super.close();
        String compressedBody = compressor.compress(new String(this.buf));
        this.origOut.write(compressedBody.getBytes());
        this.origOut.close();
    }
}


HtmlMinifyOutputStream可以在WriterInterceptor实现中使用。 HtmlCompressor实例被注入:

import com.googlecode.htmlcompressor.compressor.Compressor;
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import java.io.*;

@Provider
@HtmlMinify
public class MinifyHtmlInterceptor implements WriterInterceptor {

    @Inject
    private Compressor compressor;

    @Override
    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
        final OutputStream outputStream = context.getOutputStream();
        context.setOutputStream(new HtmlMinifyOutputStream(outputStream, compressor));
        context.proceed();
    }
}


@HtmlMinifyNameBinding注释,用于在特定资源方法上激活MinifyHtmlInterceptor。 (请参见https://jersey.java.net/documentation/latest/filters-and-interceptors.html#d0e9988):

import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@NameBinding
@Retention(value = RetentionPolicy.RUNTIME)
public @interface HtmlMinify {}


HtmlCompressor每个应用程序只能创建一次,并且可以同时使用,因为:


  HtmlCompressor和XmlCompressor类被认为是线程安全的*,可以在多线程环境(https://code.google.com/archive/p/htmlcompressor/)中使用


这是一个HK2工厂(请参见:Implementing Custom Injection Provider),该工厂创建压缩程序​​实例并启用内联css和javascript压缩:

import com.googlecode.htmlcompressor.compressor.Compressor;
import com.googlecode.htmlcompressor.compressor.HtmlCompressor;
import org.glassfish.hk2.api.Factory;

public class HtmlCompressorFactory implements Factory<Compressor> {

    private HtmlCompressor compressor;

    @Override
    public Compressor provide() {
        if(compressor == null) {
            compressor = new HtmlCompressor();
        }
        compressor.setCompressJavaScript(true);
        compressor.setCompressCss(true);
        return compressor;
    }

    @Override
    public void dispose(Compressor compressor) {}
}


工厂是registered with an AbstractBinder

final ResourceConfig rc = new ResourceConfig().packages("com.example");
rc.register(new AbstractBinder() {
    @Override
    protected void configure() {
        bindFactory(HtmlCompressorFactory.class).to(Compressor.class).in(Singleton.class);
    }
});


如果启用了内联JavaScript或内联CSS压缩:


  具有默认设置的HTML压缩器不需要任何依赖关系。内联CSS压缩需要YUI compressor库。内联JavaScript压缩需要YUI compressor库(默认情况下)或Google Closure Compiler库。 (https://code.google.com/archive/p/htmlcompressor/


我使用maven,因此我将此依赖项添加到了pom.xml中:

<dependency>
    <groupId>com.yahoo.platform.yui</groupId>
    <artifactId>yuicompressor</artifactId>
    <version>2.4.8</version>
</dependency>


如果要使用Google Closure Compiler,请使用以下依赖项:

<dependency>
    <groupId>com.google.javascript</groupId>
    <artifactId>closure-compiler</artifactId>
    <version>r2388</version>
</dependency>


并激活它:

compressor.setJavaScriptCompressor(new ClosureJavaScriptCompressor());
compressor.setCompressJavaScript(true);
compressor.setCssCompressor(new YuiCssCompressor());
compressor.setCompressCss(true);
return compressor;


如果要压缩纯JavaScript或CSS文件,则不能使用htmlcompressor。该库仅支持带有内联CSS / JS的HTML文件。但是您可以实现MinifyJsInterceptorMinifyCssInterceptor类似的MinifyHtmlInterceptor类似物,它们直接使用YUI-Compressor和/或Google Closure库。

对于gzip压缩,您应该实现另一个拦截器。因此可以分别配置缩小和压缩。如果激活多个拦截器,请使用javax.annotation.Priority控制执行顺序。 (请参阅:https://jersey.java.net/documentation/latest/filters-and-interceptors.html#d0e9927

10-05 22:20