RxSwift:使用运算符组合下载照片并保存在本地

Posted

技术标签:

【中文标题】RxSwift:使用运算符组合下载照片并保存在本地【英文标题】:RxSwift: use a combination of operators to download a photo and save it locally 【发布时间】:2016-10-29 19:28:15 【问题描述】:

我正在尝试实现一种 reactive 方式来执行一些操作:

    请求下载照片 从next events 获取下载进度 完成后将照片保存到本地

所以我从 RxSwift 开始并像这样实现它

photoController.downloadPhoto(photoItem.photo)
.doOnNext  downloadTaskInfo in
    photoItem.viewState = .NetworkProgress(task: downloadTaskInfo.task, progress: downloadTaskInfo.progress)

.flatMapLatest  downloadTaskInfo in
    return phphotoLibrary.savePhoto(downloadTaskInfo.buffer)

.observeOn(MainScheduler.instance)
.subscribe(
    onError:  error in
        photoItem.viewState = .NetworkFailed
    ,
    onCompleted: 
        photoItem.viewState = .Default
    
)
.addDisposableTo(disposeBag)

但是flatMapLatest 并没有达到我的预期。我以为flatMapLatest 可以让我抓住最新的事件并进行另一个操作。

因此,我决定将其替换为reduce 以实现我的想法,但我认为这不是正确的运算符,因为我不想将所有下载进度加入一个变量中。我想要的是可以等待下载完成然后获取最新信息以继续其他操作,例如在本地保存照片。 使用 concat 我无法从第一个 Observable 接收结果。

我需要类似的东西

// ????
.waitUntilDownloadFinishesAndContinueWith  downloadTaskInfo in
    return PHPhotoLibrary.savePhoto(downloadTaskInfo.buffer)

有人能解释一下设计这个的正确方法吗?

更新

我决定选择withLatestFrom,但即便如此我还是遇到了一些问题。 downloadPhotoObservable 处理得太早了。

let downloadPhotoObservable = photoController.downloadPhoto(photoItem.photo)
    .doOnNext  downloadTaskInfo in
        photoItem.viewState = .NetworkProgress(task: downloadTaskInfo.task, progress: downloadTaskInfo.progress)
    

Observable.just(photoItem)
    .withLatestFrom(downloadPhotoObservable)
    .map  downloadTaskInfo in
        PHPhotoLibrary.savePhoto(downloadTaskInfo.buffer)
    
    .observeOn(MainScheduler.instance)
    .subscribe(
        onError:  error in
            photoItem.viewState = .NetworkFailed
        ,
        onCompleted: 
            photoItem.viewState = .Default
        
    )
    .addDisposableTo(disposeBag)

我肯定做错了什么。

【问题讨论】:

【参考方案1】:

所以,我找到了一种方法来实现我想要做的事情。我决定过滤所有结果并比较最终的buffer 长度。 buffer 是照片暂留的下一部分。

photoController.downloadPhoto(photoItem.photo)
    .downloadProgress()
    // Receive the download progress
    .doOnNext  downloadTaskInfo in
        photoItem.viewState = .NetworkProgress(task: downloadTaskInfo.task, progress: downloadTaskInfo.progress)
    
    // Wait for the complete buffer
    .filter  downloadTaskInfo in
        downloadTaskInfo.contentLength == Int64(downloadTaskInfo.buffer.length)
    
    // Save it locally
    .flatMap  downloadTaskInfo in
        PHPhotoLibrary.savePhoto(downloadTaskInfo.buffer)
    
    .observeOn(MainScheduler.instance)
    .subscribe(
        onError:  error in
            photoItem.viewState = .NetworkFailed
        ,
        onCompleted: 
            photoItem.viewState = .Default
        
    )
    .addDisposableTo(disposeBag)

顺便说一句,我正在使用scan 运算符来调用进度信息。我使用名为downloadProgress 的自定义运算符创建了一个快捷方式:

extension ObservableType where E == NetworkDataTaskInfo 
    func downloadProgress() -> Observable<NetworkDownloadTaskInfo> 
        let seed = NetworkDownloadTaskInfo(task: NopNetworkTask(), buffer: NSMutableData(), progress: 0, contentLength: 0)
        return scan(seed, accumulator:  latestDownloadTaskInfo, currentDataTaskInfo in
            var downloadedProgress: Float = 0
            var contentLength: Int64 = 0

            if let response = currentDataTaskInfo.response 
                // Start
                contentLength = response.expectedContentLength
            
            else if let data = currentDataTaskInfo.data 
                // Accumulate
                contentLength = latestDownloadTaskInfo.contentLength
                latestDownloadTaskInfo.buffer.appendData(data)
                downloadedProgress = Float(latestDownloadTaskInfo.buffer.length) / Float(contentLength)
            

            if contentLength <= 0 
                throw NSURLError.ZeroByteResource
            

            // Accumulated info
            return NetworkDownloadTaskInfo(
                task: currentDataTaskInfo.task,
                buffer: latestDownloadTaskInfo.buffer,
                progress: downloadedProgress,
                contentLength: contentLength
            )
        )
    

【讨论】:

以上是关于RxSwift:使用运算符组合下载照片并保存在本地的主要内容,如果未能解决你的问题,请参考以下文章

Android 使用系统相机拍摄照片保存到本地并在系统相册中显示

手机QQ音乐里,歌手→详情→歌手相册,里的照片怎么保存到本地

RxSwift - Observable.generate - 使用附加映射处理顺序请求

使用PhoneGap截取图片,在app中显示并保存到本地存储

android保存照片到相册的一些事

RxSwift 对一个 observable 的多个订阅