压缩 NIO

一个用于 Swift NIO ByteBuffers 的压缩库。

压缩和解压

Compress NIO 包含许多用于压缩和解压缩 ByteBuffers 的方法。一个简单的用法是:

var compressedBuffer = buffer.compress(with: .gzip)
var uncompressedBuffer = buffer.decompress(with: .gzip)

这些方法会为您分配一个新的 ByteBufferdecompress 方法在解压缩时可以分配多个 ByteBuffers,具体取决于原始 ByteBuffer 的压缩程度。 最好提前知道您需要的缓冲区大小,并自己分配一次,然后使用以下函数。

let uncompressedSize = buffer.readableBytes
var compressedBuffer = ByteBufferAllocator().buffer(capacity: knownCompressedSize)
try buffer.compress(to: &compressedBuffer, with: .deflate)
var uncompressedBuffer = ByteBufferAllocator().buffer(capacity: uncompressedSize)
try compressedBuffer.decompress(to: &uncompressedBuffer, with: .deflate)

这将返回使用 deflate 压缩算法写入压缩数据所需的缓冲区的最大大小。

如果您提供的缓冲区太小,则会抛出 CompressNIO.bufferOverflow 错误。 您需要提供更大的 ByteBuffer 才能完成操作。

流式处理

在某些情况下,您可能想要或需要将数据块分成更小的切片进行压缩/解压缩。 如果您有一个大文件要压缩,最好加载到较小的切片中,而不是一次性将所有内容加载到内存中。 如果您通过 HTTP 接收到压缩数据块,则无法保证它会一次性交付。 Swift NIO Compress 提供了流式 API 来支持这些情况。

压缩

有三种方法可以进行流式压缩:窗口(window)、分配(allocating)和原始(raw)。 所有这些方法都以调用 compressor.startStream 开始,并以调用 compressor.finishStream 结束。

窗口方法

对于窗口方法,您为压缩器提供一个工作缓冲区。 当您调用 compressStream 时,它会压缩到此缓冲区中,当缓冲区已满时,它将调用您提供的 process 闭包。

let compressor = ZlibCompressor(algorithm: .gzip)
var window = ByteBufferAllocator().buffer(capacity: 64*1024)
while var buffer = getData() {
    try buffer.compressStream(with: compressor, window: window, flush: .finish) { buffer in
        // process your compressed data
    }
}
try compressor.reset()

分配方法

使用分配方法,您可以让压缩器分配用于输出数据的 ByteBuffers。 它将计算压缩数据的最大可能大小,并为每个压缩数据块分配该大小的空间。 最后一个压缩块需要将 flush 参数设置为 .finish

let compressor = ZlibCompressor(algorithm: .gzip)
while var buffer = getData() {
    let flush: CompressNIOFlush = isThisTheFinalBlock ? .finish : .sync
    let compressedBuffer = try buffer.compressStream(with: compressor, flush: flush, allocator: ByteBufferAllocator())
}
try compressor.reset()

如果您不知道您的最终数据块是什么,您可以始终压缩一个空的 ByteBuffer,并将 flush 设置为 .finish 以获取您的最终块。 另请注意,flush 参数在循环中设置为 .sync。 这是必需的,否则下一个 compressStream 无法成功估计其缓冲区大小,因为可能仍有缓冲数据等待输出。

原始方法

使用此方法,您可以调用最低级别的函数,并处理当输出缓冲区中的空间不足时抛出的 .bufferOverflow 错误。 您需要一个循环来接收数据,然后您需要一个内部循环来压缩该数据。 您调用 compress,直到没有更多数据需要压缩。 每次您收到 .bufferOverflow 错误时,都必须提供新的输出数据。 读取所有输入数据后,您再次执行相同的操作,但将 flush 参数设置为 .finish

解压缩

与压缩流式数据一样,窗口(window)、分配(allocation)、原始(raw)这三种方法也适用于解压缩流式数据,但在解压缩时无需将 flush 参数设置为 .finish,这使一切都变得更容易一些。