从iOS将多个图像上传到S3的有效方法 [英] Efficient way to upload multiple images to S3 from iOS

查看:55
本文介绍了从iOS将多个图像上传到S3的有效方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Amazon S3作为应用程序中的文件存储系统.我所有的项目对象都有几个与之关联的图像,并且每个对象仅存储图像URL,以保持数据库的轻量化.因此,我需要一种有效的方法直接从iOS将多个图像上传到S3,并在成功完成后将它们的URL存储在我发送给服务器的对象中.我仔细研究了Amazon提供的SDK和示例应用程序,但是我遇到的唯一示例是单个图像上传,如下所示:

I'm using Amazon S3 as my file storage system in the app. All my item objects have several images associated with them, and each stores only the image urls to keep my database lightweight. As such I need an efficient way to upload multiple images to S3 directly from iOS, and upon successful completion store their urls in the object I send to the server. I've perused the SDK and sample app Amazon provides, but the only example I've come across is for a single image upload, and goes as follows:

 func uploadData(data: NSData) {
    let expression = AWSS3TransferUtilityUploadExpression()
    expression.progressBlock = progressBlock

    let transferUtility = AWSS3TransferUtility.defaultS3TransferUtility()

    transferUtility.uploadData(
        data,
        bucket: S3BucketName,
        key: S3UploadKeyName,
        contentType: "text/plain",
        expression: expression,
        completionHander: completionHandler).continueWithBlock { (task) -> AnyObject! in
            if let error = task.error {
                NSLog("Error: %@",error.localizedDescription);
                self.statusLabel.text = "Failed"
            }
            if let exception = task.exception {
                NSLog("Exception: %@",exception.description);
                self.statusLabel.text = "Failed"
            }
            if let _ = task.result {
                self.statusLabel.text = "Generating Upload File"
                NSLog("Upload Starting!")
                // Do something with uploadTask.
            }

            return nil;
    }
}

对于5张以上的图像,这将变成一团糟,因为我必须等待每次上传成功返回后才能启动下一个,然后最终将对象发送到我的数据库.我是否需要有效,干净的代码来实现自己的目标?

For upwards of 5 images this will become a nested mess since I'd have to wait for each upload to return successfully before initiating the next, and then finally sending the object to my DB. Is there an efficient, code-clean for me to accomplish my goal?

指向亚马逊示例应用程序github的URL: https://github.com/awslabs/aws-sdk-ios-samples/tree/master/S3TransferUtility-Sample/Swift

URL to Amazon's sample app github: https://github.com/awslabs/aws-sdk-ios-samples/tree/master/S3TransferUtility-Sample/Swift

推荐答案

正如我在H. Al-Amri的回复中所说,如果您需要知道最后一次上传完成的时间,就不能简单地遍历一系列数据并一次全部上传.

As I said on H. Al-Amri's response, if you need to know when the last upload is complete you can't simply iterate through an array of data and upload them all at once.

在Javascript中,有一个库(我认为是Async.js),可以很轻松地对数组的各个元素进行后台操作,并在每个数组完成和整个数组完成时获取回调.由于我不了解Swift的其他功能,因此您直觉必须链接上传文件.

In Javascript there is a library (Async.js I think) that will make it easy to do background operations on individual elements of an array, and to get callbacks when each one finishes and when the entire array is finished. Since I'm not aware of anything like that for Swift, your intuition that you have to chain the uploads is correct.

正如@DavidTamrazov在评论中解释的那样,您可以使用递归将呼叫链接在一起.因为我的网络操作是使用NSOperationQueue链接NSOperation来完成的,所以解决此问题的方式稍微复杂一些.我将图像数组传递给自定义的NSOperation,该自定义的NSOperation从该数组上载第一张图像.完成时,它将另一个NSOperation与剩余图像数组添加到我的NSOperationsQueue中.当阵列中的图像用完时,我知道我已经完成了.

