iOS-Swift-AWSS3备份相册图片和视频

最近公司一个项目需要用到亚马逊AWS来做云存储功能,由于几个端的人员都不在同一个地方且网上AWSS3来做图片视频备份的资料几乎没有,只能是一点一点和后台人员沟通并在官网上摸索。
我这边只负责iOS端,由后台负责登录后提供OSS配置,所以关于亚马逊账号管理设置就不在下面描述了,这部分网上相关资料是蛮多的。

总结所有步骤

1.导入AWSS3
2.获取OSS配置后设置AWSServiceConfiguration
3.判断相册权限,获取相册所有照片或视频
4.将PHAsset转成Data(这一步一定要注意,要考虑上万张图片情况,具体会在下面描述)
5.使用AWSS3开始上传(上传成功后假如需要刷新页面显示时,一定不要把所有记录全部显示出来,会造成卡顿并内存暴涨崩溃)
6.通过本地存储的上传记录获取相册中的图片和视频用于UI显示
1.导入AWSS3
pod 'AWSCore'
pod 'AWSCognito'
pod 'AWSS3'
2.获取OSS配置后设置AWSServiceConfiguration
PD_appDelegate.bucketName = dataDic.dictionaryForKey("bucket").stringForKey("bucketName")
let accessKey = dataDic.stringForKey("accessKey")
let secretKey = dataDic.stringForKey("secretKey")
let endPointKey = dataDic.stringForKey("endpoint")
            
let credentialsProvider = AWSStaticCredentialsProvider(accessKey: accessKey, secretKey: secretKey)
let endPoint = AWSEndpoint(urlString: endPointKey)
let configuration = AWSServiceConfiguration(region: .USEast1, endpoint: endPoint, credentialsProvider: credentialsProvider)
            
AWSServiceManager.default().defaultServiceConfiguration = configuration
3.判断相册权限,获取相册所有照片或视频
class AuthorizationManager {
    /// 相机权限
    public static func camera(authorizedBlock: CustomNoParamTypealias?) {
        
        let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
        
        // .notDetermined  .authorized  .restricted  .denied
        if authStatus == .notDetermined {
            // 第一次触发授权 alert
            AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) in
                self.camera(authorizedBlock: authorizedBlock)
            })
            
        //打开相机
        } else if authStatus == .authorized {
            if authorizedBlock != nil {
                authorizedBlock!()
            }
            
        //没有权限使用相机
        } else {
            DispatchQueue.main.async {
                CommonUntils.showAlertController("权限未开启", "请在系统定位中开启相机权限", "去设置", "取消", { (alertAction) in
                    
                    let url = URL(string: UIApplication.openSettingsURLString)
                    UIApplication.shared.openURL(url!)
                })
            }
        }
    }
    
    /// 相册权限
    public static func photoAlbum(authorizedBlock: CustomNoParamTypealias?) {
        
        let authStatus = PHPhotoLibrary.authorizationStatus()
        
        // .notDetermined  .authorized  .restricted  .denied
        if authStatus == .notDetermined {
            // 第一次触发授权 alert
            PHPhotoLibrary.requestAuthorization { (status:PHAuthorizationStatus) -> Void in
                self.photoAlbum(authorizedBlock: authorizedBlock)
            }
            
        //打开相册
        } else if authStatus == .authorized  {
            if authorizedBlock != nil {
                authorizedBlock!()
            }
            
        //没有权限打开相册
        } else {
            DispatchQueue.main.async {
                CommonUntils.showAlertController("权限未开启", "请在系统定位中开启相册权限", "去设置", "取消", { (alertAction) in
                    
                    let url = URL(string: UIApplication.openSettingsURLString)
                    UIApplication.shared.openURL(url!)
                })
            }
        }
    }
}
//获取相册所有照片或视频
class AlbumManager {
    //单例
    static let shared = AlbumManager()
    
    //MARK:-获取相册所有图片
    func getAllPhoto(photoHandler: @escaping (_ assetArr: [PHAsset]) -> Void) {
        weak var weakSelf = self
        AuthorizationManager.photoAlbum {
            let assetArr: [PHAsset] = weakSelf!.getCameraRoll()
            photoHandler(assetArr)
        }
    }
    
    //MARK:-获取相册所有视频
    func getAllVideo(videoHandler: @escaping (_ assetArr: [PHAsset]) -> Void) {
        weak var weakSelf = self
        AuthorizationManager.photoAlbum {
            let assetArr: [PHAsset] = weakSelf!.getAlbumVideos()
            videoHandler(assetArr)
        }
    }
}

//MARK:*******************获取相册所有图片相关代码*******************
extension AlbumManager {
    //MARK:-先获取所有相册,然后获取相机胶卷
    private func getCameraRoll() -> [PHAsset] {
        // 所有智能相册集合(系统自动创建的相册)
        let smartAlbums: PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil)
        
