2013年10月24日 星期四

Read a BIG file in Node.js

寫程式的人常有需要處理到大型的檔案讀取,在Node.js中如果直接使用原生的fs.readFile()來讀檔,則會發生"RangeError: length > kMaxLength"之類的Exception,範例程式如下:

//test-fs.js
var fs = require('fs');
fs.readFile('../1G-File.txt', function(e,d){
  if(e) console.log(e);
  console.log(d);
});

執行的結果如下:

$ node test-fs.js
buffer.js:194
      this.parent = new SlowBuffer(this.length);
                    ^
RangeError: length > kMaxLength
    at new Buffer (buffer.js:194:21)
    at fs.js:220:16
    at Object.oncomplete (fs.js:107:15)


這時候借用第三方套件"lazy"+"fs.createReadStream()"的話,將可以很順利的解決讀取大檔案的問題喔∼

var lazy = require("lazy")
  , fs  = require("fs");
new lazy(fs.createReadStream('../1G-File.txt'))
   .lines
   .forEach(function(line){
       console.log(line.toString());
   }
);

執行上將會很順利...這邊不秀了∼給大家參考






2013年10月22日 星期二

Node.js銜接Google的第一步....

gapitoken

gapitoken是使用Node.js實作Oauth2認證的一個模組,透過API console中申請一個service account,並使用api console產生的p12 key所轉換的pem key,就可以快速的與API進行認證。此種(service account)與API認證的方式適用於提供查詢服務的應用程式,不需要與user的Auth做互動,與API的互動完全是透過應用程式owner所申請的service account之全縣。

套件資訊

Installation

npm install gapitoken

使用說明

準備連線需要的資訊,其中iss為當初申請service account時候的所給的Email address,如下圖:
而Scope是Google控制存取API的呼叫權限,不同的API或不同的request有不同的scope要給予,才可以做呼叫動作,最後,key.pem是創立service account時候所給予的p12檔案,透過ssl轉換後的檔案...
var opts = {
      iss: '860835...6s1@developer.gserviceaccount.com',
      scope: 'https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/cloud-platform',
      keyFile: __dirname + '/key.pem'  
};
準備API pem key
從API console下載的key為副檔名p12的key,需要透過openssl轉換為pem key
$ openssl pkcs12 -in privatekey.p12 -out privatekey.pem -nocerts  
$ openssl rsa -in privatekey.pem -out key.pem

透過GAPI物件,進行Auth連線動作,實際token可以gapi.getToken()的callback中取得...
var gapi = new GAPI(opts, function(err) {
    if (err) return console.log(err);
    gapi.getToken(function(err, token) {
      if (err)  return console.log(err);
      //這邊已經取到token了
      console.log(token);
    });
  });
呼叫API,這邊透過request模組做API的呼叫,其中auth token需要放在header的Authorization中...
var bqurl = 'https://www.googleapis.com/bigquery/v2/projects/%s/queries';  
request({
    url: util.format(bqurl,'your-project-name'),
    method: 'POST',
    headers: {
      "Authorization": "Bearer " + token
    }, 
   json: {
      query: 'select * from cp300.GetAnimals'
    }
  }, function(e,r,d){
    if(e) console.log(e);    //Print the query result
    console.log(JSON.stringify(d));
  });

使用範例 - 連線BigQuery

See:/examples/gapitoken/bq-sample.js

var GAPI = require('gapitoken')
    , request = require('request')
    , fs = require('fs')
    , util = require('util')

    //From admin console, create a service account, save the client_secrets.json and it's key
  var client_secrets = JSON.parse(fs.readFileSync(__dirname + '/client_secrets.json','utf8'))

    //Project setting
  var iss = client_secrets.web.client_email;
  var bq_scope = 'https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/cloud-platform';
  var project = 'your-project-name';
  var opts = {
      iss: iss,
      scope: bq_scope,
       keyFile: __dirname + '/key.pem'  };    
  var _token = '';
  var gapi = new GAPI(opts, function(err) {
    if (err) return console.log(err);
     gapi.getToken(function(err, token) {
      if (err)  return console.log(err);
      _token = token;
      var bqurl = 'https://www.googleapis.com/bigquery/v2/projects/%s/queries';      request({
        url: util.format(bqurl,project),
        method: 'POST',
        headers: {
          "Authorization": "Bearer " + _token
        },
        json: {
          query: 'select * from cp300.GetAnimals'
        }
      }, function(e,r,d){
        if(e) console.log(e);        //Print the query result
        console.log(JSON.stringify(d));
      });
    });
  });  

2013年8月18日 星期日

Npm Repository Info of you package

透過下面網址可以從npm取回某個到見的相關資訊
並且render成一張圖片喔∼

https://nodei.co/npm/[套件名稱].png

範例如下:



看起來這張圖片是排成去產生的
如果可以做成自動產生,那就更炫了 :D

2013年8月4日 星期日

GitLab one click installer

GitLab是一套提供類似Private Github服務的軟體,開放且因為使用上親近Github,所以是套非常建議每個開發Team都安裝的服務... 實際上看過GitLab的手動安裝方式,其實相當複雜,但是因為有Bitnami這個熱心的服務者打包GitLab的服務,讓Linux上安裝非常的方便...

