Chrome和iOS Safari中的普通图像出现意外的CORS问题

时间:2014-07-29 20:29:50

标签: google-chrome canvas cors

我正面临一个令我疯狂的CORS问题。请允许我分享一个示例网址:

http://www.jungledragon.com/image/19905/mature_female_eastern_forktail.html/zoom

由于问题每页只能复制一次,因此这里列出了其他图片:

http://www.jungledragon.com/all/recent

从该概述中,您可以打开任何照片页面。接下来,从该照片页面再次单击该图像以全屏启动它,因为这就是问题所在。

现在让我解释一下设置和问题。该站点本身托管在我控制的Linux服务器上。该网站位于www.jungledragon.com。但是,这些图像存储在Amazon S3中,其中图像存储桶的别名为media.jungledragon.com。

基本情况很简单:

<div id="slideshow-image-container">
    <div class="slideshow-image-wrapper">
    <img src="http://media.jungledragon.com/images/1755/19907_large.jpg?AWSAccessKeyId=05GMT0V3GWVNE7GGM1R2&Expires=1409788810&Signature=QH26XDrVuhyr1Qimd7IOBsnui5s%3D" id="19907" class="img-slideshow img-sec wide" data-constrained="true" data-maxheight="2056" crossorigin="anonymous">
    </div>
</div>

正如您所看到的,我只是使用普通的&#39; html&#39;加载图像的方式。图片网址已签名并可能超时,但这不应该是相关的。我的理解是CORS不适用于这种情况,因为这种方式从外部域加载图像已经支持了几十年。毕竟,使用javascript不会加载图像。

但是,可以肯定的是,crossorigin属性是在HTML中设置的。此外,作为一种测试方式,我在图像存储桶上设置了一个非常宽松的CORS策略:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Content-Type</AllowedHeader>
<AllowedHeader>x-amz-acl</AllowedHeader>
<AllowedHeader>origin</AllowedHeader>
</CORSRule>
</CORSConfiguration>

现在,情况变得更加复杂。全屏图像查看器应该获得背景颜色,该背景颜色是屏幕上实际图像的主要/平均颜色。该颜色是使用画布计算的,但它只计算一次。第一次计算该图像时,结果使用ajax调用传递给后端,然后永久存储。对图像的后续访问不会再次运行计算逻辑,它只会设置body元素的背景颜色,一切都很好。

以下是进行计算的逻辑:

<script>
$( document ).ready(function() {

    <?php if (!$bigimage['dominantcolor']) { ?>

        $('#<?= $bigimage['image_id'] ?>').load(function(){
        var rgb = getAverageRGB(document.getElementById('<?= $bigimage['image_id'] ?>'));
        document.body.style.backgroundColor = 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')';

        if (rgb!==false) {
            $.get(basepath + "image/<?= $bigimage['image_id'] ?>/setcolor/" + rgb.r + "-" + rgb.g + "-" + rgb.b);       
        }

        });

    <?php } ?>

});

是的,我将后端代码与前端代码混合在一起。上面的代码说如果我们还不知道场景中的主色,那么计算它。使用加载函数是因为在文档就绪时,可能没有完全加载来自普通html的实际图像。接下来,如果尚未知道主色,并且加载图像,则触发计算主色的函数。这是:

function getAverageRGB(imgEl) {

    var blockSize = 5, // only visit every 5 pixels
    defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs
    canvas = document.createElement('canvas'),
    context = canvas.getContext && canvas.getContext('2d'),
    data, width, height,
    i = -4,
    length,
    rgb = {r:0,g:0,b:0},
    count = 0;

    if (!context) {
        return defaultRGB;
    }

    height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
    width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;

    imgEl.crossOrigin = "anonymous";

    context.drawImage(imgEl, 0, 0);

    try {
        data = context.getImageData(0, 0, width, height);
    } catch(e) {
        /* security error, img on diff domain */
        return false;
    }

    length = data.data.length;

    while ( (i += blockSize * 4) < length ) {
        ++count;
        rgb.r += data.data[i];
        rgb.g += data.data[i+1];
        rgb.b += data.data[i+2];
    }

    // ~~ used to floor values
    rgb.r = ~~(rgb.r/count);
    rgb.g = ~~(rgb.g/count);
    rgb.b = ~~(rgb.b/count);

    return rgb;

}

