前言:
十年前的一个中春,我出去面试了。有一家公司坐落在一个还行的地方,公司周围环境非常迷人。面试我的是一个30岁的女人。以下称之为老板娘。
老板娘应该是项目经理或者主管之类的。老板娘看我建立上写的有做过壁纸 App 的经验,就让我过来面试了。
老板娘家的 App 是一个摄影类的项目,Swift 编写,分为两个端,普通用户端和摄影师端。摄影师拍照完了之后上传到服务器,然后用户在 App 里可以看到。用户可以把图片下载到手机然后拿出来去冲印。然后,问题来了。
这样的照片当然要是高清的。HD BD Blue-Ray 1080P.....
老板娘发现,从手机里拿出来的图片,都是被压缩过了的。比如,摄影师上传的图片是 3M, 在 App 里把图片保存到相册, 然后从相册里拿出来,就变成了 998KB。
老板娘看起来很不满意的样子,然后她说, 百度云盘就 OK, 原图多大拿出来就是多大,问我解决方案。
啊。那个时候我也是图样图森破。当场没有给出解决方案。
后来我回去了。花了一天时间吧,把这个难题解决掉了。我给老板娘写邮件,把我的研究结果发给她,而老板娘,却从未回复我。
十年之后,我想起了这件事情。我翻开自己的邮箱,找到自己的代码,Command + R。 Fuck,代码竟然不能正常工作了。
你为什么要在十年的今天再次让我费心?你这莎比的代码。
代码地址: https://github.com/gityuency/ObjectiveCTools
类名:【PhotoLosslessSaveViewController】
第一步 Command + C
XIB 中拖放了一个 UIIMageView, UIBUtton, UILabel, 为了是便于观察实验现象。
//
// PhotoLosslessSaveViewController.m
// ObjectiveCTools
//
// Created by 十年前的春天 on 2018/9/20.
// Copyright © 2018年 姬友大人. All rights reserved.
//
#import "PhotoLosslessSaveViewController.h"
//引入框架 系统最低版本要求8.0
#import <Photos/Photos.h>
@interface PhotoLosslessSaveViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UILabel *labelInfo;
@end
@implementation PhotoLosslessSaveViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
/// 点击按钮
- (IBAction)buttonAction:(UIButton *)sender {
// 先去 info.plit 开启相册权限,否则真机崩溃
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusAuthorized) {
[self startCoquettishOperation];
} else {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if(status == PHAuthorizationStatusAuthorized) {
dispatch_async(dispatch_get_main_queue(), ^{
// 用户点击 "OK"
[self startCoquettishOperation];
});
} else {
// 用户点击 不允许访问
self.labelInfo.text = @"玩你妈嗨";
}
}];
}
}
/// 下载图片,保存到相册
- (void)startCoquettishOperation {
//这里使用你的 App 名字作为相册的名字
NSString *myAlbumName = [NSBundle mainBundle].infoDictionary[@"CFBundleDisplayName"];
NSArray *imageArray = @[
//1993951字节 jpg格式
@"http://desk.fd.zol-img.com.cn/t_s1920x1080c5/g5/M00/0E/03/ChMkJ1jLslKISLngAB5s3zryFm0AAa0-gKh6oUAHmz3642.jpg",
//428397字节 jpg格式
@"http://desk.fd.zol-img.com.cn/t_s1920x1080c5/g5/M00/03/0C/ChMkJlekgpaIdfThAAUBS2JU_LIAAUMOwIWjK0ABQFj984.jpg",
//6525 字节, png 格式
@"http://d.lanrentuku.com/down/png/1610/creativetail-12-objects-icons-v1/tedy_bear_128px.png",
//714,940 字节
@"https://desk-fd.zol-img.com.cn/t_s2880x1800c5/g5/M00/03/02/ChMkJ1snEmSIOxuyACDtz-3LcyoAApHBQNTWKcAIO3n284.jpg",
//669,742 字节
@"https://desk-fd.zol-img.com.cn/t_s2880x1800c5/g5/M00/01/06/ChMkJlqVBu6IZnpeABHX83UjQXsAAlAVgEko9gAEdgL529.jpg",
//645,708 字节(磁盘上的 647 KB)
@"https://desk-fd.zol-img.com.cn/t_s2880x1800c5/g5/M00/0E/00/ChMkJ1nJ4yKIOsEEAGL_lEchWXoAAgzFwLR_9IAYv-s222.jpg",
];
//从以上图片地址数组找个图片吧
NSString *imageUrl = imageArray[1];
//创建图片文件夹路径, 真机不允许直接使用 Documents 目录
NSString *newFloderPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/MyImages"];
//构造图片路径
NSString *imageName = [imageUrl lastPathComponent];
NSString *imagePath = [newFloderPath stringByAppendingPathComponent:imageName];
//文件管理器
NSFileManager *manager = [NSFileManager defaultManager];
//创建文件夹
BOOL wenjianjia = [manager createDirectoryAtPath:newFloderPath withIntermediateDirectories:NO attributes:nil error:nil];
if (wenjianjia) {
NSLog(@"SUCCESS 文件夹创建成功! %@",newFloderPath);
}
//创建文件
BOOL createSuccess = [manager createFileAtPath:imagePath contents:nil attributes:nil];
if (createSuccess) {
NSLog(@"SUCCESS 文件创建成功! %@", imagePath);
}
//保存图片
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
BOOL writeSuccess = [imageData writeToFile:imagePath atomically:YES];
if (writeSuccess) {
NSLog(@"SUCCESS 写入成功! %@", imagePath);
}
self.imageView.image = [UIImage imageWithData:imageData];
/*
PHAssetCollectionTypeAlbum = 1, 这个东西是拿到 非 系统创建的相册, 可能是你创建的,可能是其他应用创建的
PHAssetCollectionTypeSmartAlbum = 2, 这个是系统创建的相册名字, 不管你在手机的相册 App 里面有没有出现对应的相册名, 这个东西都能拿到相册名
PHAssetCollectionTypeMoment 这个拿到的是默认相册里面的分组, 比如在模拟器上初始的相册, 个数就是 6
*/
/*
fetchAssetCollectionsWithType: 参数填写: PHAssetCollectionTypeAlbum,表示要拿到非系统创建的相册,
subtype: 参数填写 PHAssetCollectionSubtypeAny, 表示要拿到所有的相册.
如果在这里 不填 PHAssetCollectionTypeAlbum 而填写其他两个 枚举值, 就会发生 Error Domain=NSCocoaErrorDomain Code=-1 "(null)" 的错误
*/
PHFetchResult<PHAssetCollection *> *collections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:nil];
NSLog(@"相册个数: %lu", (unsigned long)collections.count);
PHAssetCollection *myCollection = nil;
for (PHAssetCollection *collection in collections ) {
NSLog(@"相册名字: %@ 子类型: %ld", collection.localizedTitle , (long)collection.assetCollectionSubtype);
if ([collection.localizedTitle isEqualToString:myAlbumName]) {
myCollection = collection;
break;
}
}
NSURL *imageURL = [NSURL fileURLWithPath:imagePath];
if (myCollection) {
//保存到已有相册
[self saveMyPhotoTo:myCollection imageURL:imageURL];
} else {
//保存到新创建的相册
[self saveMyPhotoToNew:myAlbumName imageURL:imageURL];
}
}
//创建和 APP 同名的相册 然后再保存 (case: 这个用户某天把我创建的这个相册整个删除,然后又往里添加照片, 第一次就会添加失败,解决这样的 BUG)
- (void)saveMyPhotoToNew:(NSString *)albumName imageURL:(NSURL *)imageURL {
[[PHPhotoLibrary sharedPhotoLibrary]performChanges:^{
PHAssetCollectionChangeRequest *changeCollectionRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:albumName];
PHAssetChangeRequest *changeAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:imageURL];
PHObjectPlaceholder *assetPlaceholder = [changeAssetRequest placeholderForCreatedAsset];
[changeCollectionRequest addAssets:@[assetPlaceholder]];
} completionHandler:^(BOOL success,NSError * _Nullable error) {
success == YES ? NSLog(@"SUCCESS 添加到新创建的相册!") : NSLog(@"爆炸: %@",error);
dispatch_async(dispatch_get_main_queue(), ^{
self.labelInfo.text = @"成功添加到 新 创建的相册!";
});
}];
}
// 添加到已经存在的相册里面
- (void)saveMyPhotoTo:(PHAssetCollection *)myCollection imageURL:(NSURL *)imageURL {
[[PHPhotoLibrary sharedPhotoLibrary]performChanges:^{
PHAssetCollectionChangeRequest *changeCollectionRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:myCollection];
PHAssetChangeRequest *changeAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImageAtFileURL:imageURL];
PHObjectPlaceholder *assetPlaceholder = [changeAssetRequest placeholderForCreatedAsset];
[changeCollectionRequest addAssets:@[assetPlaceholder]];
} completionHandler:^(BOOL success,NSError * _Nullable error) {
success == YES ? NSLog(@"SUCCESS 添加到已经存在的相册!") : NSLog(@"爆炸: %@",error);
dispatch_async(dispatch_get_main_queue(), ^{
self.labelInfo.text = @"成功添加到 已有 相册!";
});
}];
}
@end
第二步 Command + R
考虑到用户相册授权,第一次添加,相册删除之后继续添加,这种种的情况,代码都做了完善。
代码唯一的漏洞是,放在沙盒里的图片我没有及时删掉,为什么? 因为懒得去写那几行代码。
下面解释一下,实验的步骤:
1 - 在网页上找个图片
2 - 把图片下载到你的桌面
3 - 打开你的 “终端”
4 - 先输入 “MD5” 然后把你的图片拖进终端
5 - 回车,查看 MD5 值。
6 - 第一步中的代码运行完毕之后,在手机里打开你的相册。
7 - 在相册中找到和你 App 同名的那个相册,点击图片进行查看。
8 - 在屏幕下方会出现工具栏,最左边有个“分享”样式的系统按钮
9 - 点击这个按钮, 通过 AirDrop 的方式把这个图片传到你的电脑,放到桌面。
10 - 重复 步骤 2~5 查看这个从手机里拿出来的照片的 MD5值。
11 - 你会发现,MD5 值是一样的。
12 - 所以,我认为,图片无损保存并取出了。
在实验的过程中,遇到了各种各样的问题。最让人开心的,还是 SUCCESS。
偶然发现这位玩家的文章,殊途同归。
Photo Frameworks之PHAssetCollection、PHCollectionList和PHAsset
第一次体验项目上线通宵加班
网友评论