pinclub/pinclub

View on GitHub
controllers/image.js

Summary

Maintainability
F
2 wks
Test Coverage
/*!
 * nodeclub - controllers/Image.js
 */

/**
 * Module dependencies.
 */

var _ = require("lodash");
var getColors = require('get-image-colors');
var path = require('path');
var request = require("request");
var at = require('../common/at');
var User = require('../proxy').User;
var Image = require('../proxy').Topic;
var Board = require('../proxy').Board;
var ImageCollect = require('../proxy').TopicCollect;
var EventProxy = require('eventproxy');
var tools = require('../common/tools');
var store = require('../common/store');
var config = require('../config');
var structureHelper = require('../common/structure_helper');
var fs = require('fs');
var counter = require('../common/counter');
var ghash = require('ghash');
var imageinfo = require('imageinfo');
var local = require('../common/store_local');

/**
 *
 * @param req
 * @param res
 * @param next
 */
exports.delete = function (req, res, next) {
    //删除话题, 话题作者Image_count减1
    //删除回复,回复作者reply_count减1
    //删除Image_collect,用户collect_Image_count减1

    var image_id = req.params.tid;

    Image.getFullTopic(image_id, function (err, err_msg, image, author, replies) {
        if (err) {
            return res.send({success: false, message: err.message});
        }
        if (!req.session.user.is_admin && !(image.author.equals(req.session.user._id))) {
            res.status(403);
            return res.send({success: false, message: '无权限'});
        }
        if (!image) {
            res.status(422);
            return res.send({success: false, message: '此话题不存在或已被删除。'});
        }

        counter.user(author, 'image', 'sub', function(err, user) {
            image.deleted = true;
            image.save(function (err) {
                if (err) {
                    return res.send({success: false, message: err.message});
                }
                res.send({success: true, message: '话题已被删除。'});
            });
        });

    });
};

// 设为置顶
exports.top = function (req, res, next) {
    var image_id = req.params.tid;
    var referer = req.get('referer');

    if (image_id.length !== 24) {
        res.render404('此话题不存在或已被删除。');
        return;
    }
    Image.getTopic(image_id, function (err, image) {
        if (err) {
            return next(err);
        }
        if (!image) {
            res.render404('此话题不存在或已被删除。');
            return;
        }
        image.top = !image.top;
        image.save(function (err) {
            if (err) {
                return next(err);
            }
            var msg = image.top ? '此话题已置顶。' : '此话题已取消置顶。';
            res.render('notify/notify', {success: msg, referer: referer});
        });
    });
};

// 设为精华
exports.good = function (req, res, next) {
    var imageId = req.params.tid;
    var referer = req.get('referer');

    Image.getTopic(imageId, function (err, image) {
        if (err) {
            return next(err);
        }
        if (!image) {
            res.render404('此话题不存在或已被删除。');
            return;
        }
        image.good = !image.good;
        image.save(function (err) {
            if (err) {
                return next(err);
            }
            var msg = image.good ? '此话题已加精。' : '此话题已取消加精。';
            res.render('notify/notify', {success: msg, referer: referer});
        });
    });
};

// 锁定主题,不可再回复
exports.lock = function (req, res, next) {
    var imageId = req.params.tid;
    var referer = req.get('referer');
    Image.getTopic(imageId, function (err, image) {
        if (err) {
            return next(err);
        }
        if (!image) {
            res.render404('此话题不存在或已被删除。');
            return;
        }
        image.lock = !image.lock;
        image.save(function (err) {
            if (err) {
                return next(err);
            }
            var msg = image.lock ? '此话题已锁定。' : '此话题已取消锁定。';
            res.render('notify/notify', {success: msg, referer: referer});
        });
    });
};

