使用渐进式 JPEG 来提升用户体验

今天才认识到原来JPEG文件有两种保存方式他们分别是Baseline JPEG(标准型)和Progressive JPEG(渐进式)。两种格式有相同尺寸以及图像数据,他们的扩展名也是相同的,唯一的区别是二者显示的方式不同。

Baseline JPEG

这种类型的JPEG文件存储方式是按从上到下的扫描方式,把每一行顺序的保存在JPEG文件中。打开这个文件显示它的内容时,数据将按照存储时的顺序从上到下一行一行的被显示出来,直到所有的数据都被读完,就完成了整张图片的显示。如果文件较大或者网络下载速度较慢,那么就会看到图片被一行行加载的效果,这种格式的JPEG没有什么优点,因此,一般都推荐使用Progressive JPEG。

01

Progressive JPEG

和Baseline一遍扫描不同,Progressive JPEG文件包含多次扫描,这些扫描顺寻的存储在JPEG文件中。打开文件过程中,会先显示整个图片的模糊轮廓,随着扫描次数的增加,图片变得越来越清晰。这种格式的主要优点是在网络较慢的情况下,可以看到图片的轮廓知道正在加载的图片大概是什么。在一些网站打开较大图片时,你就会注意到这种技术。

02

渐进式图片带来的好处是可以让用户在没有下载完图片就可以看到最终图像的大致轮廓,一定程度上可以提升用户体验。(瀑布留的网站建议还是使用标准型的)

03

另外渐进式的图片的大小并不会和基本的图片大小相差很多,有时候可能会比基本图片更小。渐进式的图片的缺点就是吃用户的CPU和内存,不过对于现在的电脑来说这点图片的计算并不算什么。

说了这边多下面就改讲讲怎么讲图片保存为或者转化为Progressive JPEG了。

1、PhotoShop

在photoshop中有“存储为web所用格式”,打开后选择“连续”就是渐进式JPEG。

04

2、Linux

检测是否为progressive jpeg : identify -verbose filename.jpg | grep Interlace(如果输出 None 说明不是progressive jpeg;如果输出 Plane 说明是 progressive jpeg。)

将basic jpeg转换成progressive jpeg:> convert infile.jpg -interlace Plane outfile.jpg

3、PHP

使用imageinterlace和imagejpeg函数我们可以轻松解决转换问题。

<?php 
$im= imagecreatefromjpeg('pic.jpg'); 
imageinterlace($im, 1); 
imagejpeg($im, './php_interlaced.jpg', 100); 
imagedestroy($im); 
?>

4、Python

importPIL 
fromexceptions importIOError 
 
img =PIL.Image.open("c:\\users\\biaodianfu\\pictures\\in.jpg") 
destination ="c:\\users\\biaodianfu\\pictures\\test.jpeg"
try: 
img.save(destination, "JPEG", quality=80, optimize=True, progressive=True) 
exceptIOError: 
PIL.ImageFile.MAXBLOCK =img.size[0] *img.size[1] 
img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)

5、jpegtran

jpegtran -copy none -progressive <inputfile> <outputfile>

6、C#

using(Image source = Image.FromFile(@"D:\temp\test2.jpg")) { 
ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().First(c => c.MimeType == "image/jpeg"); 
EncoderParameters parameters = newEncoderParameters(3); 
parameters.Param[0] = newEncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L); 
parameters.Param[1] = newEncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod, (int)EncoderValue.ScanMethodInterlaced); 
parameters.Param[2] = newEncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod, (int)EncoderValue.RenderProgressive); 
source.Save(@"D:\temp\saved.jpg", codec, parameters); 
}

注意PHP程序里的敏感信息

何为敏感信息?简单点来说就是你不想让别人知道的信息,比如说数据库的地址,用户名,密码等等,此类信息往往知道的人越少越好。

通常,PHP程序里的配置文件大致如下所示:

<?php

return array(
    'database' => array(
        'host'     => '192.168.0.1',
        'user'     => 'administrator',
        'password' => 'e1bfd762321e409cee4ac0b6e841963c',
    ),
);

?>

有时候出于某些原因,比如说代码审查,亦或者合作开发等等,第三方需要获取代码版本仓库的读权限,一旦授权,数据库的地址,用户名,密码等敏感信息就暴露了。当然也可以不在代码版本仓库里保存配置文件,取而代之是撰写文档进行说明,但我不喜欢这样的方法,因为如此一来,代码本身是不完整的。

