plupload上传图片时浏览器自动旋转图片导致的bug修正

原创
2021/08/02 10:01
阅读数 102

新版浏览器(chrome 81+, safari 13.1+, safari ios 13.4+, firefox 26+)会自动旋转img标签中的带方向标识(有exif信息, 有orientation且不为1)的图片.

而大家常用的文件上传插件 plupload 等 仍然没有对此进行处理, 导致缩略图及resize后保存图片时会出现方向不对的问题.

关于css的image-orientation支持兼容性见: https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation#browser_compatibility

部分版本中对 background-image也进行了自动旋转

下面用一个2px*1px, 方向为6的小图来演示一下效果

<html>
<head>
<title>是否自动旋转img标签的图片</title>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />
</head>
<body>
    <script>
        document.writeln("UA: "+navigator.userAgent);
    </script>
    <div>
        <img src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QCXRXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAIdpAAQAAAABAAAAMoglAAQAAAABAAAANAAAAIMAAAADAAEAAgAAAAJOAAAAAAIABQAAAAMAAABeAB0AAgAAAAsAAAB2AAAAAAAAAB8AAAABAAAAMAAAAAEAABTqAAAAZDIwMjE6MDc6MjQAAAAAAE1nRXhpMDI1OAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wgARCAABAAIDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==" style="width:20px; border:1px solid #f00;" />
    </div>
    <div>
    css设置"image-orientation: none"
    </div>
    <div>
        <img src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QCXRXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAIdpAAQAAAABAAAAMoglAAQAAAABAAAANAAAAIMAAAADAAEAAgAAAAJOAAAAAAIABQAAAAMAAABeAB0AAgAAAAsAAAB2AAAAAAAAAB8AAAABAAAAMAAAAAEAABTqAAAAZDIwMjE6MDc6MjQAAAAAAE1nRXhpMDI1OAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wgARCAABAAIDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==" style="width:20px; border:1px solid #f00; image-orientation: none" />
    </div>
</body>
</html>

在新版pc浏览器的效果:

在老版手机浏览器上的效果:

浏览器对<img src="" />的处理, 其实也是走了new Image()的. 所以检测浏览器是否自动旋转就很简单了

var _canAutoRotateImgLabel = false;//是否支持自动旋转img标签的带exif的图片
var img = new Image();
    img.onload = function(){
        _canAutoRotateImgLabel = img.naturalWidth == 1;//如果自动旋转了, 则宽度是原图的高度1px
    }
   //这是一个 width=2px, height=1px, orientation=6 的图片
    img.src = "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QCXRXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAIdpAAQAAAABAAAAMoglAAQAAAABAAAANAAAAIMAAAADAAEAAgAAAAJOAAAAAAIABQAAAAMAAABeAB0AAgAAAAsAAAB2AAAAAAAAAB8AAAABAAAAMAAAAAEAABTqAAAAZDIwMjE6MDc6MjQAAAAAAE1nRXhpMDI1OAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wgARCAABAAIDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==";

也可以根据浏览器版本号进行判断, 但是在用chrome模拟device时可能会因为指定的UA版本过低导致误判

var _canAutoRotateImgLabel = false;
var browser_name = Env.browser.toLowerCase();
if(
    (browser_name.indexOf('safari') > -1 && parseFloat(Env.version) >= 13.4)
    || (browser_name.indexOf('chrome') > -1 && parseFloat(Env.version) >= 81)
){
    _canAutoRotateImgLabel = true;
}

对于plupload修正的方法就是: 在moxie.jsfunction HTML5Image()中增加是否会自动旋转img标签的变量, 并在其resize: function(rect, ratio, options)中增加对这个变量的判断和修正.