目前我常試過CentOS的安裝方式,原則上是下載下來後,直接執行:
執行安裝:
# chmod u+x bitnami-gitlab-5.4.0-0-linux-x64-installer.run
# ./bitnami-gitlab-5.4.0-0-linux-x64-installer.run


----------------------------------------------------------------------------
Welcome to the BitNami GitLab Stack Setup Wizard.

----------------------------------------------------------------------------
Select the components you want to install; clear the components you do not want
to install. Click Next when you are ready to continue.

GitLab : Y (Cannot be edited)

GitLab CI [Y/n] :
Is the selection above correct? [Y/n]:
----------------------------------------------------------------------------
Installation folder

Please, choose a folder to install BitNami GitLab Stack

Select a folder [/opt/gitlab-5.4.0-0]:
----------------------------------------------------------------------------
Create Admin account

BitNami GitLab Stack admin user creation

Login [user]: root

Password :
Please confirm your password :

----------------------------------------------------------------------------
Hostname that will be used to create internal URLs. If this value is incorrect,
you may be unable to access your Gitlab installation from other computers. It is
advisable to use a Domain instead of an IP address for compatibility with
different browsers.

Domain [127.0.0.1]: xxx.xxx.xxx.xxx

Do you want to configure mail support? [y/N]: y

----------------------------------------------------------------------------
Configure SMTP Settings

This is required so your application can send notifications via email.

Default email provider:

[1] GMail
[2] Custom
Please choose an option [1] : 1

----------------------------------------------------------------------------
Configure SMTP Settings

Default mail server configuration.

GMail address []: simonsu@mitac.com.tw

GMail password :
Re-enter :
----------------------------------------------------------------------------
Setup is now ready to begin installing BitNami GitLab Stack on your computer.

Do you want to continue? [Y/n]: Y

----------------------------------------------------------------------------
Please wait while Setup installs BitNami GitLab Stack on your computer.

 Installing
 0% ______________ 50% ______________ 100%
 #########################################

----------------------------------------------------------------------------
Setup has finished installing BitNami GitLab Stack on your computer.

Info: To access the BitNami GitLab Stack, go to
http://xxx.xxx.xxx.xxx:80 from your browser.
Press [Enter] to continue :

完成安裝之後,就可以透過80 port連線進入該主機,畫面與操作上與Github真的頗為相像,後續有趣的地方就留給有興趣的網友去發現了 :D



2013年7月30日 星期二

Ejs page scope直接使用session物件的方式

在ExpressJS中,使用Ejs view engine時,發現session不能再ejs page中直接取用,所有參數都必須靠render時候傳遞過去...跟asp, jsp等scriptlet language操作上有明顯的不同...@@

為了達到可以在ejs中直接取用,這邊可以使用ejs page的locals變數,locals變數在route設定時候可以使用res.locals來取出,這時候只要將它與req.session做串連(res.locals.session = req.session)即可讓前端的ejs page直接透過<%=locals.session%>或是<%=session%>的方式來操作session...

而如果要在每一個route中設定res.locals.session,這樣也太麻煩... 我們可以透過app.use的方式來設定:

app.use(function(req, res, next){
  res.locals.session = req.session;
  next();
});

這樣所有的route在執行的時候都會先跑過一次這個動作,所以在使用時候可以這樣:

[app.js]
app.get('/', function(req, res) {
  req.session.user = {name: 'simon'}
  ...(skip)
});

[view/index.ejs]
...(skip)
<%=session.user? 'got user: '+session.user.name : 'no user...'%>
...(skip)

這時候從ejs取用session的物件就單純多了 :D

在ExpressJS中進階使用Ejs view engine

在操作ExpressJS的Ejs View Engine時候
發現針對頁面上的操作並不是那麼的順心
舉個例子:
app.js中做一個route希望能夠將值往前端(.ejs)帶,但是前端implement了express-partials的模組,希望把頁面用template的方式組織起來,並且在template page中會用到一部分的參數... 假設有時個route用到這個template,則每個route都必須把參數設定進去,否則後端會接收到"not defined"的錯誤訊息...

上面例子的片段程式碼如下:

[app.js]
app.get('/', function(req, res){
  res.render('index', { title: 'my express page' });
});

[index.ejs]
<% if(user) { %>
<%= user %>
<% } %>

執行時候會有Exception:
...(skip)
user is not defined
...(skip)

這部份的錯誤應該是Ejs在render page時候造成的,它直接throw Exception而會造成page終止render,在遍尋不著比較好的方法時,從某篇文章找到一些蛛絲馬跡...

原來Ejs使用"locals"這個物件來包裝頁面上會用到的所有參數,而經過ejs的scriptlet tag包裝起來的部分,可以直接使用locals裡面的參數,也就是說上面的app.js做page render時候:

res.render('index', { title: 'my express page' })

相當於把title這個參數與ejs頁面的locals變數做整合
亦即ejs頁面操作:

<%=title%>

與

<%=locals.title%>

是相等的,但是直接操作title屬性時候,會被ejs compile成runtime exception,這導致如果在ejs中執行下面判斷會出錯;

<% if(user) { .... %>

因為實際上locals.user不存在,且ejs compiler會將不存在的狀況throw exception...

解決方式,直接使用locals來判斷裡面是否有user這個變數...,因此改寫上面的判斷後:

<% if(locales['user']) { .... %>

應該就可以正常運作...