        //遍历得到每一个相册
        var photoAlbum: PHAssetCollection?
        for i in 0 ..< smartAlbums.count {
            //相册的名字是相机交卷,这里面包含了所有的资源,包括照片、视频、gif。 注意相册名字中英文
            if smartAlbums[i].localizedTitle == "相机胶卷" ||
                smartAlbums[i].localizedTitle == "Camera Roll" ||
                smartAlbums[i].localizedTitle == "最近项目" ||
                smartAlbums[i].localizedTitle == "Recents"{
                photoAlbum = smartAlbums[i]
            }
        }
        if photoAlbum == nil { return [PHAsset]() }
        
        return getPHAssetsFromAlbum(photoAlbum!)
    }
    
    //MARK:-获取相机胶卷的PHAsset集合(只选照片)
    private func getPHAssetsFromAlbum(_ collection: PHAssetCollection) -> [PHAsset] {
        // 存放所有图片对象
        var assetArr: [PHAsset] = []
        
        //时间排序、只选照片
        let options = PHFetchOptions.init()
        options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
        options.predicate = NSPredicate.init(format: "mediaType == %ld", PHAssetMediaType.image.rawValue)
        
        // 获取所有图片资源对象
        let results: PHFetchResult = PHAsset.fetchAssets(in: collection, options: options)
        
        // 遍历,得到每一个图片资源asset,然后放到集合中
        results.enumerateObjects { (asset, index, stop) in
            assetArr.append(asset)
        }
        
        return assetArr
    }
    
    //MARK:-根据PHAsset获取原图片信息
    func getImageDataFromPHAsset(_ asset: PHAsset, photoHandler: @escaping (_ imageData: Data?) -> Void) {
        
        let option: PHImageRequestOptions = PHImageRequestOptions.init()
        option.deliveryMode = PHImageRequestOptionsDeliveryMode.opportunistic
        option.resizeMode = PHImageRequestOptionsResizeMode.fast
        
        PHImageManager.default().requestImageData(for: asset, options: option) { (imageData, imageName, info, parameter) in
            photoHandler(imageData)
        }
    }
}

//MARK:*******************获取相册所有视频相关代码*******************
extension AlbumManager {
    //MARK:-先获取所有相册,然后获取视频相册
    private func getAlbumVideos() -> [PHAsset] {
        // 所有智能相册集合(系统自动创建的相册)
        let smartAlbums: PHFetchResult = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.smartAlbum, subtype: PHAssetCollectionSubtype.smartAlbumVideos, options: nil)
        
        //遍历得到每一个相册
        var photoAlbum: PHAssetCollection?
        for i in 0 ..< smartAlbums.count {
            //相册的名字是相机交卷,这里面包含了所有的资源,包括照片、视频、gif。 注意相册名字中英文
            if smartAlbums[i].localizedTitle == "视频" || smartAlbums[i].localizedTitle == "Videos" {
                photoAlbum = smartAlbums[i]
            }
        }
        if photoAlbum == nil { return [PHAsset]() }
        
        return getVideosFromAlbum(photoAlbum!)
    }
    
    //MARK:-获取视频相册的PHAsset集合(只选视频)
    private func getVideosFromAlbum(_ collection: PHAssetCollection) -> [PHAsset] {
        // 存放所有图片对象
        var assetArr: [PHAsset] = []
        
        //时间排序、只选照片
        let options = PHFetchOptions.init()
        options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
        options.predicate = NSPredicate.init(format: "mediaType == %ld", PHAssetMediaType.video.rawValue)
        
        // 获取所有图片资源对象
        let results: PHFetchResult = PHAsset.fetchAssets(in: collection, options: options)
        
        // 遍历,得到每一个图片资源asset,然后放到集合中
        results.enumerateObjects { (asset, index, stop) in
            assetArr.append(asset)
        }
        
        return assetArr
    }
    
    //MARK:-根据PHAsset获取视频信息
    func getVideoDataFromPHAsset(_ asset: PHAsset, videoHandler: @escaping (_ videoData: Data) -> Void) {
        
        let option: PHVideoRequestOptions = PHVideoRequestOptions.init()
        option.deliveryMode = .automatic
        option.isNetworkAccessAllowed = true
        
        PHImageManager.default().requestAVAsset(forVideo: asset, options: option) { (avasset, audioMix, info) in

            guard let avURLAsset = avasset as? AVURLAsset else {
                return
            }
            guard let data = try? Data.init(contentsOf: avURLAsset.url) else {
                return
            }
            videoHandler(data)
        }
    }
}
4.将上万的PHAsset转成Data(这一步我大概描述一下操作,需求不一样需要做的操作也不一样)

