iOS開發關于“發送原圖”功能問題的記錄
本文主要記錄一個bug從發現、定位到延期解決的過程。文末添加了已踩過的坑
近期在做“發送原圖”功能的時候,遇到一個bug:在Android、Windows、Mac 客戶端發送原圖,iOS客戶端接收,保存原圖后,原圖物理尺寸不變,存儲空間變小,對應的location等Exif信息丟失。與此同時,iOS客戶端之間互發原圖沒有問題。針對這個問題,做了以下測試調研,現記錄下來:
一. 首先介紹一下發送一張原圖的流程:
- 比如 Android 端發送一張原圖,先上傳到 IM 的服務器,上傳成功后再發送消息體;(上傳成功后,服務器會分配三個url分別對應縮略圖、大圖、原圖)
- 接收方接收到消息體,下載縮略圖;
- 點擊縮略圖,下載大圖;
- 再點擊“查看原圖”按鈕,下載原圖;
- 下載成功后,長按圖片,保存原圖。
二. 問題定位是在***一步,保存圖片的部分:
- 下載的圖片大小與服務器存儲大小是完全一致的,保存之后大小就發生了變化(目前是“jpg”格式變小,“png”格式圖片變大);
- 下載的方式分別嘗試了 AFNetwoking 下載、SDWebImage 的普通下載和高級下載方式(因為產品需求要求顯示下載原圖進度)
- 結論下載的圖片大小與服務器存儲大小是完全一致的是因為在 SDWebImage 高級下載方法的 completionBlock 中有已下載的 bit 值
- 嘗試了能找到的各種保存圖片的方式,均不行
驗證測試:將安卓端產生的圖片(包括拍照“jpg”和屏幕截圖“png”)從瀏覽器下載到電腦,大小不變,將該圖片文件拖到項目中,執行保存圖片的方法,大小也發生了變化。
補充測試:安卓端拍攝一張圖片(大小為5M),發送給 iOS 客戶端(下載大小為5M),保存(大小為3M),再將該保存的圖片發給安卓客戶端(保存后為3M),安卓客戶端再發送給iOS 客戶端(保存后大小為3M)。結論:該壓縮只會進行一次
保存圖片后,圖片的Exif信息丟失,但是Exif信息的大小遠小于文件損失的大小。圖片物理尺寸沒有發生變化
三. 競品的該功能現狀:
- Android、Windows、Mac發送原圖,iOS客戶端接收
- 騰訊系(QQ、WeChat)各端發送接收原圖都沒有問題
- 釘釘 的圖片變小,Exif 信息丟失
- BearyChat 發送原圖的圖片變小,Exif 信息丟失
- Slack 只支持發送圖片,沒有發送原圖功能
四. 基于現有情況的分析
- iOS 系統在保存圖片的時候,會對圖片進行編解碼操作,可能在位圖上進行優化
- 一張圖片的存儲大小的計算方式:水平像素垂直像素1色黑白或3基色*顏色深度bit數 = MB數,可能是不同系統的基數不同導致
- 如果要解決這個問題,要先研究一下圖片編解碼這些很底層的東西,目前來看,性價比很低
- 如果您之前踩過類似的坑并找到有效的解決方法,方便的話,勞煩請私信告知
五. 補充點干貨
1. 關于iOS11新增的“.heic”格式圖片
什么是“.heic”格式圖片?
之前叫“live”圖片,打開下圖紅框中的按鈕即可打開該模式,拍照后會截取拍照前后大概兩秒的一個片段,與“Gif”圖不同的是,該格式還包含了聲音(目前只有)
什么樣的手機才能拍出“.heic”格式圖片?
只有在 iOS11系統下且CPU為A10及其以上(***也得是iPhone 7),其他情況下拍出來的都是普通“live”圖,即在需要轉換格式的時候會自動轉換為“.jpg/.jpeg”格式
如何判斷一張圖片是否是“.heic”格式?
SDWebImage-NSData+ImageContentType.m 已更新,剛開始遇到這個問題的時候提了個issue,還讓我提供對應url。。
- + (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;
- }
對于“.heic”格式圖片我們應該怎么處理?
- 首先肯定不是服務器去支持這個類型,因為 Windows、Android 是不支持該類型的圖片正常顯示的,尤其是 Windows 明確表示以后也不會支持。
- 微信目前的處理方式是轉換成了 jpg 格式,因此直接使用 UIImageJPEGRepresentation(originalImage, 0.82); 轉換為jpg即可
- 但是經多次測試后發現,必須設置壓縮比為0.82,轉換后的大小才盡可能的接近原圖大小