Javascript 在retina.js库中抑制404s

Javascript 在retina.js库中抑制404s,javascript,http-status-code-404,retina.js,Javascript,Http Status Code 404,Retina.js,我们使用js库将低质量图像与“视网膜”图像交换(大小乘以2)。问题是,retina.js会对每个找不到的“retina”图像抛出404 我们有一个网站,用户可以上传他们自己的图片,这些图片很可能不是视网膜分辨率 <img src="/path/to/image" data-no-retina /> 没有办法阻止js抛出404吗 如果你不知道图书馆。下面是抛出404的代码: http = new XMLHttpRequest; http.open('HEAD', this.at_2x

我们使用js库将低质量图像与“视网膜”图像交换(大小乘以2)。问题是,retina.js会对每个找不到的“retina”图像抛出404

我们有一个网站,用户可以上传他们自己的图片,这些图片很可能不是视网膜分辨率

<img src="/path/to/image" data-no-retina />
没有办法阻止js抛出404吗

如果你不知道图书馆。下面是抛出404的代码:

http = new XMLHttpRequest;
http.open('HEAD', this.at_2x_path);
http.onreadystatechange = function() {
    if (http.readyState != 4) {
        return callback(false);
    }

    if (http.status >= 200 && http.status <= 399) {
        if (config.check_mime_type) {
            var type = http.getResponseHeader('Content-Type');
            if (type == null || !type.match(/^image/i)) {
                return callback(false);
            }
        }

        RetinaImagePath.confirmed_paths.push(that.at_2x_path);
        return callback(true);
    } else {
        return callback(false);
    }
}
http.send();
http=newxmlhttprequest;
http.open('HEAD',this.at_2x_路径);
http.onreadystatechange=函数(){
如果(http.readyState!=4){
返回回调(false);
}

如果(http.status>=200&&http.status简短回答:仅使用客户端JavaScript是不可能的

在浏览了代码并进行了一些研究之后,我觉得retina.js并没有真正抛出404错误

retina.js实际做的是请求一个文件,并根据错误代码简单地检查它是否存在。这实际上意味着它是要求浏览器检查文件是否存在。浏览器是404的来源,没有跨浏览器的方法来阻止它(我说“跨浏览器”因为我只检查了webkit)

然而,如果这真的是一个问题,您可以做的是在服务器端做一些事情来完全防止404

本质上,这将是,例如,/retina.php?image=您的_URLENCODED_image_路径一个请求,当视网膜图像存在时,该请求可以返回此路径

{"isRetina": true, "path": "YOUR_RETINA_IMAGE_PATH"}}
如果不这样的话

{"isRetina": false, "path": "YOUR_REGULAR_IMAGE_PATH"}}

然后,您可以让一些JavaScript调用此脚本并根据需要解析响应。我不是说这是唯一或最好的解决方案,只是一个可行的解决方案。我看到了一些选项,可以缓解这一问题

增强并持久化retina.js的HTTP调用结果缓存 对于任何设置为交换“1x”版本的给定“2x”映像,retina.js首先通过
XMLHttpRequest
请求验证映像的可用性。具有成功响应的路径缓存在数组中,并下载映像

以下更改可能会提高效率:

  • 失败的
    XMLHttpRequest
    验证尝试可以被缓存:目前,只有在“2x”路径验证尝试以前成功的情况下才会跳过。因此,失败的尝试可能会再次出现。在实践中,这并不重要,因为验证过程是在最初加载页面时进行的。但是,如果结果是persisted,跟踪故障将防止再次出现404错误

  • localStorage
    中保留“2x”路径验证结果:初始化期间,retina.js可以检查
    localStorage
    中的结果缓存。如果找到结果缓存,则可以绕过已遇到的“2x”映像的验证过程,并且可以下载或跳过“2x”映像。新遇到的'可以验证2x'图像路径,并将结果添加到缓存中。理论上,虽然
    localStorage
    可用,但对于每个浏览器的图像,404只会出现一次。这将适用于域中任何页面的跨页面

这是一个快速的工作。可能需要添加过期功能

使用HTTP重定向头 我必须注意的是,我对“服务器端”问题的理解充其量是零碎的。请接受这个FWIW

另一个选项是,对于包含
@2x
字符且不存在的图像请求,服务器使用重定向代码进行响应。请参阅

特别是:

如果重定向图像且图像可缓存,则理想情况下应为遥远的将来的某个日期设置HTTP Expires标头(以及相应的缓存控制标头),以便至少在后续访问页面时,用户不必再次执行重定向

使用重定向响应将摆脱404,并导致浏览器跳过访问不存在的“2x”图像路径的后续尝试

retina.js可以更具选择性 可以修改retinajs以排除某些图像

与此相关的拉取请求:

根据pull请求,可以使用CSS选择器,而不是按标记名查找
元素,这可以是retina.js的可配置选项之一。可以创建一个CSS选择器,用于过滤用户上传的图像(以及预期不存在“2x”变体的其他图像)

另一种可能是在可配置选项中添加一个筛选函数。可以对每个匹配的
元素调用该函数;
返回true
将导致下载“2x”变量,其他任何操作都将导致跳过

基本的默认配置将从更改为:

var config = {
  check_mime_type: true,
  retinaImgTagSelector: 'img',
  retinaImgFilterFunc: undefined
};
Retina.init = function(context) {
  if (context == null) context = root;

  var existing_onload = context.onload || new Function;

  context.onload = function() {
    // uses new query selector
    var images = document.querySelectorAll(config.retinaImgTagSelector), 
        retinaImages = [], i, image, filter;

    // if there is a filter, check each image
    if (typeof config.retinaImgFilterFunc === 'function') {
      filter = config.retinaImgFilterFunc;
      for (i = 0; i < images.length; i++) {
        image = images[i];
        if (filter(image)) {
          retinaImages.push(new RetinaImage(image));
        }
      }
    } else {
      for (i = 0; i < images.length; i++) {
        image = images[i];
        retinaImages.push(new RetinaImage(image));
      }
    }
    existing_onload();
  }
};
Retina.init()

var config = {
  check_mime_type: true,
  retinaImgTagSelector: 'img',
  retinaImgFilterFunc: undefined
};
Retina.init = function(context) {
  if (context == null) context = root;

  var existing_onload = context.onload || new Function;

  context.onload = function() {
    // uses new query selector
    var images = document.querySelectorAll(config.retinaImgTagSelector), 
        retinaImages = [], i, image, filter;

    // if there is a filter, check each image
    if (typeof config.retinaImgFilterFunc === 'function') {
      filter = config.retinaImgFilterFunc;
      for (i = 0; i < images.length; i++) {
        image = images[i];
        if (filter(image)) {
          retinaImages.push(new RetinaImage(image));
        }
      }
    } else {
      for (i = 0; i < images.length; i++) {
        image = images[i];
        retinaImages.push(new RetinaImage(image));
      }
    }
    existing_onload();
  }
};

更新:清理并重新组织。添加了
localStorage
增强功能。

retina.js对于静态网页上的固定图像是一个很好的工具,但是如果您要检索用户上传的图像,正确的工具是服务器端。我想象这里是PHP,但是相同的逻辑可以应用于任何服务器端语言

如果上传图像的一个好的安全习惯是不让用户通过直接url访问它们:如果用户成功地将恶意脚本上传到您的服务器,他应该不能通过url启动它(
www.yoursite.com/upload/mymaliciousscript.php
)。因此,如果可以的话,通常通过一些脚本检索上传的图像是一个好习惯…(更好的是,将上传文件夹放在文档根目录之外)

现在,get_image.php脚本可以获得相应的图像123456.jpg或123456@2x.jpg取决于某些条件

这种方法似乎非常适合你的情况
<?php
    $source_file = ...
    $retina_file = ....

    if (isset($_COOKIE['devicePixelRatio'])) {
        $cookie_value = intval($_COOKIE['devicePixelRatio']);
    }
    if ($cookie_value !== false && $cookie_value > 1) {
        // Check if retina image exists
        if (file_exists($retina_file)) {
            $source_file = $retina_file;
        }
    }
    ....



    header('Content-Length: '.filesize($source_file), true);
    readfile($source_file); // or read from db, or create right size.. etc..

?>
        http = new XMLHttpRequest;
        http.open('HEAD', "/image.php?p="+this.at_2x_path);
        http.onreadystatechange = function() {
            if (http.readyState != 4) {
                return callback(false);
            }

            if (http.status >= 200 && http.status <= 399) {
                if (config.check_mime_type) {
                    var type = http.getResponseHeader('Content-Type');
                    if (type == null || !type.match(/^image/i)) {
                        return callback(false);
                    }
                }

                RetinaImagePath.confirmed_paths.push(that.at_2x_path);
                return callback(true);
            } else {
                return callback(false);
            }
        }
        http.send();
<?php
 if(file_exists($_GET['p'])){
  $ext = explode('.', $_GET['p']);
  $ext = end($ext);
  if($ext=="jpg") $ext="jpeg";
  header("Content-Type: image/".$ext);
  echo file_get_contents($_GET['p']);
 }
?>
retinaImages.push(new RetinaImage(image));
 if(image.src.match(/@1x\.\w{3}$/)) {
    image.src = image.src.replace(/@1x(\.\w{3})$/,"$1");
    retinaImages.push(new RetinaImage(image));
}
<img src="/path/to/image" data-no-retina />