设为首页 收藏本站
查看: 880|回复: 0

[经验分享] 使用nodejs搭建图片服务器(一)

[复制链接]

尚未签到

发表于 2017-2-22 11:54:34 | 显示全部楼层 |阅读模式
背景
    当我们开发一个Web项目的时候,为了将图片管理与web服务分离开,通常都会搭建一个图片服务器。
    之所以选择nodejs是因为使用nodejs来搭建web项目相当简单而且快速,虽然这个图片服务器很简单,也有很多人会认为使用nodejs来当图片服务器不合适,但是当我们的应用没有达到非常大的程度的情况下,其实nodejs还是够用的。
    会使用到的工具如下:


  • nodejs
  • express(nodejs mvc框架)
  • body-parser(express middleware)
  • gm(nodejs中用来处理图片的)
  • uuid(nodejs中用于生成uuid)
  • underscore(nodejs数据处理库)
  • ImageMagick(gm会调用该程序处理图片)
    那么接下来就来尝试实现这个简易的图片服务器吧, ^_^
搭建项目
    首先要使用express来搭建项目,由于图片服务器的功能相对简单,只有2个功能:1、获取图片资源 2、上传图片,因此对应express只需要使用到bodyParser这样的组件,代码大致如下:

//app.js
var app = require('express')();
process.app = app;//方便在其他地方使用app获取配置
require('./config')(__dirname, app);//所有配置
var mode = app.get('mode');
require('./controller/' + mode + 'Controller.js');
var config = app.get(mode);
require('http').createServer(app).listen(config.port, function () {
console.log('%s已经启动,正监听:%s', config.name, config.port);
});
//config.js
var path = require('path');
var OPTIONS = {
targetDir: function (app) {
return path.join(app.get('rootDir'), 'img');
},
read: {
name: '<<图片服务器(读取)>>',
port: 80,
'default': 'default.jpg',
sizeReg: /(\w+)-(\d+)-(\d+)\.(\w+)$/
},
save: {
name: '<<图片服务器(保存)>>',
port: 9990
},
mode: 'read',
contentType: {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'png': 'image/png'
}
};
module.exports = function (rootDir, app) {
app.set('rootDir', rootDir);
var $ = require('underscore');
$.each(OPTIONS, function (v, k) {
app.set(k, typeof v === 'function' ? v(app) : v);
});
app.use(require('body-parser')())
};

  上面独立出了config.js是为了将所有的配置放在一起方便维护,其次配置中的mode是为了区分当前是一个读取还是存储服务器。
获取图片
    这是图片服务器的其中一个功能,用户根据url获取图片服务器内存储的图片,当后台接收这个请求后,首先判断图片上会否存在,如果存在则返回对应的图片否则返回默认的图片,大致代码如下:

var path = require('path');
var fs = require('fs');
var gm = require('gm').subClass({ imageMagick: true });//默认的情况下 gm使用的是另外一个图片处理程序
var app = process.app;
var config = app.get('read');
var targetDir = app.get('targetDir');
app.get('/:filename', function (req, res) {
var filePath = path.join(targetDir, req.params.filename);
fs.exists(filePath, function (exists) {
res.sendfile(exists ? filePath : path.join(targetDir, config.default));
});
});

    使用以上的代码便可以对图片进行读取了,但是只能对targetDir目录下的文件进行读取且没有对文件类型进行判断,对文件类型的判断比较简单只要在方法执行之前对文件扩展名进行判断即可,至于增加了文件夹结构的话,跟直接从targetDir目录下读取是差不多的流程,稍微调整一下代码:

var contentTypes = app.get('contentType');
app.get('/:filename', function (req, res) {
sendFile([], req.params.filename, res);
});
app.get('/:folder/:filename', function (req, res) {
sendFile([req.params.folder], req.params.filename, res);
});
app.get('/:folder1/:folder2/:name', function (req, res) {
sendFile([req.params.folder1, req.params.folder2], req.params.filename, res);
});
function sendFile(folders, filename, res) {
var ext = path.extname(filename).substr(1);
if (!contentTypes[ext])
return res.sendfile(getFilePath());
folders.push(filename);
var filePath = getFilePath(path.join.apply(path, folders));
fs.exists(filePath, function (exists) {
res.sendfile(exists ? filePath : getFilePath());
});
}
function getFilePath(filename) {
return path.join(app.get('targetDir'), filename || config.default);
}

    接下来假设一个场景,如果上传了一张800*600的图片,但是在页面上显示的时候,也许我只想显示一张200*150的缩略图,这时候gm就该登场了,我们可以使用gm来为800*600的图片临时生成一张200*150的图片,并以buffer的形式回传给客户端,大致代码如下:

