用 node.js 來搞定 AutoPatch 軟體的自動更新
node.js 真的是方便好用的程式語言,效率也很不錯,可以輕易的與 Win32 Application 結合
Client 端我是使用 C++ Builder 來實作,主要用於更新客戶的執行檔,DLL 等等的函式庫
記得以前還沒有 node.js 的時候,Server 端也是用 C++ Builder 來開發,但是缺點是必須 Run 於 Windows 平台上
這對目前 VPS 盛行,大部分都是 Linux 主機無疑是一大阻礙,如今有了 node.js
很多 Socket 或是 HTTP 通訊的工作都轉而使用 node.js 來開發
var http = require("http"), url = require('url'), path = require('path'), fs = require('fs'), async = require('async'); var ReadFileObject = function (item, callback) { var ReadFileObject = this; this.resultFiles = []; this.readSizeRecursive = function (item, cb) { fs.lstat(item, function (err, stats) { if (!err && stats.isDirectory()) { fs.readdir(item, function (err, list) { if (err) return cb(err); async.each( list, function (diritem, callback) { ReadFileObject.readSizeRecursive(path.join(item, diritem), function (err, size) { if (typeof size !== 'undefined' && diritem[0] != '.') { ReadFileObject.resultFiles.push({ name : diritem, size : size.size, crc32 : 0 }); } callback(err); }); }, function (err) { cb(err); } ); }); } else { cb(err, stats); } }); }; this.readSizeRecursive(item, function () { callback(ReadFileObject.resultFiles); }); }; var server = http.createServer(function (req, res) { var url_parts = url.parse(decodeURI(req.url), true); switch (url_parts.pathname) { case '/' : if (typeof url_parts.query.package != 'undefined' && url_parts.query.package != '') { url_parts.query.package = path.normalize(url_parts.query.package.toLowerCase()); var _file = path.join(__dirname, url_parts.query.package); if (_file.match(__dirname + '/') && _file != __dirname + '/' && fs.existsSync(_file)) { var autoPatch = new ReadFileObject(_file, function (resultFiles) { console.log(url_parts.query.package, resultFiles, resultFiles.length); if (typeof url_parts.query.json === 'undefined') { res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'}); async.each( resultFiles, function (currentValue, callback) { res.write(currentValue.name.toString() + '\n'); res.write(currentValue.size.toString() + '\n'); res.write(currentValue.crc32.toString() + '\n'); callback(); }, function (err) { res.end(); } ); } else { res.writeHead(200, {'Content-Type': 'application/json; charset=utf-8'}); res.end(JSON.stringify(resultFiles)); } autoPatch = null; }); } else { if (typeof url_parts.query.json === 'undefined') { res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'}); res.end(); } else { res.writeHead(200, {'Content-Type': 'application/json; charset=utf-8'}); res.end(JSON.stringify([])); } } } else { res.writeHead(500); res.end('HTTP 500'); } break; default : var _file = path.join(__dirname, url_parts.pathname); if (_file.match(__dirname + '/') && fs.existsSync(_file)) { fs.lstat(_file, function (err, stats) { if (!err && stats.isFile()) { console.log(_file, 'Starting patch.'); res.writeHead(200, { "Content-Type" : 'application/x-msdownload', "Content-Disposition" : "attachment", "Content-Transfer-Encoding" : "binary", "Content-Length" : stats.size }); fs.createReadStream(_file).pipe(res); } else { res.writeHead(500); res.end('HTTP 500'); } }); } else { res.writeHead(404); res.end('HTTP 404'); } break; } }); server.listen(3002, function () { console.log('server is listening'); });