// 收藏主题
exports.collect = function (req, res, next) {
    var image_id = req.body.image_id;

    Image.getTopic(image_id, function (err, image) {
        if (err) {
            return next(err);
        }
        if (!image) {
            res.json({status: 'failed'});
        }

        ImageCollect.getTopicCollect(req.session.user._id, image._id, function (err, doc) {
            if (err) {
                return next(err);
            }
            if (doc) {
                res.json({status: 'failed'});
                return;
            }

            ImageCollect.newAndSave(req.session.user._id, image._id, function (err) {
                if (err) {
                    return next(err);
                }
                res.json({status: 'success'});
            });

            counter.user(req.session.user._id, 'image', 'collect', function (err, user) {
                if (err) {
                    return next(err);
                }
                req.session.user = user;
                image.collect_count += 1;
                image.save();
            });

            // User.getUserById(req.session.user._id, function (err, user) {
            //     if (err) {
            //         return next(err);
            //     }
            //     user.collect_image_count += 1;
            //     req.session.user.collect_image_count += 1;
            //     user.save();
            // });
            //
            // req.session.user.collect_image_count += 1;
            // image.collect_count += 1;
            // image.save();
        });
    });
};

exports.de_collect = function (req, res, next) {
    var image_id = req.body.image_id;
    Image.getTopic(image_id, function (err, image) {
        if (err) {
            return next(err);
        }
        if (!image) {
            res.json({status: 'failed'});
        }
        ImageCollect.remove(req.session.user._id, image._id, function (err, removeResult) {
            if (err) {
                return next(err);
            }
            if (removeResult.result.n === 0) {
                return res.json({status: 'failed'});
            }

            counter.user(req.session.user._id, 'image', 'decollect', function (err, user) {
                if (err) {
                    return next(err);
                }
                req.session.user = user;

                image.collect_count -= 1;
                image.save();

                res.json({status: 'success'});
            });

            // User.getUserById(req.session.user._id, function (err, user) {
            //     if (err) {
            //         return next(err);
            //     }
            //     user.collect_image_count -= 1;
            //     req.session.user.collect_image_count -= 1;
            //     req.session.user = user;
            //     user.save();
            // });
            //
            // image.collect_count -= 1;
            // image.save();
            //
            // res.json({status: 'success'});
        });
    });
};

