Kotlin解压gzip失败

huangapple go评论81阅读模式
英文:

Kotlin gzip uncompress fail

问题

这是我尝试将Java GZIP解压缩代码简化为Kotlin后的情况,但在更改后似乎出现了问题。

以下是Java代码:

public static byte[] uncompress(byte[] compressedBytes) {
  if (null == compressedBytes || compressedBytes.length == 0) {
    return null;
  }

  ByteArrayOutputStream out = null;
  ByteArrayInputStream in = null;
  GZIPInputStream gzipInputStream = null;

  try {
    out = new ByteArrayOutputStream();
    in = new ByteArrayInputStream(compressedBytes);
    gzipInputStream = new GZIPInputStream(in);
    byte[] buffer = new byte[256];
    int n = 0;

    while ((n = gzipInputStream.read(buffer)) >= 0) {
      out.write(buffer, 0, n);
    }

    return out.toByteArray();
  } catch (IOException ignore) {
  } finally {
    CloseableUtils.closeQuietly(gzipInputStream);
    CloseableUtils.closeQuietly(in);
    CloseableUtils.closeQuietly(out);
  }

  return null;
}

这是我的Kotlin代码:

payload = GZIPInputStream(payload.inputStream())
    .bufferedReader()
    .use { it.readText() }
    .toByteArray()

我得到了以下错误:

com.google.protobuf.nano.InvalidProtocolBufferNanoException: 在解析协议消息时,在字段的中间意外结束了输入。这可能意味着输入已被截断,或者嵌入的消息错误地报告了其自己的长度。

似乎解压缩过程被读取器中断了?

英文:

I try to simplify my java gzip uncompress code to kotlin. But after I changed, it sames broken.

Here is the java code

  public static byte[] uncompress(byte[] compressedBytes) {
    if (null == compressedBytes || compressedBytes.length == 0) {
      return null;
    }

    ByteArrayOutputStream out = null;
    ByteArrayInputStream in = null;
    GZIPInputStream gzipInputStream = null;

    try {
      out = new ByteArrayOutputStream();
      in = new ByteArrayInputStream(compressedBytes);
      gzipInputStream = new GZIPInputStream(in);
      byte[] buffer = new byte[256];
      int n = 0;

      while ((n = gzipInputStream.read(buffer)) >= 0) {
        out.write(buffer, 0, n);
      }

      return out.toByteArray();
    } catch (IOException ignore) {
    } finally {
      CloseableUtils.closeQuietly(gzipInputStream);
      CloseableUtils.closeQuietly(in);
      CloseableUtils.closeQuietly(out);
    }

    return null;
  }

This is my kotlin code.

  payload = GZIPInputStream(payload.inputStream())
      .bufferedReader()
      .use { it.readText() }
      .toByteArray()

And I got this error.

com.google.protobuf.nano.InvalidProtocolBufferNanoException: While parsing a protocol message, the input ended unexpectedly in the middle of a field.  This could mean either than the input has been truncated or that an embedded message misreported its own length.

It seems that the decompression process was interrupted by reader?

答案1

得分: 1

readText(charset: Charset = Charsets.UTF_8) 方法将字节解码为 UTF-8 字符集,这就是为什么它说 "这可能意味着输入已被截断",它可能尝试将 8 位转换为 Char 并构建一个字符串。

使用 readBytes() 方法获取 ByteArray,它在 JVM 平台中表示为 byte[]

示例:

GZIPInputStream(payload.inputStream())
      .bufferedReader()
      .use { it.readBytes() }

编辑:
要读取字节,不应该使用 Reader,它是用于按照 Kotlin 的 InputStream.bufferedReader 中定义的 UTF-8 格式读取文本的:

public inline fun InputStream.bufferedReader(charset: Charset = Charsets.UTF_8): BufferedReader = reader(charset).buffered()

InputStream.readBytes() 将以 8KB 缓冲区自身读取字节。