如何解决此类问题呢?最直接的方法是把敏感信息从代码中拿掉,换个地方保存。具体保存到哪里呢?有很多选择,比如说通过nginx的fastcgi_param来设置:

fastcgi_param DATABASE_HOST 192.168.0.1;
fastcgi_param DATABASE_USER administrator;
fastcgi_param DATABASE_PASSWORD e1bfd762321e409cee4ac0b6e841963c;

经过这样的映射后,我们的代码就不会直接包含敏感信息了:

<?php

return array(
    'database' => array(
        'host'     => $_SERVER['DATABASE_HOST'],
        'user'     => $_SERVER['DATABASE_USERNAME'],
        'password' => $_SERVER['DATABASE_PASSWORD'],
    ),
);

?>

此外,还可以通过php-fpm的env指令来设置:

env[DATABASE_HOST] = 192.168.0.1
env[DATABASE_USERNAME] = administrator
env[DATABASE_PASSWORD] = e1bfd762321e409cee4ac0b6e841963c

需要说明的一点是,这个设置必须放在主配置文件php-fpm.conf里,不能放到include指令设置的子配置文件里,否则会报错:「Array are not allowed in the global section」;另外一点,虽然是通过env设置的,但结果还是在$_SERVER里,而不是$_ENV。

说明: @Laruence 提醒了我,如果配置信息通过nginx的fastcgi_param来设置的话,当nginx和php交互时,会带来大量的数据传输(如此看来通过php-fpm的env来设置相对更有优势),鸟哥建议使用独立的扩展来搞定,比如「hidef」。

通过nginx和php-fpm配置文件来解决问题的话,有一个缺点,仅对Web有效,如果通过命令行来运行,那么无法在$_SERVER里获取相关信息,不过这不算什么难事儿,只要写个公共的脚本正则匹配一下nginx或者php-fpm的配置文件,就可以动态的把这些信息映射到命令行环境,具体怎么搞就留给大家自己操作吧。

代码干净了,剩下的工作就是如何确保nginx或php-fpm配置文件的安全了,不过和代码比起来,nginx或php-fpm配置文件并不需要很多人有权限,所以相对更容易管理。

javascript文件中获取当前文件路径

在开发javascript插件的过程中,我们有时候需要获取当前JS文件的路径,用于自动加载一些图片、CSS等外部资源,但是javascript文件中并没有像PHP那样的__FILE__常量来供我们取得当前文件路径。
在开发javascript插件的过程中,我们有时候需要获取当前JS文件的路径,用于自动加载一些图片、CSS等外部资源,但是javascript文件中并没有像PHP那样的__FILE__常量来供我们取得当前文件路径。研究了下,发现我们可以用下面几种方法来实现:

1,在jQuery中获取当前JS文件路径

在jQuery中获取当前JS文件的路径比较简单,只需要如下一行代码就搞定。

var __FILE__ = $("script").last().attr("src");

复制代码

注意,我们一般把这行代码放到文件的开头,让文件加载的时候就立即执行,这样页面中的script元素中,当前文件恰好是最后一个script。我们千万不要把这行代码放到

$(document).ready();
$(function(){   });

复制代码
中运行,因为如果放到这些语句中去的话,页面的DOM对象已经加载完毕,当前的script就不一定是最后一个script了,从而导致获取的路径不正确。

2,原生javascript获取当前JS文件路径

在原生的javascript中,要获取当前JS文件路径有两种方法。第一种其实思路和在jQuery中是一样的:

var __FILE__, scripts = document.getElementsByTagName("script");
__FILE__ = scripts[scripts.length - 1].getAttribute("src");

第二种方法是利用浏览器的异常处理机制,但是这种方法不支持IE10以下版本的IE浏览器:

    var __FILE__;
    try {
        throw Error("获取JS路径有误");
    }catch(ex){
        if(ex.fileName) //Firefox
            __FILE__ = ex.fileName;
        else if(ex.stack)//Chrome 或 IE10
            __FILE__ = (ex.stack.match(/at\s+(.*?):\d+:\d+/)||['',''])[1];
        else if(ex.sourceURL)//Safari
            __FILE__ = ex.sourceURL;
    }

在原生的javascript中获取当前文件路径的时候,建议使用第一种方法,兼容所有浏览器,第二种方法仅供参考。