【问题标题】:Binary Data and GWT二进制数据和 GWT
【发布时间】:2012-06-18 09:05:45
【问题描述】:

已经有几个关于二进制数据和 GWT 的问题。阅读它们后,我仍然不确定以下是否可行(尽管我是一个完整的 GWT 初学者!):

我有一些非常复杂的数据文件,它们只以二进制形式存在,我无法将它们转换为 XML 或 JSON 之类的东西。我有一个封闭的源代码库,尽管它接受一个 byte[] 并返回一个我可以使用的 Java 对象。为了让我的 GWT 应用程序运行,我“打印出”其中一个二进制数据文件,并将生成的 byte[] 硬编码到我从 GWT 应用程序代码访问的 .java 文件中。一切正常。显然这只是一个测试,在部署的应用程序中我无法对这些数据文件进行硬编码。我想将它们放在我的 GWT 应用程序所在的目录中,然后用我的 GWT 应用程序“加载”它们。

我认为我可以使用 GWT 从我的服务器“加载”文本文件,对吧?为什么我不能用 GWT 读取二进制数据?或者我可以将二进制数据文件作为文本读取,将字符串读取为字节 []?我读了很多关于 base64 编码的内容并且 GWT 可以阅读它,尽管我并不真正理解他们在说什么。我可以将我的服务器配置为以 base64 编码的形式提供这些二进制数据文件,然后使用 GWT 读取它们吗?

或者还有其他解决方案吗?如果我能提供帮助,我不想碰任何 JS 代码。这就是我开始使用 GWT 的原因;)

感谢您的帮助:)

