二进制数据和GWT

时间:2012-06-15 09:41:27

标签: gwt binary bytearray

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

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

我认为我可以通过GWT从我的服务器“加载”文本文件,对吧?为什么我不能用GWT读取二进制数据?或者我可以将二进制数据文件作为文本读取,将字符串读入byte []吗?我读了很多关于base64编码的内容,GWT可以读取它,虽然我真的不明白他们在说什么。我可以将我的服务器配置为以base64编码的方式提供这些二进制数据文件,然后使用GWT读取它们吗?

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

感谢您的帮助:)

4 个答案:

答案 0 :(得分:5)

我们假设我们使用的是HTML 4。

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

您为文件设置了mime类型,因为您希望浏览器下载文件并调用本地PC来调用相应的软件 - 例如,pdf调用pdf reader或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 / class文件转换为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编码背后的动机:auth令牌,图片,音频文件的传输 - 这样他们的二进制表示和排序就不会被像endianness这样的架构细微差别弄乱。

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

如果是浏览器的OS处理(如音频,图片,pdf),则仅使用二进制信息。发送二进制数据仅由javascript例程处理没有意义。 javascript例程必须使用无关的处理时间来翻译它(除非再次,如果你生活中的目的是攀爬......)。

答案 1 :(得分:4)

是的,这是可能的。

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

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

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

答案 2 :(得分: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));
}

答案 3 :(得分:1)

这是一个解决方案,可让您轻松地从任何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();