RxSwift - 将序列拆分为较小的序列以进行连接

时间:2017-04-20 18:02:52

标签: ios swift concat rx-swift

背景

我正在尝试使用Range标头通过数据块下载文件。

目标

我想将大量的http请求分成四个序列,然后我可以连接到一次处理4个请求。

当前的立场

我正在使用我的序列并使用concat来确保第一个请求observable在我开始第二个之前完成。这样做是为了确保我不会因为请求过多而使Alamofire超载,从而最终导致请求超时。

理想情况下,我希望将我的序列分成四个相当相等的序列,因为Alamofire设置为一次处理与主机的四个连接。我想这样做是因为我相信它会提高我的下载速度。

使用大块下载文件

Observable.generate(initialState: 0, condition: { $0 < fileSize }, iterate: {$0 + self.defaultChunkSize})
    .map( { (startChunk) in
        let endChunk = startChunk + self.defaultChunkSize > fileSize ? fileSize : startChunk + self.defaultChunkSize

        return (startChunk, endChunk)
    })
    .map({ (startChunk: Int, endChunk: Int) -> Observable<FileChunkResult> in
        self.filesClient.downloadChunkOf(fileId: file.id, startChunk: Int64(startChunk), endChunk: Int64(endChunk))
    })

    .concat() // <----- This is where I am forcing the large sequence to do one observable at a time

    .flatMap( { (result: FileChunkResult) -> Observable<FileSaveChunkResult> in
        switch (result) {
        case FileChunkResult.success(let chunkData):
            return self.saveChunkToFile(fileChunk: chunkData, location: urlToSaveTo)
        case FileChunkResult.failure: // Maybe change this to just default and return Observable.just(FileSaveChunkResult.failure)
            break
        case FileChunkResult.parserError:
            break
        }

        return Observable.just(FileSaveChunkResult.failure)
    })
    .flatMap( { (result: FileSaveChunkResult) -> Observable<Progress> in
        switch (result) {
        case FileSaveChunkResult.success(let bytesSaved):
            progress.completedUnitCount += bytesSaved
        case FileSaveChunkResult.failure:
            break
        }

        return Observable.just(progress)
    })

1 个答案:

答案 0 :(得分:1)

下面的代码会将块分成四个相同大小的数组,这些数组使用concat来确保每次只有一个来自每个数组的保存是活动的。这意味着无论任何特定呼叫有多快或多慢,您都将始终有4 saveChunkToFile个呼叫处于活动状态。

换句话说,它会立即启动四个请求,然后每当前一个请求完成时就会启动一个请求。

let generator = Observable.generate(initialState: 0, condition: { $0 < fileSize }, iterate: { $0 + defaultChunkSize })
let chunks  = generator.map( { (startChunk) -> (Int64, Int64) in
    let endChunk = (startChunk + defaultChunkSize > fileSize ? fileSize : startChunk + defaultChunkSize )
    return (startChunk, endChunk)
})

let count = ceil(Double(fileSize) / Double(defaultChunkSize) / 4)
let requests = chunks.window(timeSpan: 0.0, count: Int(count), scheduler: MainScheduler.instance)
    .flatMap { $0
        .map( { (startChunk: Int64, endChunk: Int64) -> Observable<FileChunk> in
            return makeChunkRequest(url: downloadUrl, startChunk: startChunk, endChunk: endChunk)
        }).concat()
}

let downloadObservable = requests
    .flatMap( { (fileChunk: FileChunk) -> Observable<FileSaveChunkResult> in
        return saveChunkToFile(fileChunk: fileChunk, location: localDestinationUrl)
    }).flatMap( { (saveResult: FileSaveChunkResult) -> Observable<Progress> in
        if case .success(let bytesSaved) = saveResult {
            progress.completedUnitCount += bytesSaved
        }
        return Observable.just(progress)
    })

_ = downloadObservable.subscribe(onNext: { print(Date(), $0) })