嘛… 最近真心补番补多了智商降得有点厉害.. 直到我发现自己已经看不懂别人的 js 代码,却还会手贱点开 bilibili 之后才立下军令状 ,再不开新坑再不敲代码就敲不出来啦!
一直很纠结再写点啥。之前财务管理系统的坑估计要弃,因为实在一个人填不完…然后 NAE 一直没计划好,估计这个坑会更巨。之前 luke 酱说发现了一个不错的东方音乐站,想要个桌面端。咱之前有试着学Qt但是一直没有方便的文档而且..终归是咱能力不行吧,Qt估计是一时半会搞不定了。写桌面端的话,似乎只有 node-webkit 了.. 然后看了一下午的文档只有一个感觉——我要死了。
Intel开发的东西和红帽一样蛋疼
桌面端什么的…. 所以就放着吧… 随便搞个东西练练手算了。想想看还没有练习过文件上传 真丢人啊都不会写文件上传 于是来写一个上传文件并分享的东西好了w
于是老样子.. express -e
新建一个项目模板,初始化git仓库… 诶似乎有什么东西不对?
express.bodyParser()
没了…
先不管这些,基本的路由和功能堆起来,打印出结果来看看能不能正确获取文件信息。然后… 当然失败了。
没有 bodyParser
之后,带有文件上传的表单无法被处理了,req.files
是 undefined
,req.body
里也是空的..
一阵 Google 之后表示,泥煤的 express 3.4.x 又改了… bodyParser
已经被弃用 ,原因是存在可能占满磁盘的漏洞 。目前推荐的方式是直接使用 multiparty
。于是又去翻这货的文档,表示要蛋疼许多… 纠结了一会觉得还是先用回 3.3.4 之后慢慢折腾下 multiparty。
Directshare 使用 HTTP 直链文件,看起来似乎有点危险,不过毕竟可以拿来做图床、临时的文件共享或者在没有FTP/SSH的情况下把文件上传到服务器什么的.. 最重要的是,我只是想练练手而已搞那么复杂干嘛..
基本思路:整个程序分为负责接收文件的master和负责分发文件的cluster。客户端使用浏览器POST方法上传文件 => master得到文件后计算SHA256并把文件从临时目录移动到保存文件的目录,文件名是SHA256值,不带有后缀。接下来通知所有cluster来拖。这里要有一个验证,暂且用 POST accesskey 之类的东西来做好了。因为似乎没找到啥能通过 http 同步的 node 包,所以简单的双向 POST 数据基本上也没问题。本来想在cluster接收到文件后再计算一次hash值.. 不过这个后面再说。通知所有 cluster 用了一个 forEach,虽然是阻塞的方法不过发几个post请求应该是相当快的。接下来把从cluster下载文件的地址返回给客户端浏览器。这个时候cluster应该已经收到post请求,过来拖文件了。
文件 master.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 var express = require ('express' );var http = require ('http' );var path = require ('path' );var config = require ('./config.js' );var crypto = require ('crypto' );var fs = require ('fs' );var querystring = require ('querystring' );var app = express();app.set('port' , config.port || 3000 ); app.set('views' , path.join(__dirname, 'views' )); app.set('view engine' , 'ejs' ); app.use(express.favicon()); app.use(express.logger('dev' )); app.use(express.json()); app.use(express.bodyParser({ keepExtensions: false , uploadDir: "tmpdata" })); app.use(express.methodOverride()); app.use(express.static(path.join(__dirname, 'public' ))); if ('development' == app.get('env' )) { app.use(express.errorHandler()); } app.get('/' , function (req, res ) { res.render('index' , { siteName: config.siteName }); }); app.post('/' , function (req, res ) { var sha256sum = crypto.createHash('sha256' ); var filehash = fs.ReadStream(req.files.uploadfile.path); filehash.on('data' , function (d ) { sha256sum.update(d); }); filehash.on('end' , function ( ) { var d = sha256sum.digest('hex' ); console .log(d + ' ' + req.files.uploadfile.name); fs.rename(req.files.uploadfile.path, 'files/' + d, function ( ) { config.clusters.forEach(function (cluster ) { var post_data = querystring.stringify({ clusterkey: config.clusterkey, filename: req.files.uploadfile.name, filehash: d }); var post_options = { hostname: cluster, port: config.clustersport, path: '/syncfile' , method: 'POST' , headers: { 'Content-Type' : 'application/x-www-form-urlencoded' , 'Content-Length' : post_data.length } } console .log('syncing with ' + cluster); var post_req = http.request(post_options); post_req.write(post_data); post_req.end(); }); res.send(200 , config.clusterurl + d); }); }); }); app.post('/syncfile' , function (req, res ) { if (req.body.clusterkey != config.clusterkey) { return res.send(401 ); } else { res.sendfile('files/' + req.body.filehash); } }); app.use(function (req, res ) { res.send(404 , 'My files gone?! :-O' ); }); http.createServer(app).listen(app.get('port' ), function ( ) { console .log('Express server listening on port ' + app.get('port' )); });
然后是 cluster.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 var express = require ('express' );var http = require ('http' );var path = require ('path' );var config = require ('./config.js' );var fs = require ('fs' );var querystring = require ('querystring' );var app = express();app.set('port' , config.port || 3000 ); app.use(express.favicon());app.use(express.logger('dev' )); app.use(express.json()); app.use(express.methodOverride()); app.use(express.bodyParser()); app.use(app.router); app.use(express.static(path.join(__dirname, 'public' ))); if ('development' == app.get('env' )) { app.use(express.errorHandler()); } app.get('/' , function (req, res ) { res.send(400 , 'Bad request' ); }); app.get('/:hash' , function (req, res ) { fs.readFile('files/' + req.params.hash + '.json' , 'utf8' , function (err, filedata ) { if (err) { if (err.code === 'ENOENT' ) { res.send(404 , 'File not found. :-(' ); } else { console .log(err); } } else { var filedata = JSON .parse(filedata); res.download('files/' + req.params.hash, filedata.filename); } }) }); app.post('/syncfile' , function (req, res ) { if (req.body.clusterkey != config.clusterkey) { return res.send(401 ); } else { res.send(200 ); var post_data = querystring.stringify({ clusterkey: config.clusterkey, filehash: req.body.filehash }); var post_options = { hostname: config.master, port: config.masterport, path: '/syncfile' , method: 'POST' , headers: { 'Content-Type' : 'application/x-www-form-urlencoded' , 'Content-Length' : post_data.length } } console .log('Getting file with SHA256 ' + req.body.filehash); var post_req = http.request(post_options, function (res ) { var file = fs.createWriteStream('files/' + req.body.filehash); res.pipe(file); }); post_req.write(post_data); post_req.end(); fs.appendFile('files/' + req.body.filehash + '.json' , JSON .stringify({ "filename" : req.body.filename }), function ( ) { console .log('Saved file ' + req.body.filename + ' with hash ' + req.body.filehash); }); } }); http.createServer(app).listen(app.get('port' ), function ( ) { console .log('Express server listening on port ' + app.get('port' )); });
这样程序应该就相当清晰了。在 cluster 端使用了SHA256值作为文件名的 json 作为文件信息存储,当前只保存了对应的文件名。至于重复文件啊什么的.. 暂时没想好怎么做,不过拿sha256做文件名本来就有这个功能的想法。本地测试完成,commit #f6365cfcbd
当我发军令状的时候是昨晚9点,第一个可用版本提交是今天2点左右,5个小时开新坑再填上… 不过这坑有点小所以好填… 所以5个小时不算什么了吧就… 嘛至少不用改名BAKA了~