As @DavidTamrazov explained in comments, you can chain your calls together using recursion. The way I solved this problem was a little more complicated since my network operations are done using NSOperationQueue to chain NSOperations. I pass an array of images to a custom NSOperation that uploads the first image from the array. When it completes, it adds another NSOperation to my NSOperationsQueue with the array of remaining images. When the array runs out of images, I know I'm complete.

以下是我从一个更大的块中切出的示例.将其视为伪代码,因为我对其进行了大量编辑,甚至没有时间对其进行编译.但是希望在框架上已经足够清楚如何使用NSOperations做到这一点.

Following is an example I chopped out of a much much larger chunk I use. Treat it as pseudocode because I edited it heavily and didn't have time to even compile it. But hopefully it's clear enough on the framework for how to do this with NSOperations.

class NetworkOp : Operation {
    var isRunning = false

    override var isAsynchronous: Bool {
        get {
            return true
        }
    }

    override var isConcurrent: Bool {
        get {
            return true
        }
    }

    override var isExecuting: Bool {
        get {
            return isRunning
        }
    }

    override var isFinished: Bool {
        get {
            return !isRunning
        }
    }

    override func start() {
        if self.checkCancel() {
            return
        }
        self.willChangeValue(forKey: "isExecuting")
        self.isRunning = true
        self.didChangeValue(forKey: "isExecuting")
        main()
    }

    func complete() {
        self.willChangeValue(forKey: "isFinished")
        self.willChangeValue(forKey: "isExecuting")
        self.isRunning = false
        self.didChangeValue(forKey: "isFinished")
        self.didChangeValue(forKey: "isExecuting")
        print( "Completed net op: \(self.className)")
    }

    // Always resubmit if we get canceled before completion
    func checkCancel() -> Bool {
        if self.isCancelled {
            self.retry()
            self.complete()
        }
        return self.isCancelled
    }

    func retry() {
        // Create a new NetworkOp to match and resubmit since we can't reuse existing.
    }

    func success() {
        // Success means reset delay
        NetOpsQueueMgr.shared.resetRetryIncrement()
    }
}

class ImagesUploadOp : NetworkOp {
    var imageList : [PhotoFileListMap]

    init(imageList : [UIImage]) {
        self.imageList = imageList
    }

    override func main() {
        print( "Photos upload starting")
        if self.checkCancel() {
            return
        }

        // Pop image off front of array
        let image = imageList.remove(at: 0)

        // Now call function that uses AWS to upload image, mine does save to file first, then passes
        // an error message on completion if it failed, nil if it succceeded
        ServerMgr.shared.uploadImage(image: image, completion: {  errorMessage ) in
            if let error = errorMessage {
                print("Failed to upload file - " + error)
                self.retry()
            } else {
                print("Uploaded file")
                if !self.isCancelled {
                    if self.imageList.count == 0 {
                        // All images done, here you could call a final completion handler or somthing.
                    } else {
                        // More images left to do, let's put another Operation on the barbie:)
                        NetOpsQueueMgr.shared.submitOp(netOp: ImagesUploadOp(imageList: self.imageList))
                    }
                }
            }
            self.complete()
         })
    }

    override func retry() {
        NetOpsQueueMgr.shared.retryOpWithDelay(op: ImagesUploadOp(form: self.form, imageList: self.imageList))
    }
}


// MARK: NetOpsQueueMgr  -------------------------------------------------------------------------------

class NetOpsQueueMgr {
    static let shared = NetOpsQueueMgr()

    lazy var opsQueue :OperationQueue = {
        var queue = OperationQueue()
        queue.name = "myQueName"
        queue.maxConcurrentOperationCount = 1
        return queue
    }()

    func submitOp(netOp : NetworkOp) {
         opsQueue.addOperation(netOp)
    }

   func uploadImages(imageList : [UIImage]) {
        let imagesOp = ImagesUploadOp(form: form, imageList: imageList)
        self.submitOp(netOp: imagesOp)
    }
}

这篇关于从iOS将多个图像上传到S3的有效方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