//判断请求图片是否存在
if (exists)                                                     
return res.sendfile(filePath);     
var groups = filename.match(config.sizeReg);                    
if (!groups)                                                   
return res.sendfile(getFilePath());                        
folders[len] = groups[1] + "." + groups[4];              
filePath = getFilePath(path.join.apply(path, folders));         
var width = parseInt(groups[2]);                                
var height = parseInt(groups[3]);   
//判断原始图是否存在                           
fs.exists(filePath, function (exists) {                        
if (!exists)                                                
filePath = getFilePath();                              
var gm = gm(filePath);                                      
if (width > 0 && height > 0)                                
gm.resize(width, height);                              
gm.toBuffer(function (err, buffer) {                        
if (err)                                                
return res.sendfile(getFilePath());                 
res.set('Content-Type', contentTypes[ext]);            
res.send(buffer);                                       
});                                                         
});                                                            

上传图片
  由于图片服务器只提供最简单的功能,支持文件上传,但是并不会对上传的文件制作水印以及其他的处理,也不会将此功能开放到外网,因此图片上传服务器是在内网的,只能从web服务器那边处理图片以后上传到图片服务器,图片服务器对其进行存储并返回存储后的图片路径,大致代码如下:

var buf = require('buffer');
var fs = require('fs');
var path = require('path');
var util = require('util');
var gm = require('gm').subClass({ imageMagick: true });
var uuid = require('uuid');
var app = process.app;
var contentTypes = app.get('contentType');
/*
请求包含如下参数:
@ext图片扩展名
@buffer图片buffer数据
@folder文件夹,格式:/aa/bb
*/
app.post('/', function (req, res) {
var ext = req.body.ext;
var buffer = req.body.buffer;
if (!(ext && buffer && contentTypes[ext]))
return res.json({ success: false });
var pathArgs = req.body.folder.replace(/\n/g, '');
if (pathArgs)
pathArgs = pathArgs.substr(1).split('/');
else
pathArgs = [''];
createDir(pathArgs);
pathArgs.push('');
var filePath = createPath(pathArgs, ext);
gm(new buf.Buffer(JSON.parse(buffer))).write(filePath, function (err) {
if (err)
return res.json({ success: false });
res.json({ success: true, data: util.format('\\%s.%s', pathArgs.join('\\'), ext) });
});
});
function createDir(pathArgs) {
if (pathArgs[0]) {
var dir = path.join(app.get('targetDir'), path.join.apply(path, pathArgs));
var exists = fs.existsSync(dir);
if (!exists)
fs.mkdirSync(dir);
}
}
function createPath(pathArgs, ext) {
var args = [app.get('targetDir')];
pathArgs[pathArgs.length - 1] = uuid.v1().replace(/-/g, '');
args.push.apply(args, pathArgs);
var filePath = path.join.apply(path, args) + '.' + ext;
return fs.existsSync(filePath) ? createPath(pathArgs, ext) : filePath;
}

结尾
  到这里我们就完成了一个nodejs版本的图片服务器了,因为尽可能将不需要的功能剥离到了其他的项目中去,因此图片服务器的功能就变得很简单、单一了。
  对于图片获取服务器生成临时大小的文件,可以先缓存起来,并结合leasing模式进行管理,频繁使用的情况下可以翻倍增加它的租约,到期则直接释放。
  而图片存储服务器,如果对于某些应用上传的图片都会生成固定系列大小的情况下, 可以使用gm对原始图进行二次处理,根据规则批量生成各种大小的图,至于图片的名字可以采用一些规则,这样返回的话,依然是存储后的原始图,而且格式则可以通过获取服务器获取,无需再进行缓存了。
  由于大部分的代码已经提供了,请不要再跟我要什么源码之类的哦,大家可以自己尝试搭建一下,今天就到这里了,如果有什么问题请告知我,谢谢各位。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-345729-1-1.html 上篇帖子: Nodejs学习笔记(十四)— Mongoose介绍和入门 下篇帖子: 【原】nodejs全局安装和本地安装的区别
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表