使用 Node.js 实现图片的动态裁切及算法实例代码详解
背景&概览
目前常见的图床服务都会有图片动态裁切的功能,主要的应用场景用以为各种终端和业务形态输出合适尺寸的图片。
一张动辄以MB为计量单位的原始大图,通常不会只设置一下显示尺寸就直接输出到终端中,因为体积太大加载体验会很差,除了影响加载速度还会增加终端设备的内存占用。所以要想在各种终端下都能保证图片质量的同时又确保输出合适的尺寸,那么此时就需要根据图片URL来对原始图片进行裁切,然后动态生成并输出一张新的图片。
URL的设计
图片URL需要包含图片id、尺寸、质量等信息。有两种类型的图片URL,分别是原图URL和带动态裁切信息的URL。
//原图URL http://example.com/$imgId //带裁切信息的图片URL http://example.com/$cropType/$width_$height_$quality/$imgId
来分析一下上面URL中的变量:
- $imgId
- $cropType
- $width
- $height
- $quality
那么一张图片id为4b2d4edcc1f82452的原图URL应该是:
http://example.com/4b2d4edcc1f82452.jpg
如果想要一张该图800×600的版本,裁切的URL大致是下面这样的:
http://example.com/es/800_600_/4b2d4edcc1f82452.jpg
裁切算法
该来说说以上URL背后的算法了。在Node.js中可以使用著名的图片裁切库GM,该库是基于imagemagick和graphicsmagick底层库的封装。
最常见的裁切算法是等比例裁切,等比裁切的算法需要至少给出裁切目标图片的宽度和高度的其中一个,如果图片限宽就给出宽度,限高就给出高度,如果两个参数都有,就需要确保裁切的目标宽高相对于原始的宽高是按比例计算的,否则裁切的结果就会出现拉伸。
vargm=require('gm');
//裁切的最小尺寸
varminSize=48;
vardefaultQuality=90;
/**
*等比例缩放equalscaling
*@param{String}原文件路径
*@param{String}新文件路径
*@param{String}缩放规则
*@return{promise}
*/
vares=function(src,dest,rules){
returnnewPromise(function(resolve,reject){
//900_600_90=>宽度900/高度600/品质90
rules=rules.split('_');
if(rules.length!==3){
returnreject(newError('Resizerulesinvalid'));
}
//解析裁切的目标宽高
letresizeWidth=parseInt(rules[0]);
letresizeHeight=parseInt(rules[1]);
letquality=parseInt(rules[2])||defaultQuality;
constreadStream=fs.createReadStream(src);
constwriteStream=fs.createWriteStream(dest);
gm(readStream)
.size({
bufferStream:true
},function(err,size){
if(err){
returnreject(err);
}
constorigWidth=size.width;
constorigHeight=size.height;
letresizeResult;
//缩放的宽度和高度做最大最小值限制
if(resizeWidth){
if(resizeWidth>origWidth*1.5){
resizeWidth=Math.floor(origWidth*1.5);
}
elseif(resizeWidthorigHeight*1.5){
resizeHeight=Math.floor(origHeight*1.5);
}
elseif(resizeHeight
说说几个重要的API:
quality设置图片的质量,GM图片质量范围是0-100,默认的质量是75。
interlace用于设置图片在显示器上加载时的显示方式,当然显示方式本身还要受图片本身的影响。
unsharp用来设置图片的锐度,将一张大图缩放成一张小图时,会损失很多像素,需要适当的增加图片锐度来保证图片的质量。关于unsharp的使用,详见UsingImageMagicktomakesharpweb-sizedphotographs。
等比例裁切严格来说实际上还只是对图片进行缩放,并未动用图片裁切的API。
还有一种比较常见的裁切方式,会先将图片等比例缩放后再从中心裁切,裁切出来的图片是一个正方形,这样能尽可能保证图片的内容。
/*
*等比例缩放后从中心裁切equalscalingcropcenter(正方形裁切)
*@param{String}原文件路径
*@param{String}新文件路径
*@param{String}缩放规则
*@return{promise}
*/
varescc=function(src,dest,rules){
returnnewPromise(function(resolve,reject){
//600_90=>宽度600/高度600/品质90
rules=rules.split('_');
if(rules.length!==2){
returnreject(newError('Resizerulesinvalid'));
}
letcropSize=parseInt(rules[0]);
letquality=parseInt(rules[1])||defaultQuality;
constreadStream=fs.createReadStream(src);
constwriteStream=fs.createWriteStream(dest);
if(!cropSize){
reject(newError('Cropparamsinvalid'));
return;
}
gm(readStream)
.size({
bufferStream:true
},function(err,size){
if(err){
reject(err);
return;
}
constorigWidth=size.width;
constorigHeight=size.height;
letcropX=0;
letcropY=0;
letresizeWidth;
letresizeHeight;
letresizeResult;
//裁切的宽度和高度做最大最小值限制
if(cropSize>origWidth){
cropSize=origWidth;
}
elseif(cropSize>origHeight){
cropSize=origHeight;
}
elseif(cropSizeorigHeight){
resizeWidth=cropSize/origHeight*origWidth;
resizeHeight=cropSize;
cropX=Math.floor((resizeWidth-cropSize)/2);
cropY=0;
}
else{
resizeHeight=cropSize/origWidth*origHeight;
resizeWidth=cropSize;
cropX=0;
cropY=Math.floor((resizeHeight-cropSize)/2);
}
resizeResult=this.resize(resizeWidth,resizeHeight);
resizeResult
.quality(quality)
.interlace('line')//使用逐行扫描方式
.crop(cropSize,cropSize,cropX,cropY)
.unsharp(2,0.5,0.5,0)
.stream()
.on('end',resolve)
.pipe(writeStream);
});
});
};
上面的crop就是对图片进行裁切。当然除了中心裁切,还能延伸出顶部裁切,底部裁切等,相对来说使用场景要少很多。
结语
在服务的实际应用中,还会做一些优化,比如对服务的接口做一些安全限制,确保该接口不会被刷,裁切本身是比较消耗资源的操作。由于裁切操作比较耗资源,那么相同的尺寸应该保证只有一次裁切操作,这样只有第一次请求裁切图片才会真正有裁切操作,后续的访问就直接读取原来就裁切好的实体文件即可。
以上所述是小编给大家介绍的使用Node.js实现图片的动态裁切及算法实例代码详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。