不加载图片!直接HTTP方式获取图片尺寸
发布时间:2017-08-24, 17:34:10 分类:HTML | 编辑 off 网址 | 辅助
图集1/4
正文 2256字数 540,138阅读
之前的文章中有介绍过类似的东西,但是那中方法依然会把整张图片加载下来,只不过是在加载完成前获取了图片尺寸而已。现在,我们要使用更直接的方法,直接从服务器上读取到图片的尺寸信息。之前的文章就介绍过了关于图片头的知识,现在我要向服务器发送一个HTTP请求,让服务器返回图片的尺寸信息,然后自己解析就行了。
这种方法比较适合PNG和GIF图片,因为他们的头信息中的尺寸位置是固定的。我们很容易就能解析出它的大小,JPG图片由于头部的描述信息在尺寸信息之前。
所以我们无法直接获取它尺寸信息在文件流中的位置。虽然可以加载整个头来分析,但是我不推荐使用这种做法来获取JPG文件的尺寸,除非你服务器上的JPG图片是固定格式生成的。
断点续传这种技术我想大家没用过也应该听说过吧,这就是运用了HTTP协议中的Range字段来指定让服务器返回数据流中的部分信息。现在,我们就利用这个Range字段让服务器返回图片的尺寸信息。要向服务器发送这个Range字段首先要知道,图片的尺寸信息在图片文件流中的位置,我们用16进制工具打开图片就能找到。
上图是PNG和GIF文件的尺寸信息保存的位置PNG是从第16字节到第23字节,GIF是从第6字节到第9字节。先不说啥了,来看代码
//获取浏览器版本
var isIE=navigator.userAgent.match(/MSIE (\d)/i);
isIE=isIE?isIE[1]:undefined;
//变量声明
var xhr,w,h,s;
//创建HTTP对象
if(isIE)
xhr=new ActiveXObject("Microsoft.XMLHTTP");
else{
xhr=new XMLHttpRequest;
//设置返回值的类型
xhr.responseType="arraybuffer";
};
//创建HTTP请求
xhr.open("GET","onepiece.png",true);
//在请求头中添加Range字段
xhr.setRequestHeader("Range","bytes=16-23");
//异步回调函数
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
//获取二进制流并转换成数组
s=isIE
?new VBArray(xhr.responseBody).toArray()
:new Uint8Array(xhr.response);
//解析返回信息
w=(s[0]<<24)+(s[1]<<16)+(s[2]<<8)+s[3];
h=(s[4]<<24)+(s[5]<<16)+(s[6]<<8)+s[7];
alert("宽:"+w+"\n高:"+h);
};
};
//发送请求
xhr.send();
Run code
Cut to clipboard
上面就是获取PNG图片尺寸的代码。先创建一个XHR对象,这东西是AJAX的基础我就不多说了。这里关键的是非IE中的responseType必须设置成arraybuffer,要不如返回的结果会被强制转换成字符,这可能会导致字节丢失。
致至于Range字段的参数简单的用法就是bytes=开始字节-结束字节,当然还有很多用法,这篇文字关键不是说这个所以就不介绍了。下面是接收返回的二进制数据,在浏览器上有差异,IE使用responseBody返回,但是它返回的是一个内存数组,JS的数组是经过封装的类数组所以不能直接接收。
但是可以用VBS中的数组来接收,因为VBS的数组是内存数组,这就是为什么在微软的脚本引擎上VBS的效率比JS效率高的原因。获取VBArray之后可以用它的toArray方法转换成JS使用的数组,这个是微软提供的VBS和JS之间沟通的接口。
接下来就是非IE浏览器的部分了。我们用response接收服务器返回的数据,这个类型我们在创建XHR对象的时候就已经设置了,所以可以直接使用Uint8Array把它转换成JS可用的无符号整型 数组。最后一个步骤就是解析这个数组,PNG的尺寸部分前4位是宽度,后4位是高度。我们只要逐个字节计算位权然后相加就可以得到宽度和高度了。
至于GIF的获取方法,和上面这个代码大同小异。你只需要修改Range的参数和最后一部的解析返回值就可以了,我就不再演示了。这个方法加载的最大好处就是不用去请求整个图片,我去找个超级大的图片来试试
这图片够大了吧,如果直接使用Image对象载入它,用户会奔溃的。但是使用这个方法就算网速很差也不会有什么影响。而且还可以节省服务器的资源,服务器只需要处理我们请求的几个字节的数据就可以了,不用载入整张图片。基本上一瞬间就可以获取到图片的大小。
好了,我想现在应该都会使用这方法了吧。对于其它图片格式当然也不是不行只不过麻烦,但是如果把它封装好就不会麻烦了。因为这个东西也不会经常用到,所以我也懒的封装这个了。
(支付宝)给作者钱财以资鼓励 (微信)→
有过 1 条评论 »
var t_img; // 定时器 var isLoad = true; // 控制变量 // 判断图片加载状况,加载完成后回调 isImgLoad(function(){ // 加载完成 }); // 判断图片加载的函数 function isImgLoad(callback){ // 注意我的图片类名都是cover,因为我只需要处理cover。其它图片可以不管。 // 查找所有封面图,迭代处理 $('.cover').each(function(){ // 找到为0就将isLoad设为false,并退出each if(this.height === 0){ isLoad = false; return false; } }); // 为true,没有发现为0的。加载完毕 if(isLoad){ clearTimeout(t_img); // 清除定时器 // 回调函数 callback(); // 为false,因为找到了没有加载完成的图,将调用定时器递归 }else{ isLoad = true; t_img = setTimeout(function(){ isImgLoad(callback); // 递归扫描 },500); // 我这里设置的是500毫秒就扫描一次,可以自己调整 } }
来谈一下关于load的问题。 ------------------------------------------------------------- 众所周知,常见瀑布流当鼠标滚动到浏览器底部的时候,就会发起一个ajax的请求。在服务端生成item列表后,通过 js append到相应的div里边。 看起来很简单的样子,关键问题就出在图片的加载问题上,图片一般都放在服务器上,通过http下载到客户端。 例如我的图片地址: http://xxx.xxx.com/sc/item/cover/9-4352-c400.jpg 而图片下载到本地是需要一定时间的(网速快的路过)。当图片还没有下载完的时候,使用js获取到元素的宽高将会是0。 ------------------------------------------------------------------- 有的同学说了我使用jquery的ready不就好了。如下: $(document).ready(function(){ // 在这里写你的代码... }); 如果这么简单就好了,我这里就说下ready与window.onload的区别。 jquery的ready只是dom的结构加载完毕,便视为加载完成。(缺点是图片没有加载完毕,宽高为0,程序出错) js的window.onload是指dom的生成和资源的加载,比如flash、图片完全加载出来后才执行onload。(缺点就是当某一张图片很大的时候,岂不阻止了其它js的正常执行) ------------------------------------------------------------------------------------- 知道了他们的区别后,我们再来谈谈如何避免错误和选择性使用。 如果你进行了百度,很多人会告诉你。 这样: $('img').load(function(){ // 加载完成 }); 好像很强大的样子,其实不然,他的缺点是每加载一张图片,回调函数就执行一次。好吧太烦了,我只想全部加载完走一次就可以了。当然可以,你可以进行修改如下: va imgNum=$('img').length; $('img').load(function(){ if(!--imgNum){ // 加载完成 } }); 这样总可以了吧,我加载一张,就用图片总数去减一,减到0我就加载完毕。看起来很完美,前提是你没遇到IE。 IE的图片总是从缓存文件里去拿,这就造成load方法根本就不执行,只有是新图片才会走load。 服了吧?继续往下看。 --------------------------------------------------------------- 或者是这样: document.getElementById('img').onload=function(){ // 加载完成 }; 看我原生代码一统天下,实际上效果甚微,一次只能处理一个你准备写多少个document,有人说我可以用循环去绑定,经过我测试貌似根本没效果。
你这样搞的结果就是网速很慢的时候,要等好久,然后一下子图片都出来了,不觉得很诡异吗
常见的瀑布流都是先出来一个空白区域加载提示小图片,然后图片慢慢load上去的