-获取已上传的文件名称或者图片localIdentifier也行(我这边是本地保存上传记录)

    //MARK:-获取相册所有图片并去除已上传的图片
    func getUploadPhotoAssetArr( _ customTypealias: @escaping (_ uploadAssetArr: [PHAsset]) -> Void) {
        
        //获取已上传的文件名称
        var alreadyUploadNameArr: [String] = []
        let dataArr = CommonValue.getHomeData()
        for i in 0 ..< dataArr.count {
            let name = dataArr.dictionaryWithIndex(i).stringForKey("titleStr")
            alreadyUploadNameArr.append(name)
        }
        
        AlbumManager.shared.getAllPhoto { (assetArr) in
            
            //用于统计真正需要上传的图片
            var uploadAssetArr: [PHAsset] = []
            
            for i in 0 ..< assetArr.count {
                
                //获取文件名称,并判断是否已上传
                guard let filename = assetArr[i].value(forKey: "filename") as? String , !alreadyUploadNameArr.contains(filename) else {
                    continue
                }
                uploadAssetArr.append(assetArr[i])
            }
            customTypealias(uploadAssetArr)
        }
    }

-获取到需要上传的图片后发通知刷新UI、开启队列、将要上传的文件数组分隔成等份的子数组(我这里设置成50,可以看情况来设置)

//获取相册所有需要上传的图片
getUploadPhotoAssetArr { (assetArr) in
    if assetArr.count == 0 {
        MBProgressHUD.showError("当前暂无可备份图片")
        return
    }
    weakSelf?.allUploadPhotoCount = assetArr.count
    
    //发送通知,开启任务页面的上传统计
    DispatchQueue.main.async {
        NotificationCenter.default.post(name: Notification.Name(StartUploadPhotoKey), object: nil)
    }

    //定义队列
    weakSelf?.uploadAllPhotoQueue = DispatchQueue.init(label: "uploadAllPhoto")
    
    //将要上传的文件数组分隔成等份的子数组
    var currentStart: Int = 0
    var currentEnd: Int = weakSelf!.groupPhotoAssetNum

    while currentStart < assetArr.count {
        currentEnd = min(currentStart + weakSelf!.groupPhotoAssetNum, assetArr.count)
        
        let subArr = Array(assetArr[currentStart ..< currentEnd]) as [PHAsset]
        weakSelf?.groupPhotoAssetArr.append(subArr)

        currentStart = currentEnd
    }
    weakSelf?.separatePhotoPHAsset()
}

-将分组后的PHAsset转成Data(每次转50张照片),然后分组用队列执行上传(这一步就不详细描述了)

5.使用AWSS3开始上传
    //MARK:-上传文件
    func uploadFile(_ fileData: Data?, _ key: String, _ contentType: String, _ asset: PHAsset? = nil, customTypealias: (() -> Void)? = nil) {
        if fileData == nil { return }
        
        let tempStr = "uploadingFile\(key.split(s: ".").first!)"
        //写入本地
        let fileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(tempStr)
        try? fileData?.write(to: fileURL)
        
        let request = AWSS3TransferManagerUploadRequest()!
        request.bucket = PD_appDelegate.bucketName
        request.key = key
        request.body = fileURL
        request.contentType = contentType
        
        let transferManager = AWSS3TransferManager.default()
        transferManager.upload(request).continueWith(executor: AWSExecutor.mainThread()) { (task) -> Any? in

            if task.result != nil {
                DLog(message: "****\(key)上传成功****")
            }
            //删除本地文件
            FileManager.removeFile("\(NSTemporaryDirectory())/\(tempStr)")
            
            //上传数据记录本地
            let dataArr = NSMutableArray(array: CommonValue.getUploadFileData())
            let dic = NSMutableDictionary()
            dic.setValue(key, forKey: "titleStr")
            dic.setValue(Date().toString("yyyy-MM-dd HH:mm"), forKey: "timeStr")
            //图片才需要存储
            if contentType == "image/png" {
                dic.setValue(asset!.localIdentifier, forKey: "localIdentifier")
                if asset == nil {
                    dic.setValue(fileData, forKey: "imageData")
                }
            }
            dataArr.insert(dic, at: 0)
            CommonValue.setUploadFileData(dataArr)

            //发送通知,让首页和任务页刷新数据并显示
            DispatchQueue.main.async {
                NotificationCenter.default.post(name: Notification.Name(RefreshUploadDataKey), object: dic)
            }
        
            if customTypealias != nil {
                customTypealias!()
            }
            return nil
        }
        sleep(1)
    }
6.通过本地存储的上传记录获取相册中的图片和视频用于UI显示(使用上面上传成功后存储本地的localIdentifier来获取)
if newValue!.localIdentifier == "" { return }

//通过localIdentifier从本地相册获取图片
guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [newValue!.localIdentifier], options: nil).firstObject else {
    return
}
weak var weakSelf = self
AlbumManager.shared.getImageDataFromPHAsset(asset) { (data) in
    if data == nil { return }
    
    newValue!.imageData = data!
    weakSelf?.imageB_1.setBackgroundImage(UIImage(data: newValue!.imageData), for: .normal)
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容