问题描述
我对 Swift 还很陌生,对 NIO 非常。
我正在将 Swift 代码添加到一个需要将大量数据 (GB) 上传/下载到 AWS 的大型项目。为此,我导入了严重依赖 NIO 的 GitHub 项目 Soto。
大多数发送/接收数据的方法都是通过 ByteBuffer
结构实现的。我的应用程序已将数据上传到 Foundation Data
对象中。我无法找出将这些 Data
对象放入 NIO 的最佳方法。
在 NIO's ByteBuffer
(2.26.0) 的文档中说明
支持的类型: 可以从/向 ByteBuffer 读取/写入各种类型。 ...开箱即用,ByteBuffer 支持例如以下类型(非详尽列表):
- 字符串/静态字符串
- Swift 的各种(无符号)整数类型
- 基金会的数据
- [UInt8] 以及通常的任何 UInt8 集合
然而,最新的 swift-nil 包没有 ByteBuffer
支持 Foundation Data
对象。相反,它支持 dispatchData
对象,而后者似乎与 Data
对象没有互操作性。
我想避免的是复制每个数据块(一次 100 MB),只是为了在 Data
和 dispatchData
类型之间进行转换。
所以...
现在我的想法是其中之一
-
使用
ByteBuffer
初始化dispatchData
结构,该Data
使用指向Data
对象中原始字节数组的无复制初始化程序创建,以及一个自定义释放器保留ByteBuffer
对象,直到dispatchData
和index.html
对象被销毁。
如果您有任何想法、经验或建议(尤其是选项 #1),我将不胜感激。
解决方法
您需要使用 import NIOFoundationCompat
来获取任何适用于 Foundation
数据类型的 NIO 方法,例如 Data
(或 JSONDecoder
/JSONEncoder
) . NIOFoundationCompat
只是 swift-nio
包的另一个模块,因此您不需要其他依赖项。
但要清楚的是,在引擎盖下,总会有副本,但您可能不需要担心它们,副本在当今的 CPU 上非常快。如果您绝对想避免复制,则需要立即创建 ByteBuffer
。为了帮助您解决这个问题,您可能需要添加要通过网络发送的数据的来源。
如果您担心内存使用情况并且正在上传大缓冲区,也许您应该使用 AWSPayload.stream
。这允许您将小 ByteBuffers
流式传输到 AWS。这是一个以 16k 块的形式将 Data
流式传输到 S3 的示例
func uploadData( _ data: Data) -> EventLoopFuture<S3.PutObjectOutput> {
var index = 0
let payload = AWSPayload.stream { eventLoop in
let maxChunkSize = 16*1024
let size = min(maxChunkSize,data.count - index)
// are we done yet
if size == 0 {
return eventLoop.makeSucceededFuture(.end)
} else {
// create bytebuffer and return
let byteBuffer = ByteBufferAllocator().buffer(data: data[index..<(index+size)])
index += size
return eventLoop.makeSucceededFuture(.byteBuffer(byteBuffer))
}
}
let putRequest = S3.PutObjectRequest(body: payload,bucket: name,key: "tempfile")
return s3.putObject(putRequest)
}