UEditor+nodejs+seaweedfs图片服务器搭建

参考链接:
http://blog.coinidea.com/web%E5%BC%80%E5%8F%91/nodejs-1161.html

1. UEditor+nodejs图片上传
UEditor是百度开源的富文本编辑器,功能比较强大。
下载地址是:http://ueditor.baidu.com/website/download.html
目前提供:PHP、ASP、.Net、JSP版本。UEditor主要是以前端HTML、CSS、JS为主的,之所以按各种动态语言再细分版本,我的理解是主要是在图片上传这一涉及到与服务器交互的功能上。
在众多版本中,没有提供nodejs的版本,下面将介绍如何用PHP版本的UEditor改造成nodejs版本的UEditor。
咨询查看PHP版本的所有请求,发现action参数值包括config(配置文件)、uploadimage(图片上传)、listimage(在线管理)、catchimage(抓取图片),所以只需要重写这4个请求就基本上实现了我们的需求。
1.1 修改UEditor的ueditor.config.js的serverUrl属性:

serverUrl: '/ue/uploads'

1.2 将ueditor/php/config.json文件名修改为config.js并移动到ueditor目录下。
1.3 接下来主要在nodejs端编写对应的四个action就好,图片上传使用了connect-busboy中间件。代码如下:

// 图片上传
var path = require('path');
var uploadsPath = path.resolve('public/uploads') + '/';//存储图片的路径
var busboy = require('connect-busboy');
app.use(busboy({
    limits: {
        fileSize: 10 * 1024 * 1024 // 10MB
    }
}));
var action = {
    /// 上传图片
    uploadimage: function (req, res) {
        var fstream;
        req.pipe(req.busboy);
        req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
            var filesize = 0;
            var ext = path.extname(filename);
            var newFilename = (new Date() - 0) + ext;
            fstream = fs.createWriteStream(uploadsPath + newFilename);
            file.on('data', function (data) {
                filesize = data.length;
            });
            fstream.on('close', function () {
                console.log(JSON.stringify({
                    "originalName": filename,
                    "name": newFilename,
                    "url": '/uploads/' + newFilename,
                    "type": ext,
                    "size": filesize,
                    "state": "SUCCESS"
                }));
                res.send(JSON.stringify({
                    "originalName": filename,
                    "name": newFilename,
                    "url": '/uploads/' + newFilename,
                    "type": ext,
                    "size": filesize,
                    "state": "SUCCESS"
                }));
            });
            file.pipe(fstream);
        });
    },
    /// 获取配置文件
    config: function (req, res) {
        return res.redirect('/js/UEditor/config.js');
    },
    /// 在线管理
    listimage: function (req, res) {
        fs.readdir(uploadsPath, function (err, files) {
            var total = 0, list = [];
            files.sort().splice(req.query.start, req.query.size).forEach(function (a, b) {
                /^.+.\..+$/.test(a) &&
                list.push({
                    url: '/uploads/' + a,
                    mtime: new Date(fs.statSync(uploadsPath + a).mtime).getTime()
                });
            });
            total = list.length;
            res.json({state: total === 0 ? 'no match file' : 'SUCCESS', list: list, total: total, start: req.query.start});
        });
    },
    /// 抓取图片(粘贴时将图片保存到服务端)
    catchimage: function (req, res) {
        var list = [];
        req.body.source.forEach(function (src, index) {
            http.get(src, function (_res) {
                var imagedata = '';
                _res.setEncoding('binary');
                _res.on('data', function (chunk) {
                    imagedata += chunk
                });
                _res.on('end', function () {
                    var pathname = url.parse(src).pathname;
                    var original = pathname.match(/[^/]+\.\w+$/g)[0];
                    var suffix = original.match(/[^\.]+$/)[0];
                    var filename = Date.now() + '.' + suffix;
                    var filepath = uploadsPath + 'catchimages/' + filename;
                    fs.writeFile(filepath, imagedata, 'binary', function (err) {
                        list.push({
                            original: original,
                            source: src,
                            state: err ? "ERROR" : "SUCCESS",
                            title: filename,
                            url: '/uploads/catchimages/' + filename
                        });
                    })
                });
            })
        });
        var f = setInterval(function () {
            if (req.body.source.length === list.length) {
                clearInterval(f);
                res.json({state: "SUCCESS", list: list});
            }
        }, 50);
 
    }
};
app.get('/ue/uploads',function (req, res) {
    action[req.query.action](req, res);
});
app.post('/ue/uploads', function (req, res) {
    action[req.query.action](req, res);
});

以上主要参考了博客:http://www.xiaoboy.com/detail/1341545081.html
2. GoLang的安装与配置
1中UEditor的图片上传到哪儿nodejs服务器中的/public/uploads/文件夹中,如果需要高存储、可移植、可扩展等特性的图片服务器,则需要配置专门的服务器。本文选用的是开源的图片(文件)分布式服务器seaweedfs。
Github地址:https://github.com/chrislusf/seaweedfs
该服务器使用GoLang编写,所以需要安装配置GoLang。中国的下载地址:http://www.golangtc.com/download,根据自己的操作系统选择特定的包下载即可。本文演示的windows7 x64下载安装。傻瓜式安装好之后,需要配置一下环境变量:

GOROOT=D:\Go16
GOPATH=D:\ImageServer\seaweedfs
PATH=D:\Go16\bin

GOPATH环境变量,这个变量很重要,我自己写的代码要放到这个变量中配置的目录中,go编译器才会找到并编译
3. seaweedfs的编译运行
Github上吧seaweedfs的代码clone下来,然后安装makefile中的执行也可以直接直接makefile。在seaweedfs中执行控制台:

