我们使用JS LIB retina.js其交换与“视网膜”图片(大小次2)低质量的图像。 问题是,retina.js抛出一个404的每一个“视网膜”的形象不能被发现。
我们拥有一个网站,用户可以上传自己的照片,其在视网膜的分辨率很可能不会。
有没有办法来防止JS从抛404?
如果你不知道的lib。 这里是投掷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();
Answer 1:
有迹象表明,我看到几个选项,以减轻这一点。
加强并持续retina.js' HTTP调用结果缓存
对于被设定为换出“1X”版本任何给定的“2×”图像,retina.js第一经由验证图像的可用性XMLHttpRequest
请求。 与成功的响应路径被缓存在一个阵列和图像被下载。
下面的变化可能提高效率:
失败XMLHttpRequest
验证尝试可以被缓存:目前,如果以前已经成功了“2X”路径验证尝试只跳过。 因此,失败的尝试可能复发。 在实践中,这并没有太大怎么一回事,因为在最初加载页面的验证过程中发生的事。 但是,如果结果是坚持,失败的跟踪将防止重复出现404错误。
坚持“2X”路径验证结果localStorage
:在初始化过程中,retina.js可以检查localStorage
的结果高速缓存。 如果找到一个,已经遇到了能与“2X”图片验证过程可以绕过和“2X”的图像可以被下载或跳过。 新encounterd“2X”图像路径可以验证,结果添加到缓存。 从理论上讲,当localStorage
是可用的,404会为在每个浏览器上的图像只出现一次。 这将跨页申请,域中的所有页面。
这里是一个快速的后处理。 过期的功能可能会需要添加。
https://gist.github.com/4343101/revisions
采用一个HTTP重定向头
我必须指出,我的“服务器端”问题的把握是参差不齐的 ,最好的。 请把这FWIW
另一种选择是让服务器与该有图像请求重定向代码响应@2x
字符,并且不存在。 见这相关的答案 。
特别是:
如果您重定向的图像和他们缓存,你最好设置一个HTTP过期了在遥远的将来的日期标题(及相应的Cache-Control头),所以至少在以后的访问页面的用户不会有再次通过重定向。
采用重定向响应将摆脱404和导致浏览器跳到随后尝试访问不存在的“2X”图像路径。
retina.js可以更选择性
retinajs可以进行修改,以排除考虑一些图像。
与此相关的上拉要求: https://github.com/imulus/retinajs/commit/e7930be
每拉入请求,而不是寻找<img>
标记名称元素,CSS选择可以使用,这可以是的retina.js'可配置的选项之一。 CSS选择可以创建将筛选出的用户上载的图像(和针对其“2X”的变体预期不存在其他的图像)。
另一种可能性是过滤功能添加到配置选项。 该函数可以被调用每个匹配的上<img>
元件; 一return true
会导致出现下载的“2X”的变体和其他任何将导致<img>
被跳过。
基本的,默认的配置会从改变目前的版本 ,以这样的:
var config = {
check_mime_type: true,
retinaImgTagSelector: 'img',
retinaImgFilterFunc: undefined
};
该Retina.init()
函数会从改变目前的版本 ,以这样的:
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();
}
};
要付诸实践,之前window.onload
火灾,请致电:
window.Retina.configure({
// use a class 'no-retina' to prevent retinajs
// from checking for a retina version
retinaImgTagSelector : 'img:not(.no-retina)',
// or, assuming there is a data-owner attribute
// which indicates the user that uploaded the image:
// retinaImgTagSelector : 'img:not([data-owner])',
// or set a filter function that will exclude images that have
// the current user's id in their path, (assuming there is a
// variable userId in the global scope)
retinaImgFilterFunc: function(img) {
return img.src.indexOf(window.userId) < 0;
}
});
更新:清理,整顿。 添加了localStorage
增强。
Answer 2:
只有它不可能使用客户端JavaScript:简答
浏览代码,和一点点研究,这在我看来,retina.js是不是真的抛出404错误。
什么retina.js实际上做的是请求文件,然后直接执行它存在与否基于错误代码的检查。 这实际上意味着它要求浏览器来检查文件是否存在。 浏览器是什么让你的404并没有跨浏览器的方式,以防止(我说“跨浏览器”,因为我只检查的webkit)。
但是,我们可以做什么,如果这确实是一个问题,是做在服务器端的东西,以防止404完全。
本质上,这将是,例如,/retina.php?image= YOUR_URLENCODED_IMAGE_PATH请求可能返回此当视网膜图像存在...
{"isRetina": true, "path": "YOUR_RETINA_IMAGE_PATH"}}
而这,如果它不...
{"isRetina": false, "path": "YOUR_REGULAR_IMAGE_PATH"}}
然后,您可以有一些JavaScript调用此脚本,并解析响应是必要的。 我不是说这是唯一或最佳的解决方案,只是一个会工作。
Answer 3:
视网膜JS支持属性的图像标签上的数据没有视网膜。 这样,它不会试图找到视网膜图像。
有助于其他人寻找一个简单的解决方案。
<img src="/path/to/image" data-no-retina />
Answer 4:
我更喜欢它的图像替换多一点控制。
对于我创建了一个@ 2X的所有图像,我改变了原来的图像名称,包括@ 1倍。 (*参见下面的说明。)我改变retina.js略,使其只看[名] @ 1倍。[转]图像。
我取代了视网膜1.1.0.js以下行:
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));
}
这使得它,以便retina.js只替换@ 1X命名的图像与@ 2X命名的图像。
(*注:在探索这个,似乎Safari和Chrome自动@ 2X图片更换@ 1倍的图像,即使没有安装retina.js我懒得跟踪下来,但我想它有一个功能最新的WebKit浏览器,因为它是,retina.js和它上面的修改是必要的跨浏览器的支持。)
Answer 5:
一个解决方案是使用PHP:
从第一后与替换代码:
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();
在你的网站根目录添加文件名为“image.php”:
<?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']);
}
?>
Answer 6:
retina.js是对静态网页固定图像一个很好的工具,但如果你是检索用户上传的图片,右边的工具是服务器端。 我想象这里PHP,但同样的逻辑可以适用于任何服务器端语言。
但就上传的图片一个很好的安全习惯是不要让用户直接链接到他们:如果用户在上传恶意脚本到你的服务器成功,他不应该是能够通过URL来启动它( www.yoursite.com/uploaded/mymaliciousscript.php
)。 因此,它通常是一个好习惯,通过一些脚本来检索上传的图片<img src="get_image.php?id=123456" />
如果你能...(甚至更好,保持文件夹上传出该文档根)
现在get_image.php脚本可以在某些条件下获得相应的图像123456.jpg或123456@2x.jpg。
的方法http://retina-images.complexcompulsions.com/#setupserver似乎非常适合您的情况。
首先,通过加载JS通过或CSS文件中设置你的头一个cookie:
内部HEAD:
<script>(function(w){var dpr=((w.devicePixelRatio===undefined)?1:w.devicePixelRatio);if(!!w.navigator.standalone){var r=new XMLHttpRequest();r.open('GET','/retinaimages.php?devicePixelRatio='+dpr,false);r.send()}else{document.cookie='devicePixelRatio='+dpr+'; path=/'}})(window)</script>
在体温的开头:
<noscript><style id="devicePixelRatio" media="only screen and (-moz-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2)">#devicePixelRatio{background-image:url("/retinaimages.php?devicePixelRatio=2")}</style></noscript>
现在每次你的脚本来检索上传的图片被调用时,它会设置Cookie要求视网膜图像(或没有)。
当然,你可以使用提供retinaimages.php脚本输出的图像,但你也可以从数据库或隐藏来自用户的上传目录进行修改,以适应您的需求,这取决于你如何制作和检索图像。
这样,不仅可以加载相应的图像,但如果安装GD2,和你保持原始上传图片的服务器上,它甚至可能相应地调整它的大小裁剪并保存在服务器上2种缓存图像尺寸。 里面的retinaimages.php来源可以看到(及复印件)它是如何工作:
<?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..
?>
优点:图像加载一次(在3G用户视网膜至少won'tload 1X + 2倍的图像)的作品,即使没有JS如果启用了Cookie,可以开启和关闭容易,没有必要使用苹果的命名规则。 您加载图像12345,你会得到您的设备选择正确的DPI。
使用URL重写,你甚至可以使其完全被重定向到/get_image/1234.jpg透明/get_image.php?id=1234.jpg
Answer 7:
我的建议是你认识的404错误是真实的错误,以及解决这些问题,你是应该的方式,它主要是提供视网膜图形。 你让你的脚本视网膜兼容的,但你没有通过使您的图形工作流的Retina兼容完成的圈。 因此,视网膜图形实际上是缺失的。 无论在图形工作流开始进入时,工作流的输出必须是2的图像文件,低分辨率和视网膜的2倍。
如果用户上传的相片是3000x2400,你应该认为这是照片的视网膜版本,将其标记2倍,然后使用服务器端脚本来产生更小的1500x1200非视网膜版本,而2倍。 2档一起然后构成能够在1500x1200被显示在Web上下文的显示是否是视网膜与否一个1500x1200视网膜兼容图像。 你不必去关心,因为你有一个视网膜兼容的图像和视网膜兼容的网站。 该RetinaJS脚本是有关心客户端是否采用Retina或不是唯一的一个。 所以,如果你收集用户的照片,你的任务是不完整的,除非你生成2个文件,这两个低分辨率和高分辨率。
典型的智能手机拍摄的相片是智能手机显示屏的10倍以上的大小。 所以,你应该始终有足够的像素。 但是,如果你变得非常小的图像,如500像素,那么您可以在设置一个断点你的服务器端图像还原脚本,以便在其下方,用于低分辨率版本上传的照片和脚本使得2X副本那将是没有比非视网膜图像更好,但它会是视网膜兼容。
有了这个解决方案,您的整个问题“是2倍的图像有或没有?”消失了,因为它是永远存在的。 视网膜兼容的网站将只是愉快地使用照片你的视网膜兼容的数据库没有任何投诉。
文章来源: Suppressing 404s in retina.js library