【问题讨论】:

    标签: gwt binary bytearray


    【解决方案1】:

    假设我们使用的是 HTML 4。

    GWT 客户端无法“读取”文件。 GWT 客户端是在浏览器上运行的 javascript。浏览器安全不允许您读取本地文件。您必须让 servlet 代理读取服务器上的文件。

    您设置文件的 mime 类型是因为您希望浏览器下载文件并调用本地 PC 来调用适当的软件 - 例如,pdf 来调用 pdf 阅读器或 xls 来调用 ms excel。与 GWT Java 或 Javascript 无关(启用下载除外)。

    为什么需要 GWT 客户端来读取二进制文件?如果你这样做了,你的架构可能是错误的。 “错”是个不客气的词。也许,错位是一个更好的词。您对 AJAX 瘦客户端-服务器的概念是错位的。一进GWT的大门,就把你的桌面处理理念和习惯丢在门口。

    GWT 是 Java 但不是 Java

    我不得不一直提醒人们,GWT Java 只是 Javascript 的一种更连贯的表示。当您使用 GWT Java 进行编码时,请始终记住您实际上是在使用 Javascript 而不是 Java 进行编码。所有 Java 源代码都被翻译成 Javascript

    因此,GWT 编译器需要在源代码中提供所有 Java 类。 GWT 编译器无法将 Java 字节码 jar/类文件翻译成 Javascript。如果您的库在字节码中,或者您的源库在调用链的任何位置调用字节码库,则编译将失败。

    服务器端和客户端 GWT 之间的混淆

    GWT RPC 有时会让 GWT 新手感到困惑。他们似乎没有意识到远程 servlet 是唯一被编译成字节码的部分,因为它是在服务器上运行的。特别是如果您使用的是 Vaadin - 因为他们故意模糊了服务器和浏览器之间的界限。所以 GWT 新手开始想,“为什么我的字节码库只在应用程序的某些部分工作?”

    ajax 客户端服务器架构

    GWT 只是一个支持 Web 的 UI。为什么你不能在服务器上做任何你想做的事情,让服务器反映它正在做什么或已经对 UI 做了什么?为什么一定要在浏览器上完成?

    想象一下您的 GWT 接口是一个增强的 JSP。假设您正在编写一个 JSP。您是否让您的 JSP 将您的二进制数据吸入浏览器并让 JSP 生成 Javascript 以分析那里的二进制数据?

    我编写了复杂的统计分析,我只是将浏览器用作服务器上正在执行的操作的反映。工程师认为他/她正在他/她的 PC 上运行分析。生成图表/报告。但这一切都是通过调用SAS在服务器上完成的。

    面向服务的模式/架构

    您的服务器将提供服务。您的浏览器 GWT 客户端将请求这些服务。 打开文件,读取文件,分析文件,生成分析的可视化/mime 表示并将其传递给浏览器。只需将 GWT 浏览器客户端视为基于服务器的操作的显示监视器。 GWT 是一个魔术师的把戏,可以帮助我变出幻觉,让工程师感觉他们正在本地 PC 上执行分析。当然,作为工程师,他们中的大多数人都知道浏览器实际上并没有在做这项工作。

    当您的用户对分析感到满意时,让您的服务生成结果的 mime 表示,以便浏览器下载它以调用 mime 映射的适当本地 PC 软件。

    在服务器上做,在浏览器上反映。

    进一步编辑:关于二进制数据...

    在 Web 应用程序中使用 base64 编码背后的动机:传输身份验证令牌、图片、音频文件 - 这样它们的二进制表示和排序就不会被字节序等架构细微差别搞砸。

    例如,不要尝试编写浏览器应用程序来读取原始二进制电子表格 - 始终让服务器将其转换为 XML 或 JSON(最好是 JSON),其中任何二进制元素都应经过 base64 编码,然后再发送到浏览器应用程序。或者,如果您的人生目标是攀登珠穆朗玛峰,请发明一种与架构无关的编码来代替 base64 来传输二进制数据。

    如果用于浏览器的操作系统处理(如音频、图片、pdf),则仅使用二进制信息。发送仅由 javascript 例程处理的二进制数据毫无意义。 javascript 例程将不得不使用额外的处理时间来翻译它(除非再一次,如果你的生活目标是攀登......)。

    【讨论】:

    • 虽然我同意您在此处陈述的几乎所有内容,但如果您在 GWT 中需要二进制数据,那么您的架构是错误的这种说法有点冒昧。如果这是真的,那么为什么 XSD 中有 hex 和 base64 二进制类型?话虽如此,在 GWT 中实际需要真正二进制数据的应用程序很少而且相差甚远,因此我同意您可能应该问自己为什么。我假设这已经完成并且用户知道并理解架构 SOA 的基础知识,这可能是一个糟糕的假设。
    【解决方案2】:

    是的,这是可能的。

    两种解决方案取决于数据类型。

    1. 动态(如果二进制数据是动态的并且可能会改变):
      只需 Base64 在后端对二进制数据进行编码并提供服务(即 GET 请求)。 然后,您可以使用 GWT 的任何通信协议(有关详细信息,请参阅here)从后端检索数据。
      然后,您必须对数据进行 base64 解码并使用它(因为您已经解决了它)。

    2. 静态(如果二进制数据不会改变并且在编译期间已知):
      您可以使用ClientBundle(即:DataResource)在编译期间生成这些二进制文件,然后可以在客户端自动检索它们,而无需手动设置传输它们。

    【讨论】:

    • 二进制数据文件是静态的。要在我的后端将它们编码为 Base64,我需要编写一个 servlet 吗?或者我可以在服务器配置中为这些文件设置一个 mime 类型,然后服务器将为我进行编码?我注意到一些像图像这样的二进制文件在响应头中作为八位字节流返回。这对我有什么帮助吗?
    • 如果它们是静态的并且如果您使用 ClientBundle,则无需执行任何操作。 GWT 编译器将为它们生成一个 ClientBundle。根据设置,它们将被内联(使用数据:在 url 中)或者将发出单独的 HTTP 请求。查看 ClientBundle 部分中的 DataResource 示例
    • 您能否回答我提出的关于 Base64 解决方案的问题?我测试了 DataResource,但将来我的二进制数据将是动态的,所以我想要一个在几个月内不会中断的解决方案。
    • 为了提供有关动态方法的更多详细信息,如果您提供有关如何进行服务器通信的详细信息(GWT RPC、JAX-RS(ReST)、Java Servlet 等... ) 以及您使用的服务器(Tomcat、Glassfish、Google App Engine 等...)
    • 我将使用 AppEngine (Python) 和 Tomcat 或 Jetty。我可以以某种方式修改 web.xml 或 app.yaml 以将我的(当前)静态二进制文件作为 base64 提供吗?当我从 GWT 应用程序执行 response.getText() 时会发生什么?一旦我调用 .getBytes() ,它会返回一个返回我想要的 byte[] 的字符串吗?
    【解决方案3】:

    客户端:

    @Override
    public void onModuleLoad()
    {
        RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, "/test");
        try
        {
            rb.sendRequest(null, new RequestCallback()
            {
                @Override
                public void onResponseReceived( Request request, Response response )
                {
                    String encoded = response.getText();
                    byte[] data = decode(encoded);
                    System.out.println(Arrays.toString(data));
                }
    
                @Override
                public void onError( Request request, Throwable exception )
                {
                }
            });
        }
        catch( RequestException e )
        {
            e.printStackTrace();
        }
    }
    
    private final static String base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
    public static byte[] decode( String s )
    {
    
        // remove/ignore any characters not in the base64 characters list
        // or the pad character -- particularly newlines
        s = s.replaceAll("[^" + base64chars + "=]", "");
    
        // replace any incoming padding with a zero pad (the 'A' character is
        // zero)
        String p = (s.charAt(s.length() - 1) == '=' ? (s.charAt(s.length() - 2) == '=' ? "AA" : "A") : "");
        s = s.substring(0, s.length() - p.length()) + p;
        int resLength = (int) Math.ceil(((s.length()) / 4f) * 3f);
        byte[] bufIn = new byte[resLength];
        int bufIn_i = 0;
    
        // increment over the length of this encrypted string, four characters
        // at a time
        for( int c = 0; c < s.length(); c += 4 )
        {
    
            // each of these four characters represents a 6-bit index in the
            // base64 characters list which, when concatenated, will give the
            // 24-bit number for the original 3 characters
            int n = (base64chars.indexOf(s.charAt(c)) << 18) + (base64chars.indexOf(s.charAt(c + 1)) << 12)
                    + (base64chars.indexOf(s.charAt(c + 2)) << 6) + base64chars.indexOf(s.charAt(c + 3));
    
            // split the 24-bit number into the original three 8-bit (ASCII)
            // characters
            char c1 = (char) ((n >>> 16) & 0xFF);
            char c2 = (char) ((n >>> 8) & 0xFF);
            char c3 = (char) (n & 0xFF);
    
            bufIn[bufIn_i++] = (byte) c1;
            bufIn[bufIn_i++] = (byte) c2;
            bufIn[bufIn_i++] = (byte) c3;
        }
    
        byte[] out = new byte[bufIn.length - p.length()];
        System.arraycopy(bufIn, 0, out, 0, out.length);
        return out;
    }
    

    服务器端(Java):

    @Override
    public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException
    {
        byte[] binaryData = new byte[1000];
        for( int i = 0; i < 1000; i++ )
            binaryData[i] = (byte) (Byte.MIN_VALUE + (i % (Math.pow(2, Byte.SIZE))));
        System.out.println("Sending: " + Arrays.toString(binaryData));
    
        byte[] base64Encoded = org.apache.commons.codec.binary.Base64.encodeBase64(binaryData);
    
        response.setContentType("application/octet-stream");
        PrintWriter out = response.getWriter();
        out.write(new String(base64Encoded));
    }
    

    【讨论】:

      【解决方案4】:

      这里有一个解决方案,可以让您轻松地从任何 URL 读取字节:

              XMLHttpRequest request = XMLHttpRequest.create();
              request.open("GET", "http://127.0.0.1:8888/sample/index.bin");
              request.setResponseType(ResponseType.ArrayBuffer);
              request.setOnReadyStateChange(new ReadyStateChangeHandler() {
      
                  @Override
                  public void onReadyStateChange(XMLHttpRequest xhr) {
                      if (xhr.getReadyState() == XMLHttpRequest.DONE) {
                          if (xhr.getStatus() == 200) {
                              ArrayBuffer buffer = xhr.getResponseArrayBuffer();
                              Uint8Array array = TypedArrays.createUint8Array(buffer);
                              System.out.println("got " + array.length() + " bytes: ");
                              for (int i = 0; i < array.length(); i++) {
                                  System.out.println(array.get(i));
                              }
                          } else {
                              System.out.println("response status: " + xhr.getStatus() + " " + xhr.getStatusText());
                          }
                      }
                  }
              });
              request.send();
      

      【讨论】:

        猜你喜欢
        • 2011-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多