人猿星球iOS开发进阶iOS开发

关于“发送原图”功能问题的记录

2017-11-03  本文已影响670人  si1ence

本文主要记录一个bug从发现、定位到延期解决的过程。文末添加了已踩过的坑

微信中发送原图样式

近期在做“发送原图”功能的时候,遇到一个bug:在Android、Windows、Mac 客户端发送原图,iOS客户端接收,保存原图后,原图物理尺寸不变,存储空间变小,对应的location等Exif信息丢失。与此同时,iOS客户端之间互发原图没有问题。针对这个问题,做了以下测试调研,现记录下来:

一. 首先介绍一下发送一张原图的流程:

  1. 比如 Android 端发送一张原图,先上传到 IM 的服务器,上传成功后再发送消息体;(上传成功后,服务器会分配三个url分别对应缩略图、大图、原图)
  2. 接收方接收到消息体,下载缩略图;
  3. 点击缩略图,下载大图;
  4. 再点击“查看原图”按钮,下载原图;
  5. 下载成功后,长按图片,保存原图。

二. 问题定位是在最后一步,保存图片的部分:

// 方法一:<Photos/Photos.h>框架,9.0以上系统支持,保存图片
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
     placeholder = [PHAssetChangeRequest creationRequestForAssetFromImage:image].placeholderForCreatedAsset;
} error:&error];

// 方法二:将 UIImage 转换为 NSData,再进行保存
// 该方法保存后的图片会变大很多,有个比较low的方法是进行压缩到正常大小
// 至于Exif信息,有的人认为泄漏了隐私,有的人坚持要带上,Exif的获取及写入在这里不再赘述
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    [[PHAssetCreationRequest creationRequestForAsset] addResourceWithType:PHAssetResourceTypePhoto data:UIImageJPEGRepresentation(image, 1) options:nil];
} completionHandler:^( BOOL success, NSError *error ) {
    if ( ! success ) {
        NSLog( @"Error occurred while saving image to photo library: %@", error );
    }
}];

// 方法三:UIImageWriteToSavedPhotosAlbum 直接保存
UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

// 方法四:
- (void)saveImage:(UIImage *)image toAlbum:(NSString *)album completionHandler:(STAlbumSaveHandler)completionHandler;

// 方法五:9.0之前的版本适用方法:将 UIImage 转换为 NSData,再进行保存
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageDataToSavedPhotosAlbum:UIImageJPEGRepresentation(image, 1) metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) {
    NSLog(@"Success at %@", [assetURL path] );
}] ;

三. 竞品的该功能现状:

Android、Windows、Mac发送原图,iOS客户端接收

四. 基于现有情况的分析

如果您之前踩过类似的坑并找到有效的解决方法,方便的话,劳烦请私信告知(被同事们吐槽好久:没解决问题还写鸡毛博客,我不会告诉他们我是为了第一张图才写的这篇博客,大一的时候看了一眼便再也难以忘怀。。)

五. 补充点干货

1. 关于iOS11新增的“.heic”格式图片
+ (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
    if (!data) {
        return SDImageFormatUndefined;
    }
    
    // File signatures table: http://www.garykessler.net/library/file_sigs.html
    uint8_t c;
    [data getBytes:&c length:1];
    switch (c) {
        case 0xFF:
            return SDImageFormatJPEG;
        case 0x89:
            return SDImageFormatPNG;
        case 0x47:
            return SDImageFormatGIF;
        case 0x49:
        case 0x4D:
            return SDImageFormatTIFF;
        case 0x52: {
            if (data.length >= 12) {
                //RIFF....WEBP
                NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
                if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
                    return SDImageFormatWebP;
                }
            }
            break;
        }
        case 0x00: {
            if (data.length >= 12) {
                //....ftypheic ....ftypheix ....ftyphevc ....ftyphevx
                NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(4, 8)] encoding:NSASCIIStringEncoding];
                if ([testString isEqualToString:@"ftypheic"]
                    || [testString isEqualToString:@"ftypheix"]
                    || [testString isEqualToString:@"ftyphevc"]
                    || [testString isEqualToString:@"ftyphevx"]) {
                    return SDImageFormatHEIC;
                }
            }
            break;
        }
    }
    return SDImageFormatUndefined;
}

+ (nonnull CFStringRef)sd_UTTypeFromSDImageFormat:(SDImageFormat)format {
    CFStringRef UTType;
    switch (format) {
        case SDImageFormatJPEG:
            UTType = kUTTypeJPEG;
            break;
        case SDImageFormatPNG:
            UTType = kUTTypePNG;
            break;
        case SDImageFormatGIF:
            UTType = kUTTypeGIF;
            break;
        case SDImageFormatTIFF:
            UTType = kUTTypeTIFF;
            break;
        case SDImageFormatWebP:
            UTType = kSDUTTypeWebP;
            break;
        case SDImageFormatHEIC:
            UTType = kSDUTTypeHEIC;
            break;
        default:
            // default is kUTTypePNG
            UTType = kUTTypePNG;
            break;
    }
    return UTType;
}

最后,说点正经的,这是一篇每个iOS开发人员都应该了解的文章 iOS11/iPhone X 适配简单,但你的Apple思维适配做好了么?

再说点不正经的,如果你很愉悦的看完了本文并且学到了知识,那么这篇文章你应该也会喜欢NavigationController已经洗干净了,就等你来

上一篇下一篇

猜你喜欢

热点阅读