利用exif.js解決ios或Android手機上傳豎拍照片旋轉90度問題


html5+canvas進行移動端手機照片上傳時,發現ios手機上傳豎拍照片會逆時針旋轉90度,橫拍照片無此問題;Android手機沒這個問題。

因此解決這個問題的思路是:獲取到照片拍攝的方向角,對非橫拍的ios照片進行角度旋轉修正。

利用exif.js讀取照片的拍攝信息,詳見  http://code.ciaoca.com/javascript/exif-js/

這里主要用到Orientation屬性。

Orientation屬性說明如下:

 

旋轉角度 參數
1
順時針90° 6
逆時針90° 8
180° 3

 

下面就直接上代碼了。

主要有html5頁面和一個js,示例功能包含了圖片壓縮和旋轉。

自己寫的是uploadImage.js。

html5測試頁面如下:

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8">  
    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />  
    <title>圖片上傳</title>  
    <script type="text/javascript" src="js/jquery-1.8.3.js"></script>  
    <script type="text/javascript" src="js/uploadPicture/uploadImage.js" ></script>  
        <script type="text/javascript" src="js/exif.js" ></script>  
    <script>  
  
    </script>  
</head>  
<body>  
    <div style="height: 50px; line-height: 50px;text-align: center;border-bottom: 1px solid #171E28;">  
            上傳圖片:  
            <input type="file" accept="image/*" id="uploadImage" capture="camera" onchange="selectFileImage(this);" />  
        </div>  
        <div style="margin-top: 10px;">  
            <img alt="preview" src="" id="myImage"/>  
        </div>  
</body>  
</html>  
uploadImage.js如下:
[javascript] view plain copy
function selectFileImage(fileObj) {  
    var file = fileObj.files['0'];  
    //圖片方向角 added by lzk  
    var Orientation = null;  
      
    if (file) {  
        console.log("正在上傳,請稍后...");  
        var rFilter = /^(image\/jpeg|image\/png)$/i; // 檢查圖片格式  
        if (!rFilter.test(file.type)) {  
            //showMyTips("請選擇jpeg、png格式的圖片", false);  
            return;  
        }  
        // var URL = URL || webkitURL;  
        //獲取照片方向角屬性,用戶旋轉控制  
        EXIF.getData(file, function() {  
           // alert(EXIF.pretty(this));  
            EXIF.getAllTags(this);   
            //alert(EXIF.getTag(this, 'Orientation'));   
            Orientation = EXIF.getTag(this, 'Orientation');  
            //return;  
        });  
          
        var oReader = new FileReader();  
        oReader.onload = function(e) {  
            //var blob = URL.createObjectURL(file);  
            //_compress(blob, file, basePath);  
            var image = new Image();  
            image.src = e.target.result;  
            image.onload = function() {  
                var expectWidth = this.naturalWidth;  
                var expectHeight = this.naturalHeight;  
                  
                if (this.naturalWidth > this.naturalHeight && this.naturalWidth > 800) {  
                    expectWidth = 800;  
                    expectHeight = expectWidth * this.naturalHeight / this.naturalWidth;  
                } else if (this.naturalHeight > this.naturalWidth && this.naturalHeight > 1200) {  
                    expectHeight = 1200;  
                    expectWidth = expectHeight * this.naturalWidth / this.naturalHeight;  
                }  
                var canvas = document.createElement("canvas");  
                var ctx = canvas.getContext("2d");  
                canvas.width = expectWidth;  
                canvas.height = expectHeight;  
                ctx.drawImage(this, 0, 0, expectWidth, expectHeight);  
                var base64 = null;  
                //修復ios  
                if (navigator.userAgent.match(/iphone/i)) {  
                    console.log('iphone');  
                    //alert(expectWidth + ',' + expectHeight);  
                    //如果方向角不為1,都需要進行旋轉 added by lzk  
                    if(Orientation != "" && Orientation != 1){  
                        alert('旋轉處理');  
                        switch(Orientation){  
                            case 6://需要順時針(向左)90度旋轉  
                                alert('需要順時針(向左)90度旋轉');  
                                rotateImg(this,'left',canvas);  
                                break;  
                            case 8://需要逆時針(向右)90度旋轉  
                                alert('需要順時針(向右)90度旋轉');  
                                rotateImg(this,'right',canvas);  
                                break;  
                            case 3://需要180度旋轉  
                                alert('需要180度旋轉');  
                                rotateImg(this,'right',canvas);//轉兩次  
                                rotateImg(this,'right',canvas);  
                                break;  
                        }         
                    }  
                      
                    /*var mpImg = new MegaPixImage(image); 
                    mpImg.render(canvas, { 
                        maxWidth: 800, 
                        maxHeight: 1200, 
                        quality: 0.8, 
                        orientation: 8 
                    });*/  
                    base64 = canvas.toDataURL("image/jpeg", 0.8);  
                }else if (navigator.userAgent.match(/Android/i)) {// 修復android  
                    var encoder = new JPEGEncoder();  
                    base64 = encoder.encode(ctx.getImageData(0, 0, expectWidth, expectHeight), 80);  
                }else{  
                    //alert(Orientation);  
                    if(Orientation != "" && Orientation != 1){  
                        //alert('旋轉處理');  
                        switch(Orientation){  
                            case 6://需要順時針(向左)90度旋轉  
                                alert('需要順時針(向左)90度旋轉');  
                                rotateImg(this,'left',canvas);  
                                break;  
                            case 8://需要逆時針(向右)90度旋轉  
                                alert('需要順時針(向右)90度旋轉');  
                                rotateImg(this,'right',canvas);  
                                break;  
                            case 3://需要180度旋轉  
                                alert('需要180度旋轉');  
                                rotateImg(this,'right',canvas);//轉兩次  
                                rotateImg(this,'right',canvas);  
                                break;  
                        }         
                    }  
                      
                    base64 = canvas.toDataURL("image/jpeg", 0.8);  
                }  
                //uploadImage(base64);  
                $("#myImage").attr("src", base64);  
            };  
        };  
        oReader.readAsDataURL(file);  
    }  
}  
  