function HTML5Image() {
    var me = this
    , _img, _imgInfo, _canvas, _binStr, _blob
    , _modified = false // is set true whenever image is modified
    , _preserveHeaders = true
    ;

    var _canAutoRotateImgLabel = false;//是否支持自动旋转img标签的带exif的图片. 低版本浏览器不会自动旋转, 需要修正图像.
    function getAutoRotateAble() {
        //使用浏览器版本号判断, 速度快, 但是pc上模拟时会因为UA导致判断错误
        // var browser_name = Env.browser.toLowerCase();
        // if(
        // 	(browser_name.indexOf('safari') > -1 && parseFloat(Env.version) >= 13.4)
        // 	|| (browser_name.indexOf('chrome') > -1 && parseFloat(Env.version) >= 81)
        // ){
        // 	_canAutoRotateImgLabel = true;
        // }

        //用img加载一个方向为6,大小为2px*1px的图片, 然后取其宽度来判断
        var img = new Image();
        img.onload = function(){
            _canAutoRotateImgLabel = img.naturalWidth == 1;//如果自动旋转了, 则宽度是原图的高度1px
            console.log('_canAutoRotateImgLabel: ', _canAutoRotateImgLabel);
        }
        img.src = "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QCXRXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAIdpAAQAAAABAAAAMoglAAQAAAABAAAANAAAAIMAAAADAAEAAgAAAAJOAAAAAAIABQAAAAMAAABeAB0AAgAAAAsAAAB2AAAAAAAAAB8AAAABAAAAMAAAAAEAABTqAAAAZDIwMjE6MDc6MjQAAAAAAE1nRXhpMDI1OAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wgARCAABAAIDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAACP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAP/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8A/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==";// width=2px, height=1px, orientation=6
    }
    getAutoRotateAble();

    Basic.extend(this, {
    //以下都不变

resize: function(rect, ratio, options) {//这里才是最终的修改尺寸的操作
    var canvas = document.createElement('canvas');
    canvas.width = rect.width;
    canvas.height = rect.height;

    canvas.getContext("2d").drawImage(_getImg(), rect.x, rect.y, rect.width, rect.height, 0, 0, canvas.width, canvas.height);

    _canvas = ResizerCanvas.scale(canvas, ratio);

    _preserveHeaders = options.preserveHeaders;

    // rotate if required, according to orientation tag
    var orientation = (this.meta && this.meta.tiff && this.meta.tiff.Orientation) || 1;
    if (!_preserveHeaders &&!_canAutoRotateImgLabel && orientation != 1) {
        _canvas = _rotateToOrientaion(_canvas, orientation);
    }
    if( _preserveHeaders && _canAutoRotateImgLabel
        && Basic.inArray(orientation, [3,6,8]) > -1 //暂时只修正这些 todo
    ){
        //如果保留header且浏览器会自动旋转, 则反旋转
        var orientation2;
        switch (orientation) {
            case 3:
                orientation2 = 3;
                break;
            case 6:
                orientation2 = 8;
                break;
            case 8:
                orientation2 = 6;
                break;
        }
        _canvas = _rotateToOrientaion(_canvas, orientation2);
    }

    this.width = _canvas.width;
    this.height = _canvas.height;

    _modified = true;

    this.trigger('Resize');
},

注意: 生成预览缩略图的代码中, 不要暴力header, 否则因为多旋转了一次导致缩略图方向不对.

function plupload_previewImage(file, callback) {//file为plupload事件监听函数参数中的file对象,callback为预览图片准备完成的回调函数
    if (!file || !/image\//.test(file.type)) return ''; //确保文件是图片
    if (file.type == 'image/gif') {//gif使用FileReader进行预览,因为moxie.image.Image只支持jpg和png
        var fr = new moxie.file.FileReader();
        fr.onload = function () {
            callback(fr.result);
            fr.destroy();
            fr = null;
        }
        fr.readAsDataURL(file.getSource());
    }
    else{
        var preloader = new moxie.image.Image();
        preloader.onload = function () {
            preloader.resize({"width":300, "height":300, "preserveHeaders":false});//先压缩一下要预览的图片: 宽,高,不保留header(会自动旋转). 这里必须设置false
            var imgsrc = preloader.type == 'image/jpeg' ? preloader.getAsDataURL('image/jpeg', 80) : preloader.getAsDataURL(); //得到图片src,实质为一个base64编码的数据 => 这里会丢失exif信息(canvas.toDataURL导致的)
            callback && callback(imgsrc); //callback传入的参数为预览图片的url
            preloader.destroy();
            preloader = null;
        };
        preloader.load(file.getSource());
    }
}
展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部