美文网首页
iOS 图片无损存取 无损保存到相册

iOS 图片无损存取 无损保存到相册

作者: Yuency | 来源:发表于2018-09-20 19:59 被阅读128次

    前言:

    十年前的一个中春,我出去面试了。有一家公司坐落在一个还行的地方,公司周围环境非常迷人。面试我的是一个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 - 所以,我认为,图片无损保存并取出了。

    MD5 校验.jpeg

    在实验的过程中,遇到了各种各样的问题。最让人开心的,还是 SUCCESS。

    偶然发现这位玩家的文章,殊途同归。

    iOS系统相册基本操作 - 简书

    iOS8 Photos Framework

    Photo Frameworks之PHAssetCollection、PHCollectionList和PHAsset

    用Photos框架保存图片到相册

    iOS9中将图片保存到照片中的某个相册的方法说明

    iOS swift实现图片不重复保存至相册

    iOS8之后, 保存图片到本地相册, 避免重复保存同一张

    第一次体验项目上线通宵加班

    相关文章

      网友评论

          本文标题:iOS 图片无损存取 无损保存到相册

          本文链接:https://www.haomeiwen.com/subject/qyybnftx.html