最近公司一个项目需要用到亚马逊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)
}