// DONE (hhdem) 不对val进行inspect
// DONE (hhdem) 上传图片支持 7牛云, 增加对应的配置
// DONE (hhdem) 更改hash参数生成方式为 ghash
// DONE (hhdem) 上传完图片后, 异步返回结果, 而不是等待 hash值 和 colors的获取
function upload (req, res, next) {
    var isFileLimit = false;
    var uploadResult;
    var topicImage = {profile_source: 'upload'};
    req.busboy.on('field', function(fieldname, value, fieldnameTruncated, valTruncated, encoding, mimetype) {

        topicImage[fieldname] = value;
    });

    req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
        file.on('limit', function () {
            isFileLimit = true;

            res.json({
                success: false,
                msg: 'File size too large. Max is ' + config.file_limit
            });
            return;
        });
        if (!topicImage.board || (!!topicImage.board && topicImage.board == 'undefined')) {
            res.json({
                success: false,
                msg: '未选择对应的Board'
            });
            return;
        }
        var ep = new EventProxy();
        let buffers = [];

        // 生成 hash 和 colors 之后保存图片信息
        ep.all('board_update', 'gen_image_hash', 'gen_image_color', function(board){
            topicImage.board = board;
            Image.newAndSaveImage(topicImage, function (err, image) {
                console.info('start new image', new Date());
                if (err) {
                    return next(err);
                }
                topicImage.id = image.id;
                // DONE (hhdem) 上传图片时与Board进行关联绑定, 目前Get图片已经做了关联, 上传图片还未做
                User.getUserById(req.session.user._id, function (err, user) {
                    user.score += 5;
                    user.image_count += 1;
                    user.save();
                    req.session.user.image_count += 1;
                    topicImage.author = user;
                    ep.emit('new_image', topicImage);

                });

            });
        });

        // 获得 图片 buffer 进行hash计算
        file.on('data', function(data) {
            console.info('file data ', data);
            buffers.push(data);
        });

        // 文件内容传输完成后,计算图片的 hash 值和主色调列表
        file.on('end', function() {
            // TODO (hhdem) 图片 hash 和 colors 的生成顺序需要优化, 前台不依赖于后台返回的 hash 和 colors, 而是自己生成
            const fileBuffer = Buffer.concat(buffers);
            ghash(fileBuffer).calculate(function (err, hash) {
                console.info('start image hash', new Date());
                if (err) {
                    console.error(err);
                    return;
                }
                topicImage.image_hash = tools.hexToBinary(hash.toString('hex'));
                ep.emit('gen_image_hash');
            });
            getColors(fileBuffer, mimetype).then(colors => {
                console.info('start image colors', new Date());
                let rgbColor = [];
                let hexColor = [];
                colors.forEach(function (color) {
                    rgbColor.push(color.rgb());
                    hexColor.push(color.toString());
                });
                topicImage.image_colors = hexColor;
                topicImage.image_colors_rgb = rgbColor;
                ep.emit('gen_image_color');
            });
        });

        ep.fail(next);

        ep.all('new_image', function (topicImage) {
            console.info('start return success', new Date());
            res.json({
                success: true,
                data: [structureHelper.image(topicImage)]
            });
            //发送at消息
            at.sendMessageToMentionUsers(topicImage.title, topicImage._id, topicImage.author);
        });

        store.upload(file, {filename: filename, userId: req.session.user._id, filesize: [86, 430]}, function (err, result) {

            if (err) {
                return next(err);
            }
            if (isFileLimit) {
                return;
            }
            console.info('start 7niu', new Date());

            if (config.qn_access && config.qn_access.secretKey !== 'your secret key') {
                // 7牛
                topicImage.image_fixed = result.url + config.qn_access.style[3];
                topicImage.image_430 = result.url + config.qn_access.style[1];
                topicImage.image_86 = result.url + config.qn_access.style[0];
                topicImage.image = result.url;
            } else {
                // 本地上传
                uploadResult = result;
                let filepath = path.resolve(__dirname, '..' + uploadResult.url);
                let extname = filepath.substring(filepath.lastIndexOf('.') + 1);
                let upload_path = uploadResult.url.substring(0, uploadResult.url.lastIndexOf('.'));

                let upload_fixed = upload_path + '_fixed.' + extname;
                if (!uploadResult.rotated) {
                    // 如果没有旋转 则在 fixed 存储原图地址
                    upload_fixed = upload_path + '.' + extname;
                }
                let upload_86 = upload_path + '_86.' + extname;
                let upload_430 = upload_path + '_430.' + extname;
                // DONE (hhdem) 自动旋转图片方向, 此处代码优化性能, 挪到 store_local 中
                topicImage.image_fixed = upload_fixed;
                topicImage.image_86 = upload_86;
                topicImage.image_430 = upload_430;

                topicImage.image = uploadResult.url;

            }

            topicImage.type = 'image';
            topicImage.author = req.session.user;


            Board.getBoardById(topicImage.board, function (err, board) {
                console.info('start board', new Date());
                if (err) {
                    return next(err);
                }
                board.topic_count += 1;
                board.save();
                ep.emit('board_update', board);
            });

        });

    });

    req.busboy.on('finish', function() {
        console.log('Done parsing form!');

    });

    req.pipe(req.busboy);
}

exports.upload = upload;

// 点击chrome 插件的 Get this Picture! 按钮后弹出页面
exports.create = function (req, res, next) {
    // 获得文件类型
    if (!req.query.media) {
        res.render('image/_not_image', {
            tabs: config.tabs
        });
    }

    // 去掉 ? 后面的参数 可解决微信公众号里图片抓取的问题
    let mediaUrl = req.query.media;
    if (mediaUrl.indexOf('http://mmbiz.qpic.cn') >= 0 && !!mediaUrl.indexOf('?')) {
        let ms = _.split(mediaUrl, '?', 2);
        mediaUrl = ms[0];
    }

    request.get({
        url: mediaUrl,
        encoding: null,
        headers: {
            'User-Agent': 'request'
        }
    }, function(error, response, buffer) {
        if (error) {
            if (error.code == 'ECONNRESET') {
                res.render('image/_can_not_get_image', {});
                return;
            } else {
                return next(error);
            }
        }

        var type = imageinfo(buffer);
        // 判断是否为图片类型文件,如果不是则跳转到提示页面
        if (type && type.type == 'image') {
            res.render('image/create_chrome', {
                src: mediaUrl,
                desc: unescape(req.query.title),
                profile_source: req.query.url
            });
        } else {
            res.render('image/_not_image', {
                tabs: config.tabs
            });
        }
    });
};

// 保存由 chrome 插件打开页面提交的图片请求
exports.createFromChrome = function (req, res, next) {
    req.checkBody({
        'media': {
            notEmpty: {
                options: [true],
                errorMessage: '图片地址不能为空'
            }
        },
        'board_id': {
            notEmpty: {
                options: [true],
                errorMessage: 'Board id 不能为空'
            }
        },
        'profile_source': {
            notEmpty: {
                options: [true],
                errorMessage: '来源 URL 不能为空'
            }
        }
    });
    req.getValidationResult().then(function(result) {
        if (!result.isEmpty()) {
            return res.status(400).json({
                success: false,
                err_message: '参数验证失败',
                err: result.useFirstErrorOnly().mapped()
            }).end();
        }
        var isFileLimit = false;
        var uploadResult;
        var ep = new EventProxy();
        var topicImage = {profile_source: req.body.profile_source};
        var boardId = req.body.board_id;
        ep.fail(next);

        // 生成 hash 和 colors 之后保存图片信息
        ep.all('board_update', 'gen_image_hash', 'gen_image_color', function(board){
            topicImage.board = board;
            Image.newAndSaveImage(topicImage, function (err, image) {
                console.info('start new image', new Date());
                if (err) {
                    return next(err);
                }
                topicImage.id = image.id;
                // DONE (hhdem) 上传图片时与Board进行关联绑定, 目前Get图片已经做了关联, 上传图片还未做
                User.getUserById(req.session.user._id, function (err, user) {
                    user.score += 5;
                    user.image_count += 1;
                    user.save();
                    req.session.user.image_count += 1;
                    req.session.user = user;
                    topicImage.author = user;
                    ep.emit('new_image', topicImage);

                });

            });
        });

        // 返回成功信息
        ep.all('new_image', function (topicImage) {
            console.info('start return success', new Date());
            res.json({
                success: true,
                data: [structureHelper.image(topicImage)]
            });
            //发送at消息
            at.sendMessageToMentionUsers(topicImage.title, topicImage._id, topicImage.author);
        });

        // 获得文件类型
        request.get({
            url: req.body.media,
            encoding: null,
            headers: {
                'User-Agent': 'request'
            }
        }, function (error, response, buffer) {
            if (error) {
                if (error.code == 'ECONNRESET') {
                    res.render('image/_can_not_get_image', {});
                    return;
                } else {
                    return next(error);
                }
            }
            var type = imageinfo(buffer);
            var filename = topicImage.title = req.body.desc;
            // 获取图片hash和colors
            const fileBuffer = buffer;
            ghash(fileBuffer).calculate(function (err, hash) {
                console.info('start image hash', new Date());
                if (err) {
                    console.error(err);
                    return;
                }
                topicImage.image_hash = tools.hexToBinary(hash.toString('hex'));
                ep.emit('gen_image_hash');
            });
            getColors(fileBuffer, type.mimeType).then(colors => {
                console.info('start image colors', new Date());
                let rgbColor = [];
                let hexColor = [];
                colors.forEach(function (color) {
                    rgbColor.push(color.rgb());
                    hexColor.push(color.toString());
                });
                topicImage.image_colors = hexColor;
                topicImage.image_colors_rgb = rgbColor;
                ep.emit('gen_image_color');
            });

            // 判断是否为图片类型文件,如果不是则跳转到提示页面
            if (type && type.type == 'image') {
                if (!_.endsWith(req.body.media, '.' + type.format)) {
                    filename += '.' + type.format;
                }
                store.upload(request(req.body.media), {
                    filename: filename,
                    userId: req.session.user._id,
                    filesize: [86, 430]
                }, function (err, result) {
                    if (err) {
                        return next(err);
                    }
                    if (isFileLimit) {
                        return;
                    }
                    console.info('start 7niu', new Date());

                    if (config.qn_access && config.qn_access.secretKey !== 'your secret key') {
                        // 7牛
                        topicImage.image_fixed = result.url + config.qn_access.style[3];
                        topicImage.image_430 = result.url + config.qn_access.style[1];
                        topicImage.image_86 = result.url + config.qn_access.style[0];
                        topicImage.image = result.url;
                    } else {
                        // 本地上传
                        uploadResult = result;
                        let filepath = path.resolve(__dirname, '..' + uploadResult.url);
                        let extname = filepath.substring(filepath.lastIndexOf('.') + 1);
                        let upload_path = uploadResult.url.substring(0, uploadResult.url.lastIndexOf('.'));

                        let upload_fixed = upload_path + '_fixed.' + extname;
                        if (!uploadResult.rotated) {
                            // 如果没有旋转 则在 fixed 存储原图地址
                            upload_fixed = upload_path + '.' + extname;
                        }
                        let upload_86 = upload_path + '_86.' + extname;
                        let upload_430 = upload_path + '_430.' + extname;
                        // DONE (hhdem) 自动旋转图片方向, 此处代码优化性能, 挪到 store_local 中
                        topicImage.image_fixed = upload_fixed;
                        topicImage.image_86 = upload_86;
                        topicImage.image_430 = upload_430;

                        topicImage.image = uploadResult.url;

                    }
                    topicImage.type = 'image';
                    topicImage.author = req.session.user;

                    Board.getBoardById(boardId, function (err, board) {
                        console.info('start board', new Date());
                        if (err) {
                            return next(err);
                        }
                        board.topic_count += 1;
                        board.save();
                        ep.emit('board_update', board);
                    });
                });
            } else {
                res.render('image/_not_image');
            }

        });
    });
};

function uploadAvatar (req, res, next) {
    var isFileLimit = false;

    req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
        file.on('limit', function () {
            isFileLimit = true;

            res.json({
                success: false,
                msg: 'File size too large. Max is ' + config.file_limit
            });
            return;
        });

        store.upload(file, {filename: filename, userId: req.session.user._id, filesize: [54, 34, 24]}, function (err, result) {

            if (err) {
                return next(err);
            }
            if (isFileLimit) {
                return;
            }
            User.getUserById(req.session.user._id, function (err, user) {
                user.avatar = result.url;
                user.save();
                req.session.user.avatar = result.url;
                res.json({
                    success: true,
                    data: [result]
                });
            });
        });

    });

    req.busboy.on('finish', function() {
        console.log('Done parsing form!');

    });

    req.pipe(req.busboy);
};

function uploadForumAvatar (req, res, next) {
    var store = local;
    var isFileLimit = false;

    req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
        file.on('limit', function () {
            isFileLimit = true;

            res.json({
                success: false,
                msg: 'File size too large. Max is ' + config.file_limit
            });
            return;
        });

        store.upload(file, {filename: filename, userId: req.session.user._id, filesize: [54, 34, 24]}, function (err, result) {

            if (err) {
                return next(err);
            }
            if (isFileLimit) {
                return;
            }
            res.json({
                success: true,
                data: [result]
            });
        });

    });

    req.busboy.on('finish', function() {
        console.log('Done parsing form!');

    });

    req.pipe(req.busboy);
};

exports.uploadAvatar = uploadAvatar;
exports.uploadForumAvatar = uploadForumAvatar;