//對圖片旋轉處理 added by lzk  
function rotateImg(img, direction,canvas) {    
        //alert(img);  
        //最小與最大旋轉方向,圖片旋轉4次后回到原方向    
        var min_step = 0;    
        var max_step = 3;    
        //var img = document.getElementById(pid);    
        if (img == null)return;    
        //img的高度和寬度不能在img元素隱藏后獲取,否則會出錯    
        var height = img.height;    
        var width = img.width;    
        //var step = img.getAttribute('step');    
        var step = 2;    
        if (step == null) {    
            step = min_step;    
        }    
        if (direction == 'right') {    
            step++;    
            //旋轉到原位置,即超過最大值    
            step > max_step && (step = min_step);    
        } else {    
            step--;    
            step < min_step && (step = max_step);    
        }    
        //img.setAttribute('step', step);    
        /*var canvas = document.getElementById('pic_' + pid);   
        if (canvas == null) {   
            img.style.display = 'none';   
            canvas = document.createElement('canvas');   
            canvas.setAttribute('id', 'pic_' + pid);   
            img.parentNode.appendChild(canvas);   
        }  */  
        //旋轉角度以弧度值為參數    
        var degree = step * 90 * Math.PI / 180;    
        var ctx = canvas.getContext('2d');    
        switch (step) {    
            case 0:    
                canvas.width = width;    
                canvas.height = height;    
                ctx.drawImage(img, 0, 0);    
                break;    
            case 1:    
                canvas.width = height;    
                canvas.height = width;    
                ctx.rotate(degree);    
                ctx.drawImage(img, 0, -height);    
                break;    
            case 2:    
                canvas.width = width;    
                canvas.height = height;    
                ctx.rotate(degree);    
                ctx.drawImage(img, -width, -height);    
                break;    
            case 3:    
                canvas.width = height;    
                canvas.height = width;    
                ctx.rotate(degree);    
                ctx.drawImage(img, -width, 0);    
                break;    
        }    
    }    

再看下Exif.js官網:

http://code.ciaoca.com/javascript/exif-js/

文檔目錄
  1. 使用方法
  2. API 方法
  3. EXIF 標識
  4. 相關信息

使用方法

載入 JavaScript 文件

<script src="exif.js"></script>

獲取 EXIF 數據

EXIF.getData(document.getElementById('imgElement'), function(){ EXIF.getAllTags(this); EXIF.getTag(this, 'Orientation'); }); 

API 方法

名稱 說明
EXIF.getData(img, callback)

獲取圖像的數據

能兼容尚未支持提供 EXIF 數據的瀏覽器獲取到元數據。

EXIF.getTag(img, tag) 獲取圖像的某個數據
EXIF.getAllTags(img) 獲取圖像的全部數據,值以對象的方式返回
EXIF.pretty(img) 獲取圖像的全部數據,值以字符串的方式返回

EXIF 標識

