You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用:zlib.safeInflate/2构建流解压gzip文件出现:data_error错误

Elixir 流式解压gzip文件报错修复方案

核心错误原因

  • File.stream!默认使用文本行读取模式,读取二进制gzip压缩文件会导致数据截断/编码错误,必须指定二进制读取模式和固定字节块大小。
  • 原实现没有处理完所有zlib内部缓冲数据就调用了:zlib.inflateEnd,触发:data_error。
  • 所有压缩chunk处理完成后没有主动刷出zlib剩余的解压数据,导致解压结果不完整。

修复后的完整实现

defmodule Z do
  def inflate(compressed_stream) do
    Stream.transform(
      compressed_stream,
      # 初始化zlib上下文,16+15表示自动识别gzip头部
      fn ->
        z = :zlib.open()
        :zlib.inflateInit(z, 16 + 15)
        {z, :in_progress}
      end,
      fn chunk, {z, :in_progress} ->
        # 处理当前压缩块,循环刷出所有可解压的数据
        decompressed_chunks = process_chunk(z, chunk)
        {decompressed_chunks, {z, :in_progress}}
      end,
      # 流结束后处理剩余缓冲,关闭zlib上下文
      fn {z, :in_progress} ->
        # 刷出最后剩余的缓冲数据
        remaining = process_chunk(z, [])
        # 确认流完成后再调用结束方法
        :ok = :zlib.inflateEnd(z)
        :zlib.close(z)
        remaining
      end
    )
  end

  # 循环调用safeInflate处理单个块,直到没有更多数据可输出
  defp process_chunk(z, chunk) do
    do_process_chunk(z, :zlib.safeInflate(z, chunk), [])
  end

  defp do_process_chunk(_z, :halt, acc), do: Enum.reverse(acc)
  defp do_process_chunk(z, {:continue, data}, acc) do
    do_process_chunk(z, :zlib.safeInflate(z, []), [IO.iodata_to_binary(data) | acc])
  end
  defp do_process_chunk(_z, {:finished, data}, acc) do
    Enum.reverse([IO.iodata_to_binary(data) | acc])
  end
end

# 调用示例,必须指定二进制模式和块大小,比如每次读4KB
filename = "file.gz"
filename
|> File.stream!([:binary], 4096)
|> Z.inflate
|> Enum.into("")
|> IO.inspect()

关键优化点

  • 调用File.stream!时传入:binary参数强制二进制读取,指定4096字节的固定块大小,避免文本模式的编码和行截断问题。
  • 单独封装process_chunk函数循环调用:zlib.safeInflate,直到当前块所有可解压数据都被输出。
  • 流结束回调中主动刷出zlib内部剩余缓冲数据,确认所有数据处理完成后再调用:zlib.inflateEnd关闭上下文,避免报错。

内容的提问来源于stack exchange,提问作者Eric

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 13:18:01