最近在處理移動端選擇圖片實時預覽並上傳時遇到一個問題:上傳前圖片預覽正常,但上傳到服務器上的圖片展示到頁面上時,有時會出現圖片翻轉的問題,一般是翻轉 90 度。后經一翻研究找到了問題所在,特在此記錄一下。
問題描述
接上面提到的問題,經過一些測試,發現:如果選取的圖片是在橫屏狀態下拍攝的,則上傳后不會出現圖片翻轉的問題;反之,如果是在豎屏狀態下拍攝的,上傳后就會出現圖片翻轉的問題。
問題分析
首先,圖片在本地預覽時正常,而且前端最后提交到后端的數據是用 FormData
來封裝處理的,用 FormData
可以保證提交的數據和通常的表單提交沒有什么區別,對后端來說是透明的,后端的處理邏輯不用修改就可以處理帶文件的 Ajax 提交。因此,我猜想問題可能出在了后端。
但是后端也沒有做什么特殊的處理,怎么展示的時候圖片就翻轉了呢?
OK,前面提到過這個問題和手機的拍照模式有關,豎屏下拍的照片會重現這個問題,橫屏下的則不會。難道這兩種模式下拍攝的照片不一樣?照片中保存的數據不一樣?,這不由的使我想到了 [Exif][] 這個名詞,雖然我對它不是十分了解,但是印象中它是用來保存照片的一些元數據信息的,也許和它有關。
簡單的一搜索發現:Exif[orientation]有一篇關於它的詳細的介紹,此處不展開細講。簡單來說就是它有 8 個值,用來表示照片拍攝時相機旋轉的狀態,而且我們可以通過編程的方式讀取它。我們平常用電腦來查看手機拍攝的照片時也會偶爾出現圖片翻轉的情況,其原因也是照片查看軟件沒有根據這個值將照片自動翻轉。
經過搜索,在 SO 上找到了相關的問題。其思路就是讀取照片的 Exif 信息,判斷 Orientation 的值,然后將圖片進行相應的旋轉。問題已確定,后端同學很快就將這個問題給修復了,並且添加到了他們的公共模塊中。
你以為問題就這樣解決了,其時並沒有。
后續
過了幾天,公司購置了幾台全新的測試機,測試同學將系統在一台三星的機子上一測,又發現新問題了:選擇完圖片進行本地預覽時,發現圖片翻轉了!但上傳后再展示又是正常的。WTF!
這次沒得說了,問題肯定出在了前端預覽上。但是之前在安卓和 iOS 的設備上測試都是正常的呀,難道和機型有關?問了一下測試,得知這台三星用的是 Android 5.0 的系統,好先進有木有!這難道是 5.0 中引入的一個問題?別想這些了,反正我們也不可能在系統層面去修復這個問題,還是想想如何去兼容 5.0 吧。
當時想到了兩種方法,一種是選擇圖片后先上傳到服務器,再展示上傳的圖片進行預覽。這種方法的優點是兼容性好,前端處理相對簡單;缺點是后端要提供圖片上傳的接口,並且如果用戶在保存之前更換圖片並不會刪除之前上傳的圖片。移動端的微博配圖就是用的這種方法。
另一種方法是在本地進行圖片翻轉。我們可以使用 FileReader
API 來讀取圖片,之后像后端那樣分析 Exif 信息,旋轉圖片。這種做法的優點是,預覽操作完全在本地完成,不會產生不必要的文件上傳。缺點是 FileReader
在 Android 4.1 及以上的機型才可以使用,加上前端處理文件數據可能稍顯復雜,另外性能也是個問題。
但是最終我們選擇了第二種方案。原因是我們之前的照片預覽就是用的 FileReader
API,不需要考慮低版本安卓的兼容性,這和項目的實際情況有關。另一個原因是我們認為也許有前人已經遇到過這種問題,也許已經有了比較好的處理這個問題的功能模塊。后經搜索,找到了一個 fix-orientation
模塊,可以通過 npm i fix-orientation --save
使用,具體的用法可以參考項目的文檔。后來,掃了一眼它的代碼,發現它是用 canvas 來實現圖片的旋轉,並不會對正常的圖片進操作,性能問題不大,iOS 上秒開,安卓設備上稍長,可以添加 loading 效果緩解一下。
至此,問題完美解決。
另外,像 fix-orientation
這樣小而美的模塊有很多,大家平時可以多關注一下。