使用: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
相关产品推荐
相关产品推荐