以下行与CORS相关:

data = context.getImageData(0, 0, width, height);

虽然我相信我已经正确设置了CORS,但我可以忍受这些代码在某些浏览器中失败。例如,它似乎在Firefox和IE11中运行良好。如果它失败了,我会指望它无法计算主色。然而,在非常特殊的情况下会出现更糟糕的情况:图像并未完全显示出来。

我的想法是我的经典&#39;通过img src标签加载图像应与此脚本工作或失败无关,在所有情况下,至少图像应该加载,无论画布技巧如何。

以下是我发现图像无法完全加载的情况,我认为这是一个主要问题:

在iPhone 5上的iOS7上,第一次加载正常。计算可能会失败,但图像会加载。刷新页面通常会破坏图像。第3次和第4次尝试然后继续成功,依此类推。

更糟糕的是,在Chrome 36中工作时,图像不会全部加载。我在工作时说,因为在家里这不是问题。代理人可能会有所不同。我可以刷新我想要的所有内容,对于尚未运行计算的图像,它会一直失败。

然后,自然要做的就是使用Chrome的检查员进行调试。你猜怎么着?检查员打开后,它总是成功。图像将始终加载,CORS请求标头和响应看起来非常好。这让我几乎无法调试这个。我可以告诉你,当图像没有加载时打开检查器确实给了我&#34; CORS错误&#34;在控制台中,从我之前提出的请求。打开检查员进行刷新将使其消失。

通过阅读其他问题,我了解到缓存可能会产生影响,但更有可能的问题在于浏览器未发送的原始标头。我认为这个问题可能就是朝这个方向发展,但我不明白这一点:

  • 它如何影响我的正常&#34;使用img标签加载图像
  • 如何只是Chrome中代理(假设)背后的问题,并且仅在检查员窗口关闭时才会出现问题
  • 如何在iOS上的Safari中如此不可靠和不一致

如上所述,我只能使用一些浏览器部分成功的浏览器,但我不能忍受在任何情况下都没有正常加载的图像。那部分应该有效。

我意识到你的调试情况非常困难,但我希望我的解释会引发一些急需的帮助。

更新:我发现当我删除crossorigin =&#34;匿名&#34;从img标签,图像将在我提到的特定场景中正确加载。然而,这一举措的结果是,颜色计算将不再适用于Chrome,而不是在家中而不是在工作中。它继续在Firefox中工作。我正在研究下一步该做什么。

1 个答案:

答案 0 :(得分:0)

我设法自己解决了这个问题。我仍然不能完全解释因果关系,但这就是我所做的:

我删除了crossorigin =&#34;匿名&#34;来自html的img元素。这至少可以确保图像始终加载。

我通过基本上重写其逻辑来解决颜色计算部分:

var imgSrc = $('#<?= $bigimage['image_id'] ?>').attr('src');
    var cacheBurstPrefix = imgSrc.indexOf("?") > -1 ? '&' : '?';
    imgSrc += cacheBurstPrefix + 'ts=' + new Date().getTime();
    var imagePreloader = new Image();
    imagePreloader.crossOrigin = "Anonymous";
    imagePreloader.src = imgSrc;

    $(imagePreloader).imagesLoaded(function() {

        var rgb = getAverageRGB(imagePreloader);
        document.body.style.backgroundColor = 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')';
        if (rgb!==false) {
            $.get(basepath + "image/<?= $bigimage['image_id'] ?>/setcolor/" + rgb.r + "-" + rgb.g + "-" + rgb.b);
        }

    });

我没有重复使用html中的img元素,而是创建了一个新的内存中的图像元素。使用缓存突发技术,我确保它是新加载的。接下来,我使用imagesLoaded(第三方插件)来检测正在加载的内存中图像的事件,这比jQuery的load()事件更可靠。

我已经进行了广泛的测试,并且可以确认在任何情况下,正常的图像加载都不会再次破坏。它适用于每个浏览器和代理情况。作为一个额外的好处,颜色计算部分现在似乎在更多的浏览器中工作,包括几个移动浏览器。

虽然我对根本原因仍然没有信心,但在经历了很多挫折之后,我对新形势感到非常满意。