Java 中客户端/服务器传输的压缩字符串

发布于 2024-08-04 11:22:54 字数 1267 浏览 5 评论 0原文

我使用专有的客户端/服务器消息格式来限制我可以通过网络发送的内容。我无法发送序列化对象,我必须将消息中的数据存储为字符串。我发送的数据是大的逗号分隔值,我想在将数据作为字符串打包到消息中之前对其进行压缩。

我尝试使用 Deflater/Inflater 来实现这一点,但在这个过程中我遇到了困难。

我使用以下两种方法来放气/充气。但是,将 compressString() 方法的结果传递给 decompressStringMethod() 将返回 null 结果。

public String compressString(String data) {
  Deflater deflater = new Deflater();
  byte[] target = new byte[100];
  try {
   deflater.setInput(data.getBytes(UTF8_CHARSET));
   deflater.finish();
   int deflateLength = deflater.deflate(target);
   return new String(target);
  } catch (UnsupportedEncodingException e) {
   //TODO
  }

  return data;
 }

 public String decompressString(String data) {

  String result = null;
  try {
   byte[] input = data.getBytes();

   Inflater inflater = new Inflater();
   int inputLength = input.length;
   inflater.setInput(input, 0, inputLength);

   byte[] output = new byte[100];
   int resultLength = inflater.inflate(output);
   inflater.end();

   result = new String(output, 0, resultLength, UTF8_CHARSET);
  } catch (DataFormatException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (UnsupportedEncodingException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

  return result;
 }

I work with a propriety client/server message format that restricts what I can send over the wire. I can't send a serialized object, I have to store the data in the message as a String. The data I am sending are large comma-separated values, and I want to compress the data before I pack it into the message as a String.

I attempted to use Deflater/Inflater to achieve this, but somewhere along the line I am getting stuck.

I am using the two methods below to deflate/inflate. However, passing the result of the compressString() method to decompressStringMethod() returns a null result.

public String compressString(String data) {
  Deflater deflater = new Deflater();
  byte[] target = new byte[100];
  try {
   deflater.setInput(data.getBytes(UTF8_CHARSET));
   deflater.finish();
   int deflateLength = deflater.deflate(target);
   return new String(target);
  } catch (UnsupportedEncodingException e) {
   //TODO
  }

  return data;
 }

 public String decompressString(String data) {

  String result = null;
  try {
   byte[] input = data.getBytes();

   Inflater inflater = new Inflater();
   int inputLength = input.length;
   inflater.setInput(input, 0, inputLength);

   byte[] output = new byte[100];
   int resultLength = inflater.inflate(output);
   inflater.end();

   result = new String(output, 0, resultLength, UTF8_CHARSET);
  } catch (DataFormatException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (UnsupportedEncodingException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

  return result;
 }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

轻拂→两袖风尘 2024-08-11 11:22:54

据我所知,您当前的方法是:

  1. 使用 getBytes("UTF-8") 将字符串转换为字节数组。
  2. 压缩字节数组
  3. 使用 new String(bytes, ..., "UTF-8") 将压缩字节数组转换为字符串。
  4. 发送压缩字符串
  5. 接收压缩字符串
  6. 使用getBytes("UTF-8") 将压缩字符串转换为字节数组。
  7. 解压缩字节数组
  8. 使用 new String(bytes, ..., "UTF-8") 将解压缩的字节数组转换为字符串。

此方法的问题在于步骤 3。压缩字节数组时,您创建的字节序列可能不再是有效的 UTF-8。结果将在步骤 3 中出现异常。

解决方案是使用 Base64 等“字节到字符”编码方案,将压缩后的字节转换为可传输的字符串。换句话说,将步骤 3 替换为对 Base64 编码函数的调用,将步骤 6 替换为对 Base64 解码函数的调用。

注意:

  1. 对于小字符串,压缩和
    编码实际上可能是
    增加传输字符串的大小。
  2. 如果压缩的字符串要合并到 URL 中,您可能需要选择与 Base64 不同的编码,以避免需要 URL 转义的字符。
  3. 根据您传输的数据的性质,您可能会发现特定于域的压缩比通用压缩效果更好。在创建逗号分隔的字符串之前考虑压缩数据。考虑逗号分隔字符串的替代方案。

From what I can tell, your current approach is:

  1. Convert String to byte array using getBytes("UTF-8").
  2. Compress byte array
  3. Convert compressed byte array to String using new String(bytes, ..., "UTF-8").
  4. Transmit compressed string
  5. Receive compressed string
  6. Convert compressed string to byte array using getBytes("UTF-8").
  7. Decompress byte array
  8. Convert decompressed byte array to String using new String(bytes, ..., "UTF-8").

The problem with this approach is in step 3. When you compress the byte array, you create a sequence of bytes which may no longer be valid UTF-8. The result will be an exception in step 3.

The solution is to use a "bytes to characters" encoding scheme like Base64 to turn the compressed bytes into a transmissible string. In other words, replace step 3 with a call to a Base64 encode function, and step 6 with a call to a Base64 decode function.

Notes:

  1. For small strings, compressing and
    encoding is likely to actually
    increase the size of the transmitted string.
  2. If the compacted String is going to be incorporated into a URL, you may want to pick a different encoding to Base64 that avoids characters that need to be URL escaped.
  3. Depending on the nature of the data you are transmitting, you may find that a domain specific compression works better than a generic one. Consider compressing the data before creating the comma-separated string. Consider alternatives to comma-separated strings.
深陷 2024-08-11 11:22:54

问题在于您将压缩字节转换为字符串,这会破坏数据。您的 compressStringdecompressString 应该适用于 byte[]

编辑:这是修订版本。它有效

编辑2:关于base64。您发送的是字节,而不是字符串。你不需要base64。

public static void main(String[] args) {
    String input = "Test input";
    byte[] data = new byte[100];

    int len = compressString(input, data, data.length);

    String output = decompressString(data, len);

    if (!input.equals(output)) {
        System.out.println("Test failed");
    }

    System.out.println(input + " " + output);
}

public static int compressString(String data, byte[] output, int len) {
    Deflater deflater = new Deflater();
    deflater.setInput(data.getBytes(Charset.forName("utf-8")));
    deflater.finish();
    return deflater.deflate(output, 0, len);
}

public static String decompressString(byte[] input, int len) {

    String result = null;
    try {
        Inflater inflater = new Inflater();
        inflater.setInput(input, 0, len);

        byte[] output = new byte[100]; //todo may oveflow, find better solution
        int resultLength = inflater.inflate(output);
        inflater.end();

        result = new String(output, 0, resultLength, Charset.forName("utf-8"));
    } catch (DataFormatException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return result;
}

The problem is that you convert compressed bytes to a string, which breaks the data. Your compressString and decompressString should work on byte[]

EDIT: Here is revised version. It works

EDIT2: And about base64. you're sending bytes, not strings. You don't need base64.

public static void main(String[] args) {
    String input = "Test input";
    byte[] data = new byte[100];

    int len = compressString(input, data, data.length);

    String output = decompressString(data, len);

    if (!input.equals(output)) {
        System.out.println("Test failed");
    }

    System.out.println(input + " " + output);
}

public static int compressString(String data, byte[] output, int len) {
    Deflater deflater = new Deflater();
    deflater.setInput(data.getBytes(Charset.forName("utf-8")));
    deflater.finish();
    return deflater.deflate(output, 0, len);
}

public static String decompressString(byte[] input, int len) {

    String result = null;
    try {
        Inflater inflater = new Inflater();
        inflater.setInput(input, 0, len);

        byte[] output = new byte[100]; //todo may oveflow, find better solution
        int resultLength = inflater.inflate(output);
        inflater.end();

        result = new String(output, 0, resultLength, Charset.forName("utf-8"));
    } catch (DataFormatException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return result;
}
少女七分熟 2024-08-11 11:22:54

对我来说:自己编写压缩算法很困难,但将二进制写入字符串并不困难。因此,如果我是你,我将正常序列化对象并通过压缩(由 ZipFile 提供)对其进行压缩,然后使用类似 Base64 编码/解码

我实际上有 BASE64 ENCODE/DECODE 函数。如果你想要的话我可以把它发布在这里。

TO ME: write compress algorithm myself is difficult but writing binary to string is not. So if I were you, I will serialize the object normally and zip it with compression (as provided by ZipFile) then convert to string using something like Base64 Encode/Decode.

I actually have BASE64 ENCODE/DECODE functions. If you wanted I can post it here.

从此见与不见 2024-08-11 11:22:54

如果你有一段代码似乎默默地失败了,也许你不应该捕获并吞下异常:

catch (UnsupportedEncodingException e) {
    //TODO
}

但是 decompress 返回 null 的真正原因是因为你的异常处理没有指定如何处理 result< /code> 当您捕获异常时 - result 保留为 null。您是否检查输出以查看是否发生任何异常?

如果我在格式错误的字符串上运行 decompress(),Inflater 会抛出这个 DataFormatException

java.util.zip.DataFormatException: incorrect header check
    at java.util.zip.Inflater.inflateBytes(Native Method)
    at java.util.zip.Inflater.inflate(Inflater.java:223)
    at java.util.zip.Inflater.inflate(Inflater.java:240)

If you have a piece of code which seems to be silently failing, perhaps you shouldn't catch and swallow Exceptions:

catch (UnsupportedEncodingException e) {
    //TODO
}

But the real reason why decompress returns null is because your exception handling doesn't specify what to do with result when you catch an exception - result is left as null. Are you checking the output to see if any Exceptions are occuring?

If I run your decompress() on a badly formatted String, Inflater throws me this DataFormatException:

java.util.zip.DataFormatException: incorrect header check
    at java.util.zip.Inflater.inflateBytes(Native Method)
    at java.util.zip.Inflater.inflate(Inflater.java:223)
    at java.util.zip.Inflater.inflate(Inflater.java:240)
前事休说 2024-08-11 11:22:54

充气器/放气器不是压缩字符串的解决方案。
我认为 GZIPInputString 和 GZIPOutputString 是压缩字符串的正确工具

Inflator/Deflator is not a solution for compress string.
I think GZIPInputString and GZIPOutputString is the proper tool to compress the string

审判长 2024-08-11 11:22:54

我遇到了类似的问题,通过 Base64 解码输入解决了这个问题。
即而不是

data.getBytes(UTF8_CHARSET)  

我尝试过

Base64.decodeBase64(data)  

并且它有效。

I was facing similar issue which was resolved by base64 decoding the input.
i.e instead of

data.getBytes(UTF8_CHARSET)  

i tried

Base64.decodeBase64(data)  

and it worked.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文