在 Node.js 中使用 PhoneGap Build API 包

openkk 12年前

昨天我在Node中使用了PhoneGap Build API包。我将它上传到了: https://github.com/germallon/phonegapbuildapi,我认为这是一个很有趣的试验。 Build API是真的简单,所以我认为我的代码也将是非常简单的。

首先,我开始阅读它的API部分。它可以获取到应用程序,图标,下载的东西,等等。PhoneGap允许每次请求一个令牌或通过身份验证信息。于是,我通过简单地方式验证每个请求,并且让大部分的API逻辑都调用两个通用函数:

function getConfig(path) {      return {          auth: username + ":" + password,          host:"build.phonegap.com",          port:"443",          path:"/api/v1/"+path      }  }    //I handle doing the config get, http, string contact, etc  function doCall(path, success, fail) {      var options = getConfig(path);      var req = http.get(options, function(res) {          var resultString = "";          res.on("data", function(c) {              resultString+=c;          });                res.on("end",function() {              var result = JSON.parse(resultString);              success(result);          });      }).on("error", function(e) {          if(fail) fail(e);      });  }

这里doCall是核心函数,它解析API的路径。所有的API都调用相同的基URL,所以我让它更加简单,只需要加上path。Node中调用HTTP比CF要复杂一些,因为它们是异步的,但也并不困难。你可能会猜想这段代码里发生了什么。我打开一个请求,它会获得到一个结果对象。结果对象有一个数据事件。我将数据追加到一个结果变量。这里还有一个结束事件,在结束事件中我们可以用json解析结果变量,并发送成功的处理。

以下是一个获得所有应用程序的例子:

exports.getAllApps = function(success,fail) {      doCall("apps", function(res) {          if(res.error && res.error.length && fail) fail(res.error);          else success(res.apps);      },function(e) {          if(fail) fail(e);      });

最后,让Node应用程序使用它:

pgbuild = require("./pgbuild");    pgbuild.setUsername("ray@camdenfamily.com");  pgbuild.setPassword("isitmillertimeyet?");    //Test getting all the apps  pgbuild.getAllApps(function(apps) {      console.log("I got lots of apps! How many? "+apps.length);      //console.dir(apps);  }, function(e) {      console.log("Oh snap, an error");      console.dir(e);  });

我写的大部分代码都遵循API的这种格式 - 通过一个成功/失败的来处理程序。

所以我说这些都是非常简单的。我想我大概用了30分钟左右阅读API。然后我开始编写API时碰壁了。为什么?我想创建一个应用程序,允许你上传的文件并定义新应用程序。你也可以将新应用程序指定在一个存储库,但我想先做好文件版本。(打电话给我一个贪吃的朋友)证明上传文件在后面更加痛苦。没有真正内置的支持核心的Node.js库。谷歌搜索真的很困难,因为几乎所有的结果是关于如何处理文件上传,而不是文件上传请求。

经过我一番疯狂搜索,我发现这篇 文章。我在想这人的真正名字,但他(她)的关于页面实际上并没有说他(她)是谁。因此我决定这个人是……

在 Node.js 中使用 PhoneGap Build API 包

……根据他(她)说的。我将他的一些逻辑写进了我最终的代码,虽然我对这种混合不是很满意,但是它确实很管用。下面是一个调用示例:

pgbuild.createApp({      title:"New App for Testing",      create_method:"file",      file:"./forupload/index.html"      }, function(res) {          console.log("Ok in making an app");          console.dir(res);      }, function(e) {          console.log("I got an error: ");          console.dir(e);      }  );

这是一个用PhoneGap新构建的网站。

在 Node.js 中使用 PhoneGap Build API 包

有趣的是,浏览器使JavaScript上传文件比XHR2更加容易。如果你还没有看到这一动作,请查看优秀的 HTML5 Rocks这篇文章。

下面我将整个pgbuild.js代码贴出来:

 

var http = require("https");  var fs = require("fs");  var path = require("path");    var username = "";  var password = "";    exports.setUsername = function(u) { username = u; }  exports.setPassword = function(p) { password = p; }    exports.createApp = function(options, success, fail) {      var httpOptions = getConfig("apps");      httpOptions.method = "POST";            //Detect if options.create_method is file, and if so, suck in the bits      //Fails if no .file      //Also note it doesn't support .zip yet.      if(options.create_method === "file") {          if(!options.file) throw new Error("Must supply file value.");          console.log("Need to read in a file:"+options.file);          //Shell out for file uploads          PreparePost(httpOptions,JSON.stringify(options), options.file, success);      } else {          //TODO      }        }    exports.getAllApps = function(success,fail) {      doCall("apps", function(res) {          if(res.error && res.error.length && fail) fail(res.error);          else success(res.apps);      },function(e) {          if(fail) fail(e);      });  }    exports.getApp = function(id, success, fail) {      doCall("apps/"+id, function(res) {          if(res.error && res.error.length && fail) fail(res.error);          else success(res);      },function(e) {          if(fail) fail(e);      });  }    exports.getAppIcon = function(id, success, fail) {      doCall("apps/"+id +"/icon", function(res) {          if(res.error && res.error.length && fail) fail(res.error);          else success(res.location);      },function(e) {          if(fail) fail(e);      });  }    //todo: Possibly validate platform? Should be: android,blackberry,ios,symbian,webos,winphone  exports.getAppDownload = function(id, platform, success, fail) {      doCall("apps/"+id +"/"+platform, function(res) {          if(res.error && res.error.length && fail) fail(res.error);          else success(res.location);      },function(e) {          if(fail) fail(e);      });  }    exports.getKeys = function() {      var platform = "";      if(arguments.length == 1) {          success = arguments[0];      } else if(arguments.length === 2) {          success = arguments[0];          fail = arguments[1];      } else if(arguments.length == 3) {          platform = arguments[0];          success = arguments[1];          fail = arguments[2];      }             var path = "keys";      if(platform != "") path+="/"+platform;            doCall(path, function(res) {          if(res.error && res.error.length && fail) fail(res.error);          else success(res.keys);      },function(e) {          if(fail) fail(e);      });  }    exports.getKey = function(platform, id, success, fail) {      doCall("keys/"+platform +"/"+id, function(res) {          if(res.error && res.error.length && fail) fail(res.error);          else success(res);      },function(e) {          if(fail) fail(e);      });  }    function getConfig(path) {      return {          auth: username + ":" + password,          host:"build.phonegap.com",          port:"443",          path:"/api/v1/"+path      }  }    //I handle doing the config get, http, string contact, etc  function doCall(path, success, fail) {      var options = getConfig(path);      var req = http.get(options, function(res) {          var resultString = "";          res.on("data", function(c) {              resultString+=c;          });                res.on("end",function() {              var result = JSON.parse(resultString);              success(result);          });      }).on("error", function(e) {          if(fail) fail(e);      });  }        //CREDIT: http://onteria.wordpress.com/2011/05/30/multipartform-data-uploads-using-node-js-and-http-request/  //Note that I modified his code quite a bit    //For file uploads  function EncodeFieldPart(boundary,name,value) {      var return_part = "--" + boundary + "\r\n";      return_part += "Content-Disposition: form-data; name=\"" + name + "\"\r\n\r\n";      return_part += value + "\r\n";      return return_part;  }    function EncodeFilePart(boundary,type,name,filename) {      var return_part = "--" + boundary + "\r\n";      return_part += "Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + filename + "\"\r\n";      return_part += "Content-Type: " + type + "\r\n\r\n";      return return_part;  }    //I expect the config options, the JSON data string, and file path  function PreparePost(httpOptions,data,file,success) {      var boundary = Math.random();      var post_data = [];        post_data.push(new Buffer(EncodeFieldPart(boundary, 'data', data), 'ascii'));      post_data.push(new Buffer(EncodeFilePart(boundary, 'text/plain', 'file', path.basename(file)), 'ascii'));        var contents = fs.readFileSync(file, "ascii");      post_data.push(new Buffer(contents, "utf8"));      post_data.push(new Buffer("\r\n--" + boundary + "--"), 'ascii');            MakePost(httpOptions,post_data, boundary,success);  }    function MakePost(httpOptions,post_data, boundary,success) {        var length = 0;            for(var i = 0; i < post_data.length; i++) {          length += post_data[i].length;      }        httpOptions.headers = {          'Content-Type' : 'multipart/form-data; boundary=' + boundary,          'Content-Length' : length      };        var post_request = http.request(httpOptions, function(response){          response.setEncoding('utf8');          var res="";          response.on('data', function(chunk){              res+=chunk;          });          response.on('end',function() {              success(JSON.parse(res));          });      });        for (var i = 0; i < post_data.length; i++) {          post_request.write(post_data[i]);      }      post_request.end();  }

 

原文地址