名稱 說明
ExifVersion Exif 版本
FlashPixVersion FlashPix 版本
ColorSpace 色域、色彩空間
PixelXDimension 圖像的有效寬度
PixelYDimension 圖像的有效高度
ComponentsConfiguration 圖像構造
CompressedBitsPerPixel 壓縮時每像素色彩位
MakerNote 制造商設置的信息
UserComment 用戶評論
RelatedSoundFile 關聯的聲音文件
DateTimeOriginal 創建時間
DateTimeDigitized 數字化創建時間
SubsecTime 日期時間(秒)
SubsecTimeOriginal 原始日期時間(秒)
SubsecTimeDigitized 原始日期時間數字化(秒)
ExposureTime 曝光時間
FNumber 光圈值
ExposureProgram 曝光程序
SpectralSensitivity 光譜靈敏度
ISOSpeedRatings 感光度
OECF 光電轉換功能
ShutterSpeedValue 快門速度
ApertureValue 鏡頭光圈
BrightnessValue 亮度
ExposureBiasValue 曝光補償
MaxApertureValue 最大光圈
SubjectDistance 物距
MeteringMode 測光方式
Lightsource 光源
Flash 閃光燈
SubjectArea 主體區域
FocalLength 焦距
FlashEnergy 閃光燈強度
SpatialFrequencyResponse 空間頻率反應
FocalPlaneXResolution 焦距平面X軸解析度
FocalPlaneYResolution 焦距平面Y軸解析度
FocalPlaneResolutionUnit 焦距平面解析度單位
SubjectLocation 主體位置
ExposureIndex 曝光指數
SensingMethod 圖像傳感器類型
FileSource 源文件
SceneType 場景類型(1 == 直接拍攝)
CFAPattern CFA 模式
CustomRendered 自定義圖像處理
ExposureMode 曝光模式
WhiteBalance 白平衡(1 == 自動,2 == 手動)
DigitalZoomRation 數字變焦
FocalLengthIn35mmFilm 35毫米膠片焦距
SceneCaptureType 場景拍攝類型
GainControl 場景控制
Contrast 對比度
Saturation 飽和度
Sharpness 銳度
DeviceSettingDescription 設備設定描述
SubjectDistanceRange 主體距離范圍
InteroperabilityIFDPointer  
ImageUniqueID 圖像唯一ID
Tiff 相關
名稱 說明
ImageWidth 圖像寬度
ImageHeight 圖像高度
BitsPerSample 比特采樣率
Compression 壓縮方法
PhotometricInterpretation 像素合成
Orientation 拍攝方向
SamplesPerPixel 像素數
PlanarConfiguration 數據排列
YCbCrSubSampling 色相抽樣比率
YCbCrPositioning 色相配置
XResolution X方向分辨率
YResolution Y方向分辨率
ResolutionUnit 分辨率單位
StripOffsets 圖像資料位置
RowsPerStrip 每帶行數
StripByteCounts 每壓縮帶比特數
JPEGInterchangeFormat JPEG SOI 偏移量
JPEGInterchangeFormatLength JPEG 比特數
TransferFunction 轉移功能
WhitePoint 白點色度
PrimaryChromaticities 主要色度
YCbCrCoefficients 顏色空間轉換矩陣系數
ReferenceBlackWhite 黑白參照值
DateTime 日期和時間
ImageDescription 圖像描述、來源
Make 生產者
Model 型號
Software 軟件
Artist 作者
Copyright 版權信息
GPS 相關
名稱 說明
GPSVersionID GPS 版本
GPSLatitudeRef 南北緯
GPSLatitude 緯度
GPSLongitudeRef 東西經
GPSLongitude 經度
GPSAltitudeRef 海拔參照值
GPSAltitude 海拔
GPSTimeStamp GPS 時間戳
GPSSatellites 測量的衛星
GPSStatus 接收器狀態
GPSMeasureMode 測量模式
GPSDOP 測量精度
GPSSpeedRef 速度單位
GPSSpeed GPS 接收器速度
GPSTrackRef 移動方位參照
GPSTrack 移動方位
GPSImgDirectionRef 圖像方位參照
GPSImgDirection 圖像方位
GPSMapDatum 地理測量資料
GPSDestLatitudeRef 目標緯度參照
GPSDestLatitude 目標緯度
GPSDestLongitudeRef 目標經度參照
GPSDestLongitude 目標經度
GPSDestBearingRef 目標方位參照
GPSDestBearing 目標方位
GPSDestDistanceRef 目標距離參照
GPSDestDistance 目標距離
GPSProcessingMethod GPS 處理方法名
GPSAreaInformation GPS 區功能變數名
GPSDateStamp GPS 日期
GPSDifferential GPS 修正


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM