我在访问时光网、网易云音乐等网站时,发现将它们页面中的一些图片 URL 修改一下就可以得到不同尺寸的图片,于是思考了其实现方案,我的思路是:
,对用户请求的 URL 进行重写,然后利用图片处理类库对图片进行处理,接着缓存该尺寸图片并输出到浏览器。使用 PHP 和 Node.js 实现了一遍,基本达到了需要的效果。
- URL Rewrite + 实时处理 + 缓存
URL 重写
这里 Nginx 主要是做一个 URL 重写和反向代理的功能,配置如下所示:
- location~ / upload / {
- if ($request_uri~ * ^/upload/ (. + ) _(\d + ) x(\d + )\. (jpg | png | gif) $) {
- set $src $1;
- set $w $2;
- set $h $3;
- set $t $4;
- rewrite. / resize ? src = $src & w = $w & h = $h & type = $t
- break;
- }
- proxy_pass http: //127.0.0.1:3000;
- }
这里说明一下:Nginx 监听本地的 80 端口,Node.js 监听的是 3000 端口。当用户访问类似
的地址时,便会被代理到
- http://127.0.0.1/upload/147332819224704_400x300.jpg
访问,看起来像是访问一张图片,其实不然。 图片实时处理 我们在 Node.js 中实时处理图片,进行缩放、模糊、水印等操作,之后将其缓存起来并输出到浏览器。Node.js 自身 API 并不擅长图片的处理,我们可以借助第三方类库来实现,这里推荐 GraphicsMagick 或 ImageMagick,使用它们之前先安装
- http://127.0.0.1:3000/resize?src=147332819224704&w=400&h=300&type=jpg
模块:
- gm
- npm install gm --save
接着便可以使用 GraphicsMagick 了,该模块的 API 可以参考。图片处理的实现如下:
- app.get('/resize',function(req,res){
- var src = req.query.src,
- width = req.query.w,
- height = req.query.h,
- type = req.query.type;
- var imgFile = uploadDir+src+'.'+type;
- var notFound = '不好意思,该图片不存在或已被删除!';
- var thumb = getThumbImg(src,width,height,type);
- if(isFileExists(imgFile)){
- if(isFileExists(thumb)){
- res.type(type).sendFile(__dirname+'/'+thumb);
- }else{
- imgResize(imgFile,thumb,width,height,type,res);
- }
- }else{
- res.status(404).send(notFound);
- }
- });
- function imgResize(f,th,w,h,t,r){
- var imgSize = sizeOf(f);
- if(!w||!h||w>=imgSize.width||h>=imgSize.height){
- r.type(t).sendFile(__dirname+'/'+f);
- }else{
- imageMagick(f)
- .resize(w,h,'!')
- .stream(function(err, stdout, stderr) {
- if (err) {
- console.log(err);
- res.end();
- }
- var ws = fs.createWriteStream(th);
- stdout.pipe(ws);
- r.type(t);
- stdout.pipe(r);
- });
- }
- }
- function isFileExists(filePath){
- var bool = !0;
- try{
- fs.accessSync(filePath,fs.constants.F_OK);
- }catch(err){
- bool = !1;
- }
- return bool;
- }
如上代码所示,当用户访问
时,如果 147332819224704 这张图片存在,且 400x300 这个尺寸也存在,则直接输出这张图片,如不存在,则生成一张该尺寸的图片保存并输出到浏览器。如果提供的尺寸超出了图片的原始尺寸,则直接输出原图。我们不仅可以修改尺寸,也可以进行模糊、打水印等操作,这里就不多介绍了。
- http://127.0.0.1/upload/147332819224704_400x300.jpg
如果不用 Nginx 反向代理也是可以的,使用 express 的正则路由实现,如下所示:
- app.get(/^\/upload\/(.+)_(\d+)x(\d+)\.(jpg|png|gif)$/,
- function(req, res) {
- var src = RegExp.$1,
- width = RegExp.$2,
- height = RegExp.$3,
- type = RegExp.$4;
- var imgFile = uploadDir + src + '.' + type;
- var notFound = '不好意思,该图片不存在或已被删除!';
- var thumb = getThumbImg(src, width, height, type);
- if (isFileExists(imgFile)) {
- if (isFileExists(thumb)) {
- res.type(type).sendFile(__dirname + '/' + thumb);
- } else {
- imgResize(imgFile, thumb, width, height, type, res);
- }
- } else {
- res.status(404).send(notFound);
- }
- });
首先得搭建 windows 下 php 开发环境,我本人用的是 apache2+php5.6,具体的搭建步骤网上一大堆,便不再累述。
开启 apache rewrite 功能
首先我们得开启 Apache rewrite 模块功能,去掉配置文件 http.conf 中 LoadModule rewrite_module modules/mod_rewrite.so 前面的注释,然后设置 Directory 块下 AllowOverride All,可能有多处,接着重启 Apache 服务。
配置. htaccess 文件
在 DocumentRoot 目录下,新建
文件,如果创建不了,可以先创建一个文本,然后另存为,在弹出的对话框文件名处填写 ".htaccess" 即可。之后,编写 URL 重写规则,如下所示:
- .htaccess
- RewriteEngine on
- RewriteCond %{REQUEST_FILENAME} !-f
- RewriteRule ^upload/(.+)_([0-9]+)x([0-9]+)\.(jpg|png|gif)$ resize.php?src=$1&w=$2&h=$3&type=$4 [L]
将类似
地址重写为
- http://127.0.0.1/upload/147332819224704_400x300.jpg
。 功能实现
- http://127.0.0.1/resize.php/src=147332819224704&w=400&h=300&type=jpg
- function getThumbImg($src,$w,$h,$type)
- {
- global $thumbs;
- return $_SERVER['DOCUMENT_ROOT'].$thumbs.$src.'_'.$w.'_'.$h.'.'.$type;
- }
- function imgResize($f,$th,$w,$h,$t)
- {
- $imagick = new Imagick();
- $imagick->readImage($_SERVER['DOCUMENT_ROOT'].'\\'.$f);
- $width = $imagick->getImageWidth();
- $height = $imagick->getImageHeight();
- if(!$w||!$h||$w>=$width||$h>=$height){
- header('Content-Type:image/'.$t);
- echo file_get_contents($_SERVER['DOCUMENT_ROOT'].'\\'.$f);
- }else{
- $imagick->stripImage();
- $imagick->cropThumbnailImage($w, $h);
- $imagick->writeImage($th);
- header('Content-Type:image/'.$t);
- echo $imagick->getImageBlob();
- $imagick->clear();
- $imagick->destroy();
- }
- }
- $uploadDir = "uploads/images/";
- $thumbs = "uploads/thumbs/";
- $src = $_GET['src'];
- $width = $_GET['w'];
- $height = $_GET['h'];
- $type = $_GET['type'];
- $imgFile = $uploadDir.$src.'.'.$type;
- $notFound = '不好意思,该图片不存在或已被删除!';
- $thumb = getThumbImg($src,$width,$height,$type);
- if(file_exists($imgFile)){
- if(file_exists($thumb)){
- header('Content-Type:image/'.$type);
- echo file_get_contents($thumb);
- }else{
- imgResize($imgFile,$thumb,$width,$height,$type);
- }
- }else{
- header("HTTP/1.0 404 Not Found");
- header("status: 404");
- echo $notFound;
- }
至此,图片访问实时处理也就完成了。其实大部分图片服务器都需要这样的功能,而不是事先生成好几套尺寸的图片。
来源: