Ios Swift:通过HTTP发送大型数字数组([Double])的最佳方式

Ios Swift:通过HTTP发送大型数字数组([Double])的最佳方式,ios,json,swift,http,Ios,Json,Swift,Http,我想在几个小时的无结论性研究和测试之后,我会问: 介绍 我正试图将非常大的Doubles数组从应用程序发送到服务器,当然,我希望尽可能地压缩它。 具体而言,这些数组包含CMDeviceMotion组件(加速度、x、y、z、陀螺仪等),但此问题应适用于任何大的数字数组(超过100K或一百万个值) 通过研究选项,我尝试并发现了什么 假设我有一个大数组Double(还有许多其他数组): var CMX=CM.map({$0.userAcceleration.x}) 这里,CMX属于[Double]

我想在几个小时的无结论性研究和测试之后,我会问:

介绍 我正试图将非常大的
Double
s数组从应用程序发送到服务器,当然,我希望尽可能地压缩它。 具体而言,这些数组包含
CMDeviceMotion
组件(加速度、x、y、z、陀螺仪等),但此问题应适用于任何大的数字数组(超过100K或一百万个值)

通过研究选项,我尝试并发现了什么 假设我有一个大数组
Double
(还有许多其他数组):


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

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

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

  • 首先,作为单个逗号分隔字符串:
    
    {“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 ....
    
  • 大小为153KB

    这比以二进制数据的形式发送要大,因为这里的单个数字是64位(8字节),变为17字节长(每个字符一个字节)+1=18(为逗号添加了一个字符)

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

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

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

    这使得文件大小为206KB

  • 将数据作为JSON数组发送
  • 只需发送:

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

    事实证明,这个数字数组实际上被转换成了一个逗号分隔的字符串,大小与试用版1中的相同(160Kb)

  • 在不使用base 64编码的情况下作为数据发送
  • 这样做:

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

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

    问题: 如何在JSON对象中以更精简的方式发送这些数组


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

    简单的方法是:

    let data: Data = CMX.withUnsafeBufferPointer { pointer in
        return Data(buffer: pointer)
    }
    
    你有一个二进制缓冲区,所有的双精度浮点数加在一起

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

    let base64String = data.base64EncodedString()
    
    这个
    base64String
    应该为
    POST
    (?)
    HTTP
    请求的
    AX
    参数传递

    编辑

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

    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
    
    扩展数组{
    初始化?(数据:数据){
    //这个检查应该更复杂,但这里我们只检查总字节数是否除以一个以字节为单位的元素大小
    guard data.count%MemoryLayout.size==0 else{return nil}
    让elementCount=data.count/MemoryLayout.size
    let buffer=UnsafeMutableBufferPointer.allocate(容量:elementCount)
    data.copyBytes(到:缓冲区)
    self=buffer.map({$0})
    buffer.deallocate()
    }
    //在这里包装上面的代码
    var数据:数据{
    返回self.withUnsafeBufferPointer{pointer in
    返回数据(缓冲区:指针)
    }
    }
    }
    let converted:[Double]?=Array(data:CMX.data)//现在转换的值应该等于CMX
    
    您可以尝试压缩数据字符串,并将其发送base64编码,或者尝试获取Double的二进制表示形式(如此处:)并发送base64 Encode64。任何压缩方法都会要求了解数据的潜在模式。例如,数据范围、顺序是否重要、固定十进制数等。您可能会看到,甚至有一个非常棒的答案!这将文件大小降低到82 Kb(从153)对于64位double和40 Kb的32位FloatsHi,再次感谢您的帮助。如何将数据转换回数组?@kmn,已添加到答案中