【问题标题】:Swift: best way to send large arrays of numbers ([Double]) over HTTPSwift:通过 HTTP 发送大量数字([Double])的最佳方式
【发布时间】:2018-09-08 08:29:17
【问题描述】:

经过数小时的不确定研究和测试,我想我会问:

简介

我正在尝试将非常大的Doubles 数组从应用程序发送到服务器,当然,我想尽可能地压缩它。 具体来说,这些数组包含CMDeviceMotion 组件(加速度、x、y、z、陀螺仪等...),但这个问题应该适用于任何大型数字数组(超过 100K 或一百万个值)

我通过研究选项尝试并发现了什么

假设我有一大堆Double(还有很多其他的):

var CMX = CM.map({$0.userAcceleration.x})

这里,CMX[Double] 类型,CM[CMDeviceMotion]

我尝试通过以不同方式发送CMX 向我的服务器发出POST 请求,然后在我在服务器上收到它后计算总大小:

  1. 首先,作为一个逗号分隔的字符串: {"AX":"-0.0441827848553658,-0.103976868093014,-0.117475733160973,-0.206566318869591,-0.266509801149368,-0.282151937484741,-0.260240525007248,-0.266505032777786,-0.315020948648453,-0.305839896202087,0.0255246963351965,0.0783950537443161,0.0749507397413254,0.0760494321584702,-0.0101579604670405,0.106710642576218,0.131824940443039,0.0630970001220703,0.21177926659584,0.27022996544838,0.222621202468872,0.234281644225121,0.288497060537338,0.176655143499374,0.193904414772987,0.169417425990105,0.150193274021149,0.00871349219232798,-0.0270088445395231,-0.0 ....

大小 153 KB。

这比作为二进制数据发送更大是有道理的,因为这里的单个数字是 64 位(8 个字节),并且变成 17 个字节长(每个字符一个字节)+1 = 18(为逗号)。

根据这个推理,将数组作为二进制数据发送应该更小。

  1. Base 64 编码

在这里,我使用NSKeyedArchiver 将数组转换为Data 对象,并在发送数据之前对数据进行base 64 编码:

["AX":NSKeyedArchiver.archivedData(withRootObject:CM.map({$0.userAcceleration.x})).base64EncodedString()]

这使得文件大小为 206 Kb

  1. 以 JSON 数组形式发送数据

只需发送:

["AX": CM.map({$0.userAcceleration.x})]

事实证明,这个数字数组实际上被转换为逗号分隔的字符串,大小最终与试验 1 中的相同(160Kb)

  1. Data 发送,不使用base 64 编码

这样做:

["AX":NSKeyedArchiver.archivedData(withRootObject:CM.map({$0.userAcceleration.x}))

使应用程序在运行时崩溃,因此我无法将 Data 对象作为 JSON 中的值发送

问题

如何在 JSON 对象中以更简洁的方式发送这些数组?

请注意,我已经考虑过下采样,并使用 32 位浮点数来减小大小。

【问题讨论】:

标签: ios json swift http


【解决方案1】:

简单的方法是这样做:

let data: Data = CMX.withUnsafeBufferPointer { pointer in
    return Data(buffer: pointer)
}

你有二进制缓冲区,所有的 Doubles/Floats 组合在一起。

但由于 HTTP 是基于文本的协议,您必须将此 data 转换为 base64 字符串:

let base64String = data.base64EncodedString()

这个base64String 应该传递给你的POSTAX 参数(?) HTTP 请求。

编辑

要将其转换回来,您可以使用如下代码:

extension Array {
    init?(data: Data) {
        // This check should be more complex, but here we just check if total byte count divides to one element size in bytes
        guard data.count % MemoryLayout<Element>.size == 0 else { return nil }

        let elementCount = data.count / MemoryLayout<Element>.size
        let buffer = UnsafeMutableBufferPointer<Element>.allocate(capacity: elementCount)
        data.copyBytes(to: buffer)

        self = buffer.map({$0})
        buffer.deallocate()
    }

   // Wrapped here code above
    var data: Data {
        return self.withUnsafeBufferPointer { pointer in
            return Data(buffer: pointer)
        }
    }
}

let converted: [Double]? = Array(data: CMX.data) // converted now should be equal to CMX

【讨论】:

  • 绝妙的答案!这使 64 位双精度的文件大小降低到 82 Kb(从 153)和 32 位浮点数的 40 Kb
  • 您好,再次感谢您的帮助。如何将数据转回数组?
  • @kmn,添加到答案中
猜你喜欢
  • 2021-03-04
  • 2020-06-20
  • 2011-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-10
相关资源
最近更新 更多