public fun InputStream.readBytes(): ByteArray {
    val buffer = ByteArrayOutputStream(maxOf(DEFAULT_BUFFER_SIZE, this.available()))
    copyTo(buffer)
    return buffer.toByteArray()
}
// 这将自动使用 8KB 缓冲区进行复制
// DEFAULT_BUFFER_SIZE = 8 * 1024
public fun InputStream.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
    var bytesCopied: Long = 0
    val buffer = ByteArray(bufferSize)
    var bytes = read(buffer)
    while (bytes >= 0) {
        out.write(buffer, 0, bytes)
        bytesCopied += bytes
        bytes = read(buffer)
    }
    return bytesCopied
}

因此,您只需执行:

GZIPInputStream(payload.inputStream()).use { it.readBytes() }
英文:

The readText(charset: Charset = Charsets.UTF_8) decodes the bytes into UTF-8 character set, which is why it says "This could mean either than the input has been truncated" it probably have tried to convert 8-bits into a Char and build a String out of it.

Use the readBytes() to get ByteArray which is represented same as byte[] in JVM platform.

Example:

GZIPInputStream(payload.inputStream())
      .bufferedReader()
      .use { it.readBytes() }

Edit:

For reading bytes, you shouldn't be using the Reader, it is meant for reading the Text in UTF-8 format as defined in the Kotlin's InputStream.bufferedReader:

public inline fun InputStream.bufferedReader(charset: Charset = Charsets.UTF_8): BufferedReader = reader(charset).buffered()

The InputStream.readBytes() will read the bytes at a buffer of 8KB itself.

public fun InputStream.readBytes(): ByteArray {
    val buffer = ByteArrayOutputStream(maxOf(DEFAULT_BUFFER_SIZE, this.available()))
    copyTo(buffer)
    return buffer.toByteArray()
}
// This copies with 8KB buffer automatically
// DEFAULT_BUFFER_SIZE = 8 * 1024
public fun InputStream.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
    var bytesCopied: Long = 0
    val buffer = ByteArray(bufferSize)
    var bytes = read(buffer)
    while (bytes >= 0) {
        out.write(buffer, 0, bytes)
        bytesCopied += bytes
        bytes = read(buffer)
    }
    return bytesCopied
}

So you just have to do:

GZIPInputStream(payload.inputStream()).use { it.readBytes() }

答案2

得分: 0

使用以下函数:

fun File.unzip(unzipLocationRoot: File? = null) {
    val rootFolder = unzipLocationRoot
        ?: File(parentFile.absolutePath + File.separator + nameWithoutExtension)
    if (!rootFolder.exists()) {
        rootFolder.mkdirs()
    }

    ZipFile(this).use { zip ->
        zip
            .entries()
            .asSequence()
            .map {
                val outputFile = File(rootFolder.absolutePath + File.separator + it.name)
                ZipIO(it, outputFile)
            }
            .map {
                it.output.parentFile?.run {
                    if (!exists()) mkdirs()
                }
                it
            }
            .filter { !it.entry.isDirectory }
            .forEach { (entry, output) ->
                zip.getInputStream(entry).use { input ->
                    output.outputStream().use { output ->
                        input.copyTo(output)
                    }
                }
            }
    }
}

将文件作为参数传递如下:

val zipFile = File(你的文件目录, 你的文件名)
zipFile.unzip()

希望这对你有所帮助!🙂📦

英文:

use the following function:

    fun File.unzip(unzipLocationRoot: File? = null) {

    val rootFolder = unzipLocationRoot
        ?: File(parentFile.absolutePath + File.separator + nameWithoutExtension)
    if (!rootFolder.exists()) {
        rootFolder.mkdirs()
    }

    ZipFile(this).use { zip ->
        zip
            .entries()
            .asSequence()
            .map {
                val outputFile = File(rootFolder.absolutePath + File.separator + it.name)
                ZipIO(it, outputFile)
            }
            .map {
                it.output.parentFile?.run {
                    if (!exists()) mkdirs()
                }
                it
            }
            .filter { !it.entry.isDirectory }
            .forEach { (entry, output) ->
                zip.getInputStream(entry).use { input ->
                    output.outputStream().use { output ->
                        input.copyTo(output)
                    }
                }
            }
    }

}

Pass the file as a parameter as follow:

val zipFile = File(your file directory, your file name)
            zipFile.unzip()

Hope this would help 🙏🏼

huangapple
  • 本文由 发表于 2020年7月21日 19:15:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/63013305.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定