go clean -i -v ./go/weed/
#rm –f weed #for linux
go get -v –d ./go/weed
go build -v -o weed ./go/weed

其中在get依赖的时候有可能有些依赖包不能下载,需要自己手动下载,放入:\seaweedfs\src目录中。
推荐一个go下载包地址:https://gopm.io/download?pkgname=golang.org/x/net
Buid之后会在seaweedfs中生成一个weed,也可以将其改名为weed.exe。这个时候就可以启动weed。以本机为演示:

weed master

weed volume –dir=”./tmp/data1” –max=5 –mserver=”localhost:9333” –port=8080 &
[![image](http://upload-images.jianshu.io/upload_images/13720662-4e045e11d550fd66.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 "image003")](http://devhu-wordpress.stor.sinaapp.com/uploads/2016/05/image0031.png) 

按照github的描述上传、下载图片都没有问题。至此基本的图片服务器搭建完成。

修改nodejs的图片上传代码如下:

// 图片上传
var path = require('path');
var multipart = require('connect-multiparty');
var multipartMiddleware = multipart();
function curlPostAssign(res, filepath, filename, filesize) {
    request.post(config.url_image + 'dir/assign',{},
        function (error, response, body) {
            if (!error && response.statusCode == 200) {
                assign = eval('(' + body + ')');
                var result = {};
                result.res = res;
                result.json = JSON.stringify({
                    "originalName": filename,
                    "name": assign.fid,
                    "url": '/res?fid=' + assign.fid,
                    "type": ext,
                    "size": filesize,
                    "state": "SUCCESS"
                });
                curlPostWrite(assign.fid, filepath, assign.publicUrl, result);
                var ext = path.extname(filename);
            }else{
                console.log("Image server assign error...")
            }
        });
}
function curlPostWrite(fid, tmp_file, publicUrl, result) {
    var files = [
        {urlKey: "file1", urlValue: tmp_file}
    ]
    var options = {
        host: publicUrl.split(":")[0] ,
        port: publicUrl.split(":")[1] ,
        method: "POST",
        path: "/" + fid
    }
 
    var req = http.request(options, function(res){
        //res.setEncoding("utf8");
        res.on("data", function(chunk){
            //console.log("BODY:" + chunk);
        })
    })
 
    req.on('error', function(e){
        console.log('problem with request:' + e.message);
        console.log(e);
    })
    postfile.postFile(files, req, result);
 
}
var action = {
    /// 上传图片
    uploadimage: function(req, res) {
       curlPostAssign(res, req.files.upfile.path, req.files.upfile.originalFilename, req.files.upfile.size);
    },
    /// 获取配置文件
    config: function (req, res) {
        return res.redirect('/js/UEditor/config.js');
    },
    /// 在线管理
    listimage: function (req, res) {
        fs.readdir(uploadsPath, function (err, files) {
            var total = 0, list = [];
            files.sort().splice(req.query.start, req.query.size).forEach(function (a, b) {
                /^.+.\..+$/.test(a) &&
                list.push({
                    url: '/uploads/' + a,
                    mtime: new Date(fs.statSync(uploadsPath + a).mtime).getTime()
                });
            });
            total = list.length;
            res.json({state: total === 0 ? 'no match file' : 'SUCCESS', list: list, total: total, start: req.query.start});
        });
    },
    /// 抓取图片(粘贴时将图片保存到服务端)
    catchimage: function (req, res) {
        var list = [];
        req.body.source.forEach(function (src, index) {
            http.get(src, function (_res) {
                var imagedata = '';
                _res.setEncoding('binary');
                _res.on('data', function (chunk) {
                    imagedata += chunk
                });
                _res.on('end', function () {
                    var pathname = url.parse(src).pathname;
                    var original = pathname.match(/[^/]+\.\w+$/g)[0];
                    var suffix = original.match(/[^\.]+$/)[0];
                    var filename = Date.now() + '.' + suffix;
                    var filepath = uploadsPath + 'catchimages/' + filename;
                    fs.writeFile(filepath, imagedata, 'binary', function (err) {
                        list.push({
                            original: original,
                            source: src,
                            state: err ? "ERROR" : "SUCCESS",
                            title: filename,
                            url: '/uploads/catchimages/' + filename
                        });
                    })
                });
            })
        });
        var f = setInterval(function () {
            if (req.body.source.length === list.length) {
                clearInterval(f);
                res.json({state: "SUCCESS", list: list});
            }
        }, 50);
 
    }
};
app.get('/ue/uploads',multipartMiddleware, function (req, res) {
    action[req.query.action](req, res);
});
app.post('/ue/uploads',multipartMiddleware, function (req, res) {
    action[req.query.action](req, res);
});

运行效果:


image

参考链接:
http://blog.coinidea.com/web%E5%BC%80%E5%8F%91/nodejs-1161.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生x阅读 15,967评论 3 119
  • 说起北京,我更愿意称其为“北平”、“京城”,一直想亲眼看看白雪皑皑的皇城故宫,感受流传千年的京城风貌。 又一次冬季...
    筱熙Cynthia阅读 822评论 0 4
  • 事实证明,在人际交往和自己生活方面,我还是一如既往的内心羞涩和自卑缠绕。抑郁期的到来。
    飞鱼据说是我阅读 284评论 0 0
  • 作为一名刚刚刷完最新全国文综卷的准高三狗,此刻内心的委屈和崩溃此起彼伏。 这个软件是很久以前无意中在杂志推荐上看到...
    孤灯隔远汀阅读 190评论 0 0