04月18, 2016

使用Keystone上传图片到CDN

Keystone是以Express和MongoDB为基础搭建的开源的Node.js CMS和web应用程序平台,快速入门

需求

上传图片到CDN,这里以又拍云为例。(非广告)

分析

keystone自带了很多文件类型,CloudinaryImage、CloudinaryImages、LocalFile、S3 File、AzureFile,除了LocalFile,其他都是第三方服务。但是我们团队使用的是国内的又拍云,于是只能自己开发。

起初我们是用LocalFile上传到服务器,但是这样的图片下载速度稍微慢了点,于是我们决定将图片上传时,直接上传到cdn。

我注意到了keystone自带的域监听,可以监听某个字段修改时的操作。我就是利用这个属性来监听图片上传,并且将图片移至又拍云的。

实现

  • models/lib/upyun.js
var keystone = require('keystone');  
var Types = keystone.Field.Types;  
var _ = require('underscore');  
var fs = require('fs');

var config = {  
    bucket:'service_name',
    operator: 'operator',
    password: 'password',
    endpoint:'v0',
    apiVersion:'legacy',
    domain: 'http://service_name.b0.upaiyun.com',
    baseDir: '/root/'
};
var UPYUN = new require('upyun')(config.bucket, config.operator, config.password, config.endpoint, config.apiVersion);

var uploadPath = '.uploads/';        // 服务器上传路径

// prefix为cdn上传文件夹. 去掉前缀末尾的斜杠"/"
var prefix = (config.domain + config.baseDir).replace(/\/$/, '');    

/**
 * 根据field name获取默认配置
 * @param field
 * @returns {config}
 */
var defaultConifg = function (field) {  
    return {
        type: Types.LocalFile,
        dest: uploadPath,       // 服务器上传路径
        label: '图片',

        // 查询数据会自动拼凑file.href=prefix+'/'+file.filename
        prefix: prefix,     

        // format是用于后台管理输出
        format: function(item, file){       
            return '<img src="'+file.href+'" style="max-width: 120px">';
        },

        watch: field,       // 监听当前字段的值, 如果修改则执行下面的value函数
        value: function () {
            // 由于value方法不支持异步, 所以我们只能通过监听到修改, 
            // 通过isChange标记, 再将上传操作移到presave处理
            // 注意: LocalFile在数据库保存格式是Object, 包含文件信息,
            // 可以console.log(this)查看
            this[field].isChange = true;    

            // 将原来的值继续作为当前字段的值 
            return _.clone(this[field]);
        }
    }
};

/**
 * return field config
 * @param fieldName
 * @param opts
 * @returns {*}
 */
function fieldConfig(fieldName, opts) {  
    if (!fieldName) {
        throw new Error('fieldName is required');
    }
    return _.defaults(opts || {}, defaultConifg(fieldName));
}

/**
 * return pre save config
 * @param value
 * @param field
 * @param next
 */
function preSave(value, field, next) {  
    if (value[field].isChange) {
        var localPath = uploadPath + value[field].filename; 
        var remotePath = config.baseDir + value[field].filename;    

        return UPYUN.uploadFile(remotePath, localPath, value[field].filetype, true, function (err, result) {
            if (err || result.statusCode !== 200) {
                console.error('上传图片失败:', localPath);
                console.error(result);
                return next(err || result);
            }

            // 上传成功
            // 删除服务器临时文件
            fs.unlink(localPath);
            return next();
        });
    }
    next();
}

module.exports = {  
    preSave: preSave,
    fieldConfig: fieldConfig
};
  • models/Post.js
var keystone = require('keystone');  
var Types = keystone.Field.Types;  
var upyun = require('./lib/upyun');

var imageConfig = upyun.fieldConfig('image');    // 获取又拍云图片类型配置

var Post = new keystone.List('Product', {  
    map: {name: 'title'}
});

Post.add({  
    title: {type: Types.Text, initial: true, index: true, label: '文章名'},
    image: imageConfig
});

/**
 * Registration
 */

Post.schema.pre('save', function (next) {  
    upyun.preSave(this, 'image', next); // 上传图片到又拍云
});

Post.register();  

这样每次文章新增或者修改图片,都会被上传到CDN,而且我们采用同名的方式上传,只要加上prefix,file.href就是我们要的图片地址了。本例子只讲到又拍云,我相信同样的方法也适用其他CDN服务的。

感谢阅读,希望对你有帮助。

参考

https://github.com/upyun/node-upyun

本文链接:http://wuyanxin.com/post/upload-file-to-cdn-on-keystone.html

-- EOF --

Comments

请在后台配置评论类型和相关的值。