2012年11月13日 星期二

JavaScript - 事件指定function與圓括號(parentheses)的關係

標題其實是想說 elementA.onclick = func; 這樣一回事
之前都已經看慣&習慣這個寫法倒也沒有想太多

直到今天寫了一個要輸入參數的function
function specialSetting(obj){
....   //original code 
}

然後就直覺寫上了 elementA.onclick = specialSetting(elementA);
畢竟一開始學寫JavaScript都是 <div ... onClick="specialSetting(this);" > 這樣開始

然後執行時就發現函式一開始就馬上被呼叫一次...
仔細想想才覺得應該是圓括號的關係
猜測 JavaScript 的執行方式是 elementA.onclick = specialSetting(elementA);
先執行一次呼叫 specialSetting(obj); 之後才綁定事件
function在JavaScript中是物件
elementA.onclick = specialSetting;  表明事件綁了個函式物件,而不是要立刻執行函式

那如果要傳入參數呢? 改用匿名函式就好了
elementA.onclick = functionn(){ specialSetting(elementA); };

回想要傳入事件作參數給函式的作法是
elementA.onclick = functionn(evt){ func(); };
從這裡其實就看得出端倪了

JavaScript 常用事件整理

常見事件整理:

事件                       觸發時機

onabort                 圖片下載被中斷時

onblur                    當游標離開元件時

onchange             當使用者更改欄位內容時

onclick                 滑鼠單擊物件時

ondblclick            滑鼠雙擊物件時

onDragDrop       物件被拉入瀏灠器視窗時

onerror                 載入文件或圖片發生錯誤時

onfocus                當游標定位在元件上時

onkeydown          當鍵盤的按鍵被壓下時

onkeypress         當鍵盤的按鍵被壓下再放開時

onkeyup              當鍵盤的按鍵被放開時

onload                 當網頁或圖片載入完成時

onmousedown     滑鼠按鍵被壓下時

onmousemove     滑鼠指標移動時

onmouseout        滑鼠指標離開物件時

onmouseover      滑鼠指標移到物件上時

onmouseup         滑鼠按鍵被放開時

onreset                當reset按鍵被點擊時

onresize               當視窗或框架被改變大小時

onselect               當文字被選取時

onsubmit              當sumbit按鍵被點擊時

onunload              當使用者離開畫面時

2012年11月12日 星期一

Tomcat,Apache配置GZip壓縮(HTTP壓縮)功能

*修改自網路文章,文章源頭不確定

HTTP 壓縮可以大大提高瀏覽網站的速度
它的原理是,在客戶端請求網頁後,從Server將網頁文件壓縮
再下載到Client,由Client的瀏覽器負責解壓縮並瀏覽
相對於普通的瀏覽過程HTML ,CSS,JavaScript , Text ,它可以節省40%左右的流量
更為重要的是,它可以對動態生成的
包括CGI、PHP、JSP、ASP、Servlet、SHTML等輸出的網頁也能進行壓縮

一,對於Tomcat5.0以後的版本支持對輸出內容進行壓縮,使用的是gzip壓縮格式.
要使用gzip壓縮功能,你可以在Connector實例中加上如下屬性即可

1. compression="on"                                      打開壓縮功能
2. compressionMinSize="2048"                      啟用壓縮的輸出內容大小,這裡預設2KB
3. noCompressionUserAgents="gozilla, traviata"  對於以下的瀏覽器,不啟用壓縮
4. compressableMimeType="text/html,text/xml"   壓縮類型

下面是$tomcat_home$/conf/server.xml的配置內容

<Connector port="8080" protocol="HTTP/1.1"
        compression="on"
        compressionMinSize="2048"
        connectionTimeout="20000"
compressableMimeType="text/html,text/xml,text/plain,
                  text/css,text/json,text/javascript,
                application/x-javascript,application/javascript,
                application/jsp,application/json,image/jpeg,image/png"
         redirectPort="8443" />

對於某些text文件比如:log、txt等,我們也可以讓Server採用gzip壓縮傳輸
修改conf目錄下web.xml,添加

    <mime-mapping>
     <extension>log</extension>
     <mime-type>text/plain</mime-type>
    </mime-mapping>

就可以指定壓縮傳輸了
通常情況下,壓縮傳輸能大幅度提高展示速度

二,對於Apache而言,有兩種情況
1.針對Apache2.0之前的版本,它原本是不支持的
   不過可以通過添加第三方的module_gzip模組來啟用
2.針對Apache2.0及之後的版本,Apache提供支持;不過不叫gzip,而叫mod_deflate

下面就對Apache2.0及之後的版本作一個說明
1. 去掉#LoadModule headers_module modules/mod_headers.so前面的註釋#,
2. 添加LoadModule deflate_module modules/mod_deflate.so
3. 在VirtualHost中添加

    <Location "/">
         SetOutputFilter DEFLATE
         BrowserMatch ^Mozilla/4 gzip-only-text/html
         BrowserMatch ^Mozilla/4\.0[678] no-gzip
         BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
         SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
         Header append Vary User-Agent env=!dont-vary
    </Location>

以下為完整範例

# 加載deflate模組
LoadModule headers_module modules/mod_headers.so
LoadModule deflate_module modules/mod_deflate.so
<VirtualHost *:80>
     DocumentRoot f:/apacheTest
    <Location "/">
         SetOutputFilter DEFLATE
         BrowserMatch ^Mozilla/4 gzip-only-text/html
         BrowserMatch ^Mozilla/4\.0[678] no-gzip
         BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
         SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
         Header append Vary User-Agent env=!dont-vary
    </Location>
</VirtualHost>

資料結構容量大小的調整

StringBuilder 和 StringBuffer 底層都使用char[]來做為資料儲存庫
而且常常需要調整容量大小
調整容量大小的方法就是配置一個容量更大的新的char[]
接著將舊資料複製過來後再拋棄舊的char[]
類似的調整過程也可能發生在使用陣列當作底層資料儲存庫的Java SE Collection

依OpenJDK的實作方式
當儲存文字超過底層資料儲存庫的容量時
會配置一個原本容量兩倍大的新char陣列,而底層的char陣列大小預設為16
實務上很少StringBuffer 或StringBuilder的物件最後只使用了16以內的char元素
要避免StringBuffer 或StringBuilder調整大小的動作,我們可以指定大小給其建構子

如ArrayList、Vector、HashMap和ConcurrenthashMap等Collections因使用陣列儲存資料
同StringBuffer 或StringBuilder,很容易在增加元素時引發調整大小的動作
需要額外的CPU週期來配置新陣列、複製舊陣列的資料,將來也需要回收舊陣列
以HashMap為例,預設建構子的容量是16筆資料,也是經常不夠用
其資料擴張時也是使用原本陣列2倍大的新陣列
又因每次調整時都要計算雜湊(hash)值,當資料量大時耗費的成本也更驚人
遇到這種情形時,比較好的處理方式就是直接將計算容量的算式傳給HashMap的建構子

2012年11月10日 星期六

[轉載] require.js的用法

原文


一、為什麼要用require.js?

最早的時候,所有Javascript代碼都寫在一個文件裡面,只要加載這一個文件就夠了
後來,代碼越來越多,一個文件不夠了,必須分成多個文件,依次加載
下面的網頁代碼,相信很多人都見過

      <script src="1.js"></script>
      <script src="2.js"></script>
      <script src="3.js"></script>
      <script src="4.js"></script>
      <script src="5.js"></script>
      <script src="6.js"></script>

這段代碼依次加載多個js文件

這樣的寫法有很大的缺點
首先,加載的時候,瀏覽器會停止網頁渲染
加載文件越多,網頁失去響應的時間就會越長
其次,由於js文件之間存在依賴關係,因此必須嚴格保證加載順序
比如上例的1.js要在2.js的前面
依賴性最大的模塊一定要放到最後加載
當依賴關係很複雜的時候,代碼的編寫和維護都會變得困難

require.js的誕生,就是為瞭解決這兩個問題:
(1)實現js文件的異步加載,避免網頁失去響應;
(2)管理模塊之間的依賴性,便於代碼的編寫和維護。


二、require.js的加載

使用require.js的第一步,是先去官方網站下載最新版本。

下載後,假定把它放在js子目錄下面,就可以加載了。

      <script src="js/require.js"></script>

有人可能會想到,加載這個文件,也可能造成網頁失去響應
解決辦法有兩個,一個是把它放在網頁底部加載,另一個是寫成下面這樣:

      <script src="js/require.js" defer async="true" ></script>

async屬性表明這個文件需要異步加載,避免網頁失去響應
IE不支持這個屬性,只支持defer,所以把defer也寫上

加載require.js以後,下一步就要加載我們自己的代碼了
假定我們自己的代碼文件是main.js,也放在js目錄下面
那麼,只需要寫成下面這樣就行了:

      <script src="js/require.js" data-main="js/main"></script>

data-main屬性的作用是,指定網頁程序的主模塊
在上例中,就是js目錄下面的main.js,這個文件會第一個被require.js加載
由於require.js默認的文件後綴名是js,所以可以把main.js簡寫成main


三、主模塊的寫法

上一節的main.js,我把它稱為"主模塊",意思是整個網頁的入口代碼
它有點像C語言的main()函數,所有代碼都從這兒開始運行

下面就來看,怎麼寫main.js。

如果我們的代碼不依賴任何其他模塊,那麼可以直接寫入javascript代碼。

      // main.js
      alert("加載成功!");

但這樣的話,就沒必要使用require.js了
真正常見的情況是,主模塊依賴於其他模塊
這時就要使用AMD規範定義的的require()函數

      // main.js
      require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
        // some code here
      });

require()函數接受兩個參數
第一個參數是一個數組,表示所依賴的模塊,上例就是['moduleA', 'moduleB', 'moduleC']
即主模塊依賴這三個模塊
第二個參數是一個回調函數,當前面指定的模塊都加載成功後,它將被調用
加載的模塊會以參數形式傳入該函數,從而在回調函數內部就可以使用這些模塊

require()異步加載moduleA,moduleB和moduleC,瀏覽器不會失去響應
它指定的回調函數,只有前面的模塊都加載成功後才會運行,解決了依賴性的問題

下面,我們看一個實際的例子

假定主模塊依賴jquery、underscore和backbone這三個模塊,main.js就可以這樣寫:

      require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){
        // some code here
      });

require.js會先加載jQuery、underscore和backbone,然後再運行回調函數
主模塊的代碼就寫在回調函數中


四、模塊的加載

上一節最後的示例中,主模塊的依賴模塊是['jquery', 'underscore', 'backbone']
默認情況下,require.js假定這三個模塊與main.js在同一個目錄
文件名分別為jquery.js,underscore.js和backbone.js,然後自動加載

使用require.config()方法,我們可以對模塊的加載行為進行自定義
require.config()就寫在主模塊(main.js)的頭部
參數就是一個對象,這個對象的paths屬性指定各個模塊的加載路徑

      require.config({
        paths: {
          "jquery": "jquery.min.js",
          "underscore": "underscore.min.js",
          "backbone": "backbone.min.js"
        }
      });

上面的代碼給出了三個模塊的文件名,路徑默認與main.js在同一個目錄(js子目錄)
如果這些模塊在其他目錄,比如js/lib目錄,則有兩種寫法。一種是逐一指定路徑

      require.config({
        paths: {
          "jquery": "lib/jquery.min.js",
          "underscore": "lib/underscore.min.js",
          "backbone": "lib/backbone.min.js"
        }
      });

另一種則是直接改變基目錄(baseUrl)。

      require.config({
        baseUrl: "js/lib",
        paths: {
          "jquery": "jquery.min.js",
          "underscore": "underscore.min.js",
          "backbone": "backbone.min.js"
        }
      });

如果某個模塊在另一台主機上,也可以直接指定它的網址,比如:

      require.config({
        paths: {
          "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
        }
      });

require.js要求,每個模塊是一個單獨的js文件
這樣的話,如果加載多個模塊,就會發出多次HTTP請求,會影響網頁的加載速度
因此require.js提供了一個優化工具
當模塊部署完畢以後,可以用這個工具將多個模塊合併在一個文件中,減少HTTP請求數


五、AMD模塊的寫法

require.js加載的模塊,採用AMD規範
也就是說,模塊必須按照AMD的規定來寫

具體來說,就是模塊必須採用特定的define()函數來定義
如果一個模塊不依賴其他模塊,那麼可以直接定義在define()函數之中

假定現在有一個math.js文件,它定義了一個math模塊。那麼,math.js就要這樣寫:

      // math.js
      define(function (){
        var add = function (x,y){
          return x+y;
        };
        return {
          add: add
        };
      });

加載方法如下:

      // main.js
      require(['math'], function (math){
        alert(math.add(1,1));
      });

如果這個模塊還依賴其他模塊,那麼define()函數的第一個參數,必須是一個數組
指明該模塊的依賴性

      define(['myLib'], function(myLib){
        function foo(){
          myLib.doSomething();
        }
        return {
          foo : foo
        };
      });

當require()函數加載上面這個模塊的時候,就會先加載myLib.js文件


六、加載非規範的模塊

理論上,require.js加載的模塊,必須是按照AMD規範、用define()函數定義的模塊
實際上雖然已經有一部分流行的函數庫(比如jQuery)符合AMD規範,更多的庫並不符合
那麼,require.js是否能夠加載非規範的模塊呢?

回答是可以的

這樣的模塊在用require()加載之前,要先用require.config()方法,定義它們的一些特徵

舉例來說,underscore和backbone這兩個庫,都沒有採用AMD規範編寫
如果要加載它們的話,必須先定義它們的特徵

      require.config({
        shim: {
          'underscore':{
            exports: '_'
          },
          'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
          }
        }
      });

require.config()接受一個配置對象,這個對象除了有前面說過的paths屬性之外
還有一個shim屬性,專門用來配置不兼容的模塊
每個模塊要定義
(1)exports值(輸出的變量名)表明這個模塊外部調用時的名稱
(2)deps數組,表明該模塊的依賴性

比如,jQuery的插件可以這樣定義:

      shim: {
        'jquery.scroll': {
          deps: ['jquery'],
          exports: 'jQuery.fn.scroll'
        }
      }


七、require.js插件

require.js還提供一系列插件,實現一些特定的功能

domready插件,可以讓回調函數在頁面DOM結構加載完成後再運行

      require(['domready!'], function (doc){
        // called once the DOM is ready
      });

text和image插件,則是允許require.js加載文本和圖片文件

      define([
        'text!review.txt',
        'image!cat.jpg'
        ],
        function(review,cat){
          console.log(review);
          document.body.appendChild(cat);
        }
      );

類似的插件還有json和mdown,用於加載json文件和markdown文件

2012年10月30日 星期二

範例 - JavaScript 等比例縮小圖片

max-width、max-height以及這兩個的 min 版本是偶爾能在某些CSS中看到的屬性設定
不過因為IE不會辨識這些屬性,所以就直接寫JavaScript 調整圖片大小

var img = this.getElementsByTagName("img")[0];

img.onload = function(){
    var maxWidth = "300", maxHeight = "80";
 
    if(img.offsetHeight >= (maxHeight / maxWidth) * img.offsetWidth){
    img.style.width = img.offsetWidth / img.offsetHeight * maxHeight + "px";
    img.style.height = maxHeight + "px";
    }else{
img.style.height = img.offsetHeight / img.offsetWidth * maxHeight + "px";
img.style.width = maxWidth + "px";
    }
}

轉換AJAX取得的XML結構為DOM操作結構

這次的目的是要用AJAX取得的 responseText ,作為節點底下的結構
取得的 responseText 會是 <div>....</div><div>...</div> 這樣的結構
再以DOM方式去操作

通常查詢到的都是用framework的解決方式
例如JQuery - $("...").html( responseText );
Pototype.js也有update() method可以滿足這類需求

不過...沒看到用native JavaScript的過程
想想也是啦...畢竟這樣應該就是要自己去解析字串了
查到較相近的是XML結構的操作
雖然不符合我的需求,但還是值得記錄起來

xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","rss.xml",false);
xmlhttp.send();
xmlText = xmlhttp.responseText;

if (window.DOMParser){
 parser = new DOMParser();
 xmlDoc = parser.parseFromString(xmlText,"text/xml");
} else{
 xmlDoc = new ActiveXObject("Microsoft.XMLDOM");  //for IE
 xmlDoc.loadXML(xmlText);
}

如果是直接取得XML的話用以下形式即可

xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","rss.xml",false);
xmlhttp.send();
xmlDoc = xmlhttp.responseXML;

2012年10月29日 星期一

Python 基礎-List 介紹 與常見BIF示範

Python保存資料使用的是類似陣列的清單結構
但型別是由Python編譯器處理
Python的List能混合存入各類型的資料,如字串和數字混合放置
另外List中也能包含其他的list,就像多重陣列一樣

Python的多行註解形式是 """註解""" ,單行註解則用#
在編譯器中執行以下程式碼

initList = []         #建立空的List,也可以用BIF方式建立 initList = list()
lists = ["new", "show", "Eclipse"]
demo = ["aa", ["bb", "cc"]]
print(initList)
print(lists[1])    
print(len(lists))     #這裡用上了len() BIF回傳List長度
print(demo[1])      

"""
output:

[]
show
3
['bb', 'cc']
"""

Python稱呼它的內建方法為BIF(built-In Function),常看到的還有

  函式                        說明                                    範例

append()   -   將一筆資料附加到尾端          lists.append("lol")
pop()        -   從最尾端取出一筆資料          lists.pop()           #output:lol
extend()    -   加入一群資料                       lists.extend(["hi", "hello"])
remove()   -   移除特定的資料項                 lists.remove("hi")
insert()      -   插入一筆資料                       lists.insert(0, "head")
list()         -   用於新建空清單的函式
len()         -   取得List長度                         len(List)
range()     -    回傳一個Iterator                    for num in range(4):  #output:0 1 2 3
                   並依照給定的範圍產生數字     print(num)
sort()       -   將List排序,由低到高
reverse()   -  逆轉陣列的順序
count()     -   回傳某個值在List中出現的次數

sum()      -   會將引數中的List數字作加總        sum(list)
randint()   -   前回介於兩引數之間的任意整數      randint(1, 10)
int()        -   將一個字串或數字轉換為整數
float()      -   將一個字串或數字轉換為浮點數
str()        -   將數字轉換成字串
id()         -   傳回一個資料物件在Python中的獨特編號
input()     -   取得並回傳使用者輸入

enumerate() - 補足Iterator的index值,使用方法如下:
           for i, n in enumerate([1, 3, 5]):
       print(i, n)

"""  output
0 1
   1 3
   2 5
"""

zip()     -   整合多個Lists,使用方法如下:
          names = ["John", "Marry", "Tom"]
          sexes = ["Male", "Female", "Male"]
          for name, sex in zip(names, sexes):
              print(name, sex)

"""  output
John Male
   Marry Female
                        Tom Male
"""

Python 安裝配置與 Eclipse PyDev 設定

參考文章:Python: Eclipse+Pydev環境安裝教學
                 在 Windows 7 環境安裝 Python 2.6.6

本篇文章介紹 Python 3 在 Windows 環境的安裝配置過程
另外開發過程會以PyDev作為IDE

首先到Python官網下載需要的版本
安裝後到 [電腦] -> 右鍵[內容] -> [進階系統設定] -> [環境變數]  中添加環境變數
環境變數的設定過程類似 Java


PYTHON_HOME :C:\Python33
PATH: ;%PYTHON_HOME%    (附加在現有內容後)


新建立PYTHON_HOME變數後,在PATH變數加上上述內容就完成了
要確認的話可以到 cmd (命令提示字元) 中輸入 Python 看看

Python雖然可以多個版本並存,但能設定在PATH裡的只能有一個版本
設定主要開發版本即可

Python本身有自帶 IDLE 可以做為簡單版的編譯器使用
它有幾個簡單的快捷鍵:

TAB鍵可以提示尚未輸入完的程式碼
Alt + P 可以召回前一組輸入的程式碼
Alt + N 移往下一個程式碼


不過因為Python是個極端重視縮排的語言
往往縮排就可以讓人在除錯過程中搞得要死不活
建議還是用IDE進行開發
個人因常使用Eclipse而選擇用其平台的PyDev

在Eclipse中安裝 plugin
[Help] -> [Install New software]  加入
name: Pydev
url: http://pydev.org/updates

就個人遇過的情形 Aptana 跟 Pydev 的安裝會有衝突
因為兩者套件都各自有引用對方的部分
最後我是只選擇安裝PyDev

安裝完後到 [Window] –> [PyDev] -> [Interpreter – Python] 設定Python編譯器的路徑
加入 Python 安裝目錄底下的所有東西就好了
之後開發時選擇Python專案就可以開始開發Python了

2012年10月24日 星期三

Java 數值轉換函式區別 - 以Integer.parseInt()、Integer.valueOf()為例

之前對於Java在數值轉換中為什麼會提供這兩種功能幾乎一樣的方法感到疑惑
看了原始碼就可以得到解答
public static Integer valueOf(String s) throws NumberFormatException {
  return new Integer(parseInt(s, 10));
}

public static int parseInt(String s) throws NumberFormatException {
  return parseInt(s,10);
}

答案在於回傳型別的不同
parseInt()回傳 int 基本型別,valueOf()則回傳Integer型別
除了Integer外,其他的數值型別也有同樣的方法(Double、Float...etc)

在Java1.4版本以前這兩種方法有差異,但在後來版本裡因為有auto boxing機制而沒差了

2012年10月22日 星期一

JavaScript 少用全域變數的理由

在 JavaScript: The Good Parts 書中即有討論到這個議題(心得文連結)
容易造成變數名稱紊亂這點,可以盡量用匿名函式的寫法解決
在匿名函式中使用的變數名稱就不會跟全域變數產生衝突
但除了這個問題外,還有效能的考量
JavaScript搜尋變數的方式是 區域變數 -> 更上層的區域變數 -> .... -> 全域變數
若有常用到的全域變數,把它另存成區域變數對效能有很大的幫助

function displayData(){
  //caching variables
  var _ctrl=ctrl;
  var _nui=nui;
  ....
}

Excel 開啟產出的 .CSV檔為亂碼的解決方式

CSV檔為以逗號區隔欄位,用來儲存資料的純文字格式
指定了編碼的純文字格式應該是與亂碼無緣
但有時以微軟的Excel開啟.csv檔卻會發現內容為亂碼
發生的原因是微軟內部系統處理格式時會在開頭暗中寫入一串判別的字碼
而微軟系統就以文字檔案中是否含這串暗碼來判定要以UTF-8或ASCII格式讀取檔案

所以我們若要讓我們產出的CSV檔能正確的被Excel讀取,就要在檔案開頭加入暗碼


public CSVWriter(OutputStream out){
    this.out=out;

    //Corresponding to Microsoft Excel, make file won't open with scrambles in Excel
    byte[] BOM_UTF8 = { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };
    try {
    out.write(BOM_UTF8);
    } catch (IOException e) {
    System.err.println("BOM寫入CSV失敗");
    }
    ....
}

2012年10月16日 星期二

IE 中checkbox的checked=true不打勾問題解決


這個問題是發現在IE中會沒辦法看到checkbox的選項被打勾
檢查其屬性checked=true無誤

追查下發現原因為在該checkbox尚未被IE初始化(還沒加入DOM樹中)前設定此屬性

解決方法有兩種 -
1.將其加入DOM樹中再設定屬性
2.改為設定其 defaultChecked=true

2012年10月15日 星期一

動態加入CSS 與 IE 的 innerHTML 屬性討論

這幾天有遇到一個情況是動態加入CSS時發現在IE會遇到問題
查過後才發現 IE 會限制部分Element的 innerHTML 屬性值的設定
col, colgroup, frameset, html, style, table, tbody, tfoot, thead, title, tr
上述標籤的innerTML attribute 在 IE 中均為唯讀

以下有兩種處理方式可通用在所有瀏覽器中

1.
....
    var se = document.getElementById("ieStyleApplier");
    se.innerHTML = "";
    se.innerHTML = "_<style>" + o.responseText + "</style>";
...

先設定一個空的div element
再再將CSS內容將入到div的範圍內用(本例中的o.responseText 為Ajax取得CSS內容)
注意stye tag 前加入 '_' 作為前綴字元(prefix character)
這是避免IE解析div element時讀到style tag會停止解析的情形
猜測這是IE的安全性考量
加入前綴字元後就沒問題了,也可以對應到其他的browser

2.

  (function(o){
      var newStyle = document.createElement("link");
      newStyle.rel = "stylesheet";
      newStyle.href = o;
      var head = document.getElementsByTagName('head')[0];
      if(head)
          head.appendChild(newStyle);
      else
          document.body.appendChild(newStyle);
  })(url);

用一次性的函式動態加入link element後引入CSS

2012年10月8日 星期一

跨網域資料取得解決方式 - JSONP

參考文章:【原創】說說JSON和JSONP,也許你會豁然開朗,含jQuery用例
                      使用 JSONP 跨站請求

JSON為JavaScript中常用的資料交換格式
簡單、輕型的特性讓他成為Web 開發的首選,這裡就不多提了

而在Web開發的過程中,我們有時會遇到跨網域存取資料的需求
不管是基於分散流量需求,又或者是使用雲端提供業者的服務
使用第三方提供的服務、資源
甚至在同一個專案目錄底下開發的檔案也可能因啟用HTTPS而導致網域的不同

我們有這麼多會碰上跨網域的情況
但Browser 本身的安全機制會禁止我們直接從其他網域取得資料
瀏覽器唯一能從其他網域取得資源的機會就是JavaScript的匯入
也因此衍伸出一個非官方的解決方案,也就是JSONP
JSONP的全名是JSON with Padding
這項技巧本身與JSON沒有實際關係,僅僅是因為JSON是實作上最使用的格式

JSONP簡單的來說即是利用JavaScript的動態載入
將JSON格式的資料讀取進來後,直接在JavaScript做處理
這與 AJAX是不同的技術方式
AJAX的目的是獲取目標頁面的資料,而JSONP則是獲取一段JavaScript程式碼

欲取得資料的頁面寫下類似下面這段程式碼:

    var head = document.getElementsByTagName('head')[0];
    var storeList = null;

//倒入JSON資料到storeList
var JSONDataGetter = function(data){
storeList = JSON.parse(JSON.stringify(data));
};

var src = "firmList.jsp";      //JSON資料提供頁面
var js = document.createElement('script');
sJs.setAttribute('src', src);
    if(head)
        head.appendChild(js);
    else
        document.body.appendChild(js);

上面這段的 JSONDataGetter() 會將傳入的JSON資料轉成JavaScript物件
接著是資料提供者的頁面的程式碼
<%
response.setContentType("text/javascript");  //將頁面的型別設定為JavaScript

....  //JSP code

int counter = 0;
out.print("JSONDataGetter([");
    for(Map.Entry<String, HashMap<String, String>> m : totalMap.entrySet()){
    HashMap<String, String> store = m.getValue();
   
    if(counter == 0){
    out.print(String.format("{ \"storeName\" : \"%s\", \"storeType\" : \"%s\", \"storeLocation\" : \"%s\"}", m.getKey(), store.get("storeType"), store.get("storeLocation")));
    }else{
    out.print(String.format(", { \"storeName\" : \"%s\", \"storeType\" : \"%s\", \"storeLocation\" : \"%s\"}", m.getKey(), store.get("storeType"), store.get("storeLocation")));
    }
    counter++;
    }
    out.print("])");

%>


大部分都是字串拼貼的處理,顯示出來大概就是下面這樣

JSONDataGetter([ //JSON資料])

引入的JavaScript code會回頭呼叫 JSONDataGetter() 處理
實務上也會常看到在引入JavaScript的路徑上加上callback變數回傳
如:var src = "firmList.jsp?callback=XXX";
提供 JSON 資料的頁面再根據參數而輸出不同的字串,以便呼叫不同的處理函式
如此一來就能做到更多元的處理
這就是JSONP了

2012年10月3日 星期三

[轉載] POI設置EXCEL單元格格式為文本、小數、百分比、貨幣、日期、科學計數法和中文大寫

轉載來源


以下將要介紹的每一種都會用到這三行中的變量

            HSSFWorkbook demoWorkBook = new HSSFWorkbook();  
            HSSFSheet demoSheet = demoWorkBook.createSheet("The World's 500 Enterprises");  
            HSSFCell cell = demoSheet.createRow(0).createCell(0);

第一種:日期格式

            cell.setCellValue(new Date(2008,5,5));
            //set date format
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            HSSFDataFormat format= demoWorkBook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("yyyy年m月d日"));
            cell.setCellStyle(cellStyle);

第二種:保留兩位小數格式
            cell.setCellValue(1.2);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00"));
            cell.setCellStyle(cellStyle);

這裡與上面有所不同,用的是HSSFDataFormat.getBuiltinFormat()方法,之所以用這個,是因為0.00是Excel內嵌的格式

第三種:貨幣格式

            cell.setCellValue(20000);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            HSSFDataFormat format= demoWorkBook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("¥#,##0"));
            cell.setCellStyle(cellStyle);

第四種:百分比格式

            cell.setCellValue(20);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00%"));
            cell.setCellStyle(cellStyle);
  此種情況跟第二種一樣

第五種:中文大寫格式

            cell.setCellValue(20000);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            HSSFDataFormat format= demoWorkBook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("[DbNum2][$-804]0"));
            cell.setCellStyle(cellStyle);

第六種:科學計數法格式

            cell.setCellValue(20000);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            cellStyle.setDataFormat( HSSFDataFormat.getBuiltinFormat("0.00E+00"));
            cell.setCellStyle(cellStyle);
此種情況也與第二種情況一樣

2012年10月2日 星期二

[轉載] Java SE 6.0 內建 Javascript 引擎測試

轉載來源

前言 :
Java SE 6.0 採納 JSR 223 建議,使得 Java 能夠與眾多 Script 語言協同運作
透過 Java 定義的標準 API,能夠讓 Script 直接存取和控制 Java 的物件
而Java 應用程式當中也能夠直接內嵌 Script 環境

範例說明 :
底下將會帶幾個範例, 來說明這個 Java 的 JavaScript 引擎能夠達成的工作 :
- 範例01 : 第一個 Hello World 程式, 直接將 JavaScript 代碼丟入 JS Engine 的 eval 函式執行.
package john.js;

import javax.script.*;

public class JSHello {
    public static void main(String args[]) {
        ScriptEngineManager mgr = new ScriptEngineManager(); // 建立一個 Script 管理員
        ScriptEngine jsEngine = mgr.getEngineByName("JavaScript"); // 取得 Javascript 引擎
        try {
          jsEngine.eval("print('Hello, world!')"); // 透過 Javascript 引擎解譯 print('hello, world') 命令
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }    
    }
}

- 範例02:執行外部 javasript 檔案, 這裡是直接把含 JavaScript 代碼(Hello.js) 的內容透過 InputStream 丟給 JS Engine 的 eval API 執行.
package john.js;

import javax.script.*;
import java.io.*;

public class JSRunExtendFile {
    public JSRunExtendFile() {
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine engine = engineMgr.getEngineByName("ECMAScript");      
        try {
            InputStream is = new FileInputStream(new File("Hello.js"));
            Reader reader = new InputStreamReader(is);
            engine.eval(reader);
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        JSRunExtendFile ref = new JSRunExtendFile();  
    }
}

- 範例03:透過 Invocable 介面喚起 Javascript 函數.
package john.js;

import javax.script.*;

public class JSInvokeFunction {

    public static void main(String[] args) {
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine jsEngine = engineMgr.getEngineByName("ECMAScript");
        try {
            jsEngine.eval("function sayHello() {"
                    + "  println('Hello, world!');" + "}");
            Invocable invocableEngine = (Invocable) jsEngine;
            invocableEngine.invokeFunction("sayHello");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } catch (NoSuchMethodException nsme) {
            nsme.printStackTrace();
        }
    }
}

- 範例04:Script 直接存取 Java 物件
package john.js;

import javax.script.*;
import java.util.*;

public class JSAccessJavaObject {

    public static void main(String[] args) {
        List namesList = new ArrayList();
        namesList.add("Jill");
        namesList.add("Bob");
        namesList.add("Laureen");
        namesList.add("Ed");
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine jsEngine = engineMgr.getEngineByName("ECMAScript");
        jsEngine.put("namesListKey", namesList); // 將 java 變數指定給 javascript 環境中的
                                                    // nameListKey 變數
        System.out.println("Executing in script environment...");
        try {
            // 列印 Java 的 nameList 變數內容,並新增一個 Dana 字串資料到 List 尾端
            jsEngine.eval("var x;" + "var names = namesListKey.toArray();"
                    + "for(x in names) {" + "  println(names[x]);" + "}"
                    + "namesListKey.add(\"Dana\");");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }
        System.out.println("Executing in Java environment...");
        for (String name : namesList) {
            System.out.println(name);
        }
    }
}

- 範例05:透過 Invocable 將參數值傳遞給 Javascript 中的函數
package john.js;

import javax.script.*;
import java.util.*;

public class JSPassValueToFunction {

    public static void main(String[] args) {
        List namesList = new ArrayList();
        namesList.add("Jill");
        namesList.add("Bob");
        namesList.add("Laureen");
        namesList.add("Ed");
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine jsEngine = engineMgr.getEngineByName("ECMAScript");
        Invocable invocableEngine = (Invocable) jsEngine;
        try {
            jsEngine.eval("function printNames1(namesList) {" + "  var x;"
                    + "  var names = namesList.toArray();"
                    + "  for(x in names) {" + "    println(names[x]);" + "  }"
                    + "}" + "function addName(namesList, name) {println('addName: Add '+name);"
                    + "  namesList.add(name);" + "}");
            invocableEngine.invokeFunction("printNames1", namesList);
            invocableEngine.invokeFunction("addName", namesList, "Dana");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } catch (NoSuchMethodException ex) {
            ex.printStackTrace();
        }
    }
}

補充說明 :
* 純種 Java 與 Javascript 結合
將Script整合進應用程式所能達到的彈性空間的確十分強大
如果能在我們開發的系統也提供Scripting的能力,除了強化擴充性之外,整個系統的質感也是提升不少…爽度up!up!
但以往要達成這目標,有個難處,就是解釋並執行Script用的Interpreting Engine那裡來,自己寫顯然是太累了…。
Java 6這回帶來了好消息:Scripting API,在Java系統中結合Scripting再也不難了,你只需要import javax.script.*; ...

[轉載] javax.script包探秘 javascript與java交互執行

轉載來源

這個包用來和JavaScript進行互操作,比如JAVA類可以調用JavaScript中的方法,而JS也可調用 JAVA中的方法.

1、可用的腳本引擎
Java 6提供對執行腳本語言的支持,這個支持來自於JSR223規範,對應的包是javax.script
預設情況下,Java 6只支持javascript腳本,它底層的實現是Mozilla Rhino,它是個純Java的javascript實現
可以通過下面的代碼列出當前環境中支持的腳本引擎:

// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// evaluate JavaScript code from String
engine.eval("print('Welocme to java world')");
System.out.println();
ScriptEngineManager manager = new ScriptEngineManager();
List<ScriptEngineFactory> factories = manager.getEngineFactories();
for (ScriptEngineFactory f : factories) {
    System.out.println("egine name:" + f.getEngineName() + "\n"
            + "engine version:" + f.getEngineVersion() + "\n"
            + "language name:" + f.getLanguageName());
}

結果輸出:
Welocme to java world
egine name:Mozilla Rhino
engine version:1.6 release 2
language name:ECMAScript

可以看到,Java內置只支持javascript一種腳本
但是,只要遵循 JSR223,便可以擴展支持多種腳本語言
可以從https://scripting.dev.java.net/上查找當前已被支持的腳本的第三方庫

2、hello script
示例如上面的代碼所示。
使用的API還是很簡單的,ScriptEngineManager是ScriptEngine的工廠,實例化該工廠的時候會加載可用的所有腳本引擎
從工廠中創建ScriptEngine可以使用getEngineByName、getEngineByExtension或 getEngineByMimeType來得到
只要參數名字能對上。執行腳本調用eval方法即可(效果等同於javascript中的eval)

3、傳遞變量
可以向腳本中傳遞變量,使得Java代碼可以和腳本代碼交互,示例如下:

// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.put("a", 4);
engine.put("b", 6);
try {
    Object maxNum = engine
            .eval("function max_num(a,b){return (a>b)?a:b;}max_num(a,b);");
    System.out.println("max_num:" + maxNum);
} catch (Exception e) {
    e.printStackTrace();
}
輸出內容:max_num:6

對於上面put的變量,它作用於自身engine範圍內,也就是ScriptContext.ENGINE_SCOPE
put 的變量放到一個叫Bindings的Map中,可以通過 engine.getBindings(ScriptContext.ENGINE_SCOPE).get(「a」);得到put的內容
和 ENGINE_SCOPE相對,還有個ScriptContext.GLOBAL_SCOPE 作用域
其作用的變量是由同一ScriptEngineFactory創建的所有ScriptEngine共享的全局作用域。

4、動態調用
上面的例子中定義了一個javascript函數max_num,可以通過Invocable接口來多次調用腳本庫中的函數
Invocable接口是 ScriptEngine可選實現的接口。下面是個使用示例:

// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
try {
    engine.eval("function max_num(a,b){return (a>b)?a:b;}");
    Invocable invoke = (Invocable) engine;
    Object maxNum = invoke.invokeFunction("max_num", 4, 6);
    System.out.println(maxNum);
    maxNum = invoke.invokeFunction("max_num", 7, 6);
    System.out.println(maxNum);
} catch (Exception e) {
    // TODO: handle exception
}

上面的invokeFunction,第一個參數調用的腳本函數名,後面跟的可變參數是對應的腳本函數參數
Invocable還有個很酷的功能,就是動態實現接口,它可以從腳本引擎中得到Java Interface 的實例
也就是說,可以定義個一個Java接口,其實現是由腳本完成
以上面的例子為例,定義接口JSLib,該接口中的函數和javascript中的函數簽名保持一致:

public interface JSLib {
    public int max_num(int a, int b);
}
[java] view plaincopyprint?
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
try {
    engine.eval("function max_num(a,b){return (a>b)?a:b;}");
    Invocable invoke = (Invocable) engine;
    JSLib jslib = invoke.getInterface(JSLib.class);
    int maxNum = jslib.max_num(4, 6);
    System.out.println(maxNum);
} catch (Exception e) {
    // TODO: handle exception
}

5、使用Java對象
可以在javascript中使用Java代碼,這確實是很酷的事情
在Rhino中,可以通過importClass導入一個類,也可以通過importPackage導入一個包,也可以直接使用全路經的類
在創建對象時,new也不是必須的。示例代碼如下:

try {
    String script = "var list = java.util.ArrayList();list.add(\"kafka0102\");print(list.get(0));";
    engine.eval(script);
} catch (Exception e) {
    e.printStackTrace();
}

又例如:

try {
    engine.eval("importPackage(javax.swing);" + "var optionPane = "
            + "  JOptionPane.showMessageDialog(null, 'Hello, world!');");
} catch (ScriptException ex) {
    ex.printStackTrace();
}

6、編譯執行
腳本引擎默認是解釋執行的,如果需要反覆執行腳本,可以使用它的可選接口Compilable來編譯執行腳本,以獲得更好的性能,示例代碼如下:

try {
    Compilable compEngine = (Compilable) engine;
    CompiledScript script = compEngine
            .compile("function max_num(a,b){return (a>b)?a:b;}");
    script.eval();
    Invocable invoke = (Invocable) engine;
    Object maxNum = invoke.invokeFunction("max_num", 4, 6);
    System.out.println(maxNum);
} catch (Exception e) {
    e.printStackTrace();
}

7、總結
除了上面提到的特性,腳本引擎還有一些不錯的功能
比如可以執行腳本文件,可以由多線程異步執行腳本等功能
引入腳本引擎,可以對一些配置擴展和業務規則做更強大而靈活的支持,也方便使用者選擇自己熟悉的腳本語言來編寫業務規則等

public class JSTest {
    public static void main(String[] args) throws Exception {
        // 根據js的後綴名生成一個解析JS的腳本解析引擎

        ScriptEngine engin = new ScriptEngineManager()
                .getEngineByExtension("js");
        // 查詢一下這個引擎是否實現了下面很實用的接口

        System.out.println(engin instanceof Invocable);
        // 聲明兩個對象,傳入到JS裡面去

        JFrame jf = new JFrame("test");
        List list = new ArrayList();
        // 得到綁定的鍵值對象,把當前的兩個JAVA對象放進去

        Bindings bind = engin.createBindings();
        bind.put("jf", jf);
        bind.put("list", list);
        // 把綁定下的鍵值對象放進去,作用域是當前引擎的範圍

        engin.setBindings(bind, ScriptContext.ENGINE_SCOPE);
        // 用引擎執行一段寫在JS文件裡面的代碼

        Object obj = engin.eval(new FileReader("test.js"));
        // 這個時候返回值當然 是null了

        System.out.println(obj);
        // 把當前的引擎強制轉為Invocable,這樣就可以調用定義在JS文件裡面的一個一個函數了

        Invocable in = (Invocable) engin;
        // 得到了從JS裡面返回來的對象

        List l = (List) in.invokeFunction("getNames");
        System.out.println(l);
        // 調用一下定義在JS裡面的另一個函數

        in.invokeFunction("testJS");
        // 最後調用一個函數,該函數可以使我們前面定義的窗體顯示出來

        in.invokeFunction("doSth");
    }
}
下面是定義在test.js裡面的內容
[javascript] view plaincopyprint?
function doSth()
{
    jf.setSize(500,300);
    jf.setVisible(true);
    jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);  
}  
function getNames()
{   
    list.add("doSth");
    list.add("getNames");
    return list;
}  
function testJS()
{
    print('Hello world!');
}
我們可以看到,在JAVA運行了以後,窗體會顯示出來,並且我們可以接收到從JS解析引擎裡面傳回的數據
當然我們也可以調用一個很普通的JS函數
想像一下,如果我們把我們程序運行時的一些對象都設到Bindings裡面去,那麼我們JS豈不是有很大的自由度了嗎?
因為JS裡面也可以操作我們的Java對象了,並且我們可以像ava編程一樣的對JS編程了,還不用再編譯,馬上就可以運行.靈活性豈不是變得更高了嗎?

參考自:
【1】javax.script包探秘 javascript與java交互執行
http://blog.csdn.net/lvshow/article/details/7252236
【2】Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/

2012年9月20日 星期四

POI 自訂方法

copyRow() - 複製Row的method
updateFormula() - 計算Excel公式結果的方法

複製Row的method

POI有cloneSheet()這個複製sheet的方便方法
但是卻缺少複製sheet裡的row的方法
當然考量到公式的計算與合併的儲存格的位置
這個方法須自訂是不太令人意外

private void copyRow(HSSFSheet worksheet, int sourceRowNum, int destinationRowNum) {
    // Get the source & new row
    HSSFRow newRow = worksheet.getRow(destinationRowNum);
    HSSFRow sourceRow = worksheet.getRow(sourceRowNum);

    // If the row exist in destination, push down all rows by 1 else create a new row
    if (newRow != null) {
        worksheet.shiftRows(destinationRowNum, worksheet.getLastRowNum(), 1);
    } else {
        newRow = worksheet.createRow(destinationRowNum);
    }

    // Loop through source columns to add to new row
    for (int i = 0; i < sourceRow.getLastCellNum(); i++) {
        // Grab a copy of the old/new cell
        HSSFCell oldCell = sourceRow.getCell(i);
        HSSFCell newCell = newRow.createCell(i);

        // If the old cell is null jump to next cell
        if (oldCell == null) {
            newCell = null;
            continue;
        }

        // Use old cell style
        newCell.setCellStyle(oldCell.getCellStyle());

        // If there is a cell comment, copy
        if (newCell.getCellComment() != null) {
            newCell.setCellComment(oldCell.getCellComment());
        }

        // If there is a cell hyperlink, copy
        if (oldCell.getHyperlink() != null) {
            newCell.setHyperlink(oldCell.getHyperlink());
        }

        // Set the cell data type
        newCell.setCellType(oldCell.getCellType());

        // Set the cell data value
        switch (oldCell.getCellType()) {
            case Cell.CELL_TYPE_BLANK:
                newCell.setCellValue(oldCell.getStringCellValue());
                break;
            case Cell.CELL_TYPE_BOOLEAN:
                newCell.setCellValue(oldCell.getBooleanCellValue());
                break;
            case Cell.CELL_TYPE_ERROR:
                newCell.setCellErrorValue(oldCell.getErrorCellValue());
                break;
            case Cell.CELL_TYPE_FORMULA:
                newCell.setCellFormula(oldCell.getCellFormula());
                break;
            case Cell.CELL_TYPE_NUMERIC:
                newCell.setCellValue(oldCell.getNumericCellValue());
                break;
            case Cell.CELL_TYPE_STRING:
                newCell.setCellValue(oldCell.getRichStringCellValue());
                break;
        }
    }

    // If there are are any merged regions in the source row, copy to new row
    for (int i = 0; i < worksheet.getNumMergedRegions(); i++) {
        CellRangeAddress cellRangeAddress = worksheet.getMergedRegion(i);
        if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) {
            CellRangeAddress newCellRangeAddress =
                  new CellRangeAddress(newRow.getRowNum(),
                    (newRow.getRowNum() +
                    (cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow())
                     ),
                    cellRangeAddress.getFirstColumn(),
                    cellRangeAddress.getLastColumn());
            worksheet.addMergedRegion(newCellRangeAddress);
        }
    }
}


計算Excel公式結果的方法

使用POI填入公式時,Excel表並不會將計算後的結果立刻展示
它的作法是顯示出公式(舊版office)或待開啟編輯權限後才計算(office 2010)
以下方法能計算特定行數的Excel公式


private void updateFormula(HSSFWorkbook wb, HSSFSheet sheet, int row){    
    Row r = sheet.getRow(row);    
    Cell cell = null;    
    HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb);    
    for(int i = r.getFirstCellNum(); i < r.getLastCellNum(); i++){    
        cell = r.getCell(i);    
        if(cell.getCellType()== Cell.CELL_TYPE_FORMULA)    
            eval.evaluateFormulaCell(cell);    
    }    
}


HSSFFormulaEvaluator提供了evaluateFormulaCell(Cell cell)方法
計算公式保存結果,但不改變公式
而evaluateInCell(Cell cell)方法是計算公式,並將原公式替換為計算結果,
也就是說該單元格的類型從Cell.CELL_TYPE_FORMULA改為Cell.CELL_TYPE_NUMBERIC

HSSFFormulaEvaluator提供了靜態方法evaluateAllFormulaCells(HSSFWorkbook wb)
計算一個Excel文件的所有公式,用起來很方便

2012年9月19日 星期三

除錯 - java.lang.IllegalStateException: getOutputStream() called

今天碰上的這個錯誤訊息是在JSP頁面發生

ServletResponse.getOutputStream()
ServetResponse.getWriter()

這兩個方法都會有可能在JSP輸出頁面內容的時候被調用
當其中一個方法被調用過後,再呼叫另一個方法就會報錯誤
解決的方法不外如下:

out.clearBuffer();                           //清除buff
out = pageContext.pushBody();    //更新out的內容

2012年7月5日 星期四

C# 資料庫存取

首先關於資料庫的開啟可考慮兩種方式
1.用using{...}區塊
using(SqlConnection con = new SqlConnection())
{
    con.ConnectionString = "....";
    con.Open();
    ....
}

這種方法會在脫離區塊後立刻釋放Connection物件的資源
所以不用使用資料庫的關閉方法(如Close()、Dispose())
另外就是正規的寫法

2.

using System.Data.SqlClient;
....
SqlConnection con = new SqlConnection("xxx");       //xxx = connection string
SqlCommand cmd = new SqlCommand("xxx", con); //xxx = SQL command 
SqlDataReader dr;
...
con.Open();
dr = cmd.ExecuteReader();    //使用DataReader物件讀取資料庫內容
...
con.Close();  


SqlCommand物件的幾個常用方法跟屬性
   ExecuteNonQuery() -
   可以執行INSERT、UPDATE、DELETE等SQL command,成功時回傳受影響的紀錄筆數

   ExecuteScalar() -
   適用於結果集只回傳一個資料列的第一欄,例如SQL command 的COUNT()、MAX()等

   ExecuteReader() -
   執行Command物件中所指定的SQL的SELECT敘述,並建立一個DataReader來瀏覽資料

   CommandText -
   設定或取得要執行的命令


SqlCommand使用範例:

string sqlStr = string.Empty;
sqlStr = "INSERT INTO TableA(姓名, 聯絡方式, 薪水)" + "VALUES(@name, @tel, @pay)"
SqlCommand cmd = new SqlCommand( sqlStr , con);
cmd.CommandType = CommandType.StoreProcedure;     //預先編譯SQL處理函式

cmd.Parameters.Add(new SqlParameter("@name", SqlDbType.NVarChar));
cmd.Parameters.Add(new SqlParameter("@tel", SqlDbType.NVarChar));
cmd.Parameters.Add(new SqlParameter("@pay", SqlDbType.NVarChar));
cmd.Parameters["@name"].Value = "vc56";
cmd.Parameters["@tel"].Value = "0000000";
cmd.Parameters["@pay"].Value = "$20000";

cmd.ExecuteNonQuery();


DataReader有幾個方法跟屬性較為重要,以下列出:
   FieldCount屬性 -
   將已查詢欄位的總數傳回
 
   Read() -
   將指標移到下一筆,並判斷是否指到EOF
   如果到資料結尾則回傳false

   Item[i] -
   取得第i欄的資料內容

   Item["欄位名稱"]集合 -
   取得指定欄位名稱所指的資料內容

   GetValue(i) -
   取得第i欄位的資料內容,傳回值為object資料型別

   IsDBNull(i) -
   判斷第i個欄位使否為資料庫Null值

使用範例:
while(dr.Read())
{
  for(int i = 0; i < dr.FieldCount; i++)
  {
      textBox1.Text += dr[i].ToString() + "\t";
  }
}

以執行效率來說,使用索引會比使用欄位名稱還快
此外,取出時可使用方法來省略手動轉型的動作,如下:

while(dr.Read())
{
  textBox1.Text += dr.GetString(0) + "\t";
  textBox1.Text += dr.GetInt32(1).ToString() + "\t";
  ...
}


DataSet則是ADO.NET的特色
他會先將資料庫的內容存至記憶體中,並得以執行離線操作
操作完畢後再將內容回寫至資料庫中
適用於多用戶端資料存取,當然占用的記憶體多是不可避免的事
DataSet中可以包含一個以上的DataTable物件
由DataAdapter使用Command物件執行SQL command
再將取得的資料寫至DataSet,如此就能用DataTable存取資料表,範例:

using System.Data.SqlClient;
using System.Data;

.....
using(SqlConnection con = new SqlConnection())
{
    con.ConnectionString = "....";
    DataSet ds = new DataSet();
    SqlDataAdapter dsContent = new SqlDataAdapter("xxx", con);  //xxx = sql command
    dsContent.Fill(ds, "Content");    //使用Fill()時,DataAdapter會自動連線到資料庫
    /*
    Adapter也可以改成SqlDataAdapter dsContent = new SqlDataAdapter();
                                     dsContent.SelectCommand = cmd;
    這樣就可以使用SqlCommand物件來保持彈性


    另外有幾種特性語法示範
   int n = ds.Tables.Count;                          //取得DataSet中DataTable的總數
   String tName = ds.Table[i].TableName;  //取得第i個DataTable的表格名稱
   */
   DataTable dt = ds.Tables["Content"];     //對應SqlDataAdapter  Fill ()時填入的資料表名
   for(int i = 0; i < dt.Rows.Count; i++)
   {
        for(int j = 0; j < dt.Columns.Count; j++)
        {
              textBox1.Text += dt.Rows[i][j].ToString() + "\t";
        }
        textBox1.Text += Enviroment.NewLine;
   }
}
....


DataSet另外尚有使資料表之間產生關連的操作

ds.Realations.Add(  "關聯名稱",
      ds.Tables["dt1"].Columns["dt1要關聯的欄位名稱"],
      ds.Tables["dt2"].Columns["dt2要關聯的欄位名稱"],
)

日後可與DataView物件結合使用
dataGridView1.DataSource = ds;
dataGridView1.DataMember = "dt1";

dataGridView2.DataSource = ds;
dataGridView2.DataMember = "dt1.關聯名稱";

2012年7月3日 星期二

[轉貼] C# 淺析SqlConnection的dispose和close方法差異

原文

引用微軟ADO.Team的經理的話說,sqlconnection的close和dispose實際是做的同一件事,
唯一的區別是Dispose方法清空了connectionString,即設置為了null.

SqlConnection con = new SqlConnection("Data Source=localhost;Initial Catalog=northwind;User ID=sa;Password=steveg");
        con.Open();
        con.Close();
        con.Open();
        con.Dispose();
        con.Open();

    上例運行發現,close掉的connection可以重新open
    dispose的不行,因為connectionstring清空了,
    會拋出InvalidOperationException提示The ConnectionString property has not been initialized,
    但請注意此時sqlconnection對象還在。

    如果dispose後給connectionString重新賦值,則不會報錯。 
    由此得出的結論是不管是dispose還是close都不會銷毀對象,即不會釋放內存,
    它們會把sqlconnection對象丟到連接池中,那此對象什麼時候銷毀呢?
    我覺得應該是connection timeout設置的時間內,
    如果程序中沒有向連接池發出請求說要connection對象,sqlconnection對象便會銷毀,
    這也是連接池存在的意義。

    剛開始以為dispose會釋放資源清空內存,
    如果這樣的話,連接池不是每次都是要創建新對象,那何來重用connection呢?
    在網上看到很多人說close比dispose好,
    我想真正的原因是dispose後的sqlconnection對象要重新初始化連接字符串而已,
    並不是像某些人說的dispose會釋放對象。

    所以在try..catch和using的選擇上大膽的使用using吧,
    真正的效率差異我想可能只有百萬分之一秒吧
    (連接池重用該連接對象初始化連接字符串的時間),
    而且enterprise library中封裝的data access層全是用的using,
    從代碼的美觀和效率上綜合考慮,using好 

    補充:
    using不會捕捉其代碼快中的異常,
    只會最後執行dispose方法,相當於finally{dispose},
    本文主要是想說明dispose和close的差異,因為using是絕對dispose的,
    可是如果人為的寫try..finally有的人會選擇close有的人會選擇dispose,
    實際上在這2者的選擇上是有差異的。

    2012年7月1日 星期日

    C# 文字控制項-自動完成輸入功能

    C#的自動完成功能提供了當使用者在文字方塊中輸入的資料符合條件時
    便會自動填入或彈出符合的字串以提升使用者輸入資料的效率

    TextBox或ComboBox 中的三個屬性須注意:
    AutoCompleteSource、AutoCompleteMode、AutoCompleteCustomSource



    AutoCompleteSource的常用屬性值包括

    1.HistoryList - 以URL中的歷史清單作為自動完成輸入功能的來源
       ex:textBox1.AutoCompleteSource = AutoCompleteSource.HistoryList;
       程式碼部分以下類推

    2.RecentlyUsedList - 以URL中的歷史清單和最近瀏覽過的URL為來源

    3.AllUrl - 以最近瀏覽過的URL作為自動完成輸入功能的來源

    4.FileSystem - 以檔案系統為來源

    5.FileSystemDirectories - 以磁碟和目錄為來源,不包括檔案

    6.AllSystemSources - 以AllUrl和FileSystem為來源

    7.None - 取消自動完成輸入功能來源

    8.CustomSource - 自訂來源,須搭配AutoCompleteCustomSource屬性設定



    AutoCompleteMode常用屬性

    1.Append - 將最可能相符之候選字串其餘部分附加到現有字串之後,並反白顯示
       ex:textBox1.AutoCompleteMode = AutoCompleteMode.Append;

    2.Suggest - 挑選出最符合的字串於下拉選單的選項中

    3.SuggestAppend - 混用上述兩種功能

    4.None - 停用自動完成輸入功能


    AutoCompleteCustomSource屬性

    除了直接在Widget屬性介面上設計外
    也可以用程式碼部分撰寫,如:

    string[] content = new string[]{"a","b","c"};
    AutoCompleteStringCollection newAdd = new AutoCompleteStringCollection();
    newAdd.AddRange(content);

    textBox1.AutoCompleteMode = AutoCompleteMode.Suggest;
    textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
    textBox1.AutoCompleteCustomSource = newAdd;

    2012年6月27日 星期三

    [轉貼] Business or Geek?

    原文


    這個話題來自於華蟒用戶組郵件列表2010年1月的一個貼子,因為我本身不是該組的訂閱者,最近無意中搜索到,覺得有點意思就分享一些想法。
    通常想創業的同學應該也遇到過這樣的一些情況,你有一個想法需要想集思廣益地跟大家分享討論,以便*期待*得到更多的人認同,從而再去推進行動。這裡的討論也由一位叫sliuqin的同學展開,他希望跟老婆想要做一個賣茶的電子商務網站:
    *關於我們*: 目前,我們分別在兩家大型的電子商務公司做*前端開發*,工作2年了。朝九晚六的工作雖然挺安逸,但不是自己想要的,對!我們想創業了。


    *創業項目*: 獨立電子商務網站,主營:茶葉和茶葉相關商品。
    *項目優勢*: 老爸是開茶葉店的。
    *項目開發:* 週期:10個月 人員:
    - 我,負責網站後台開發(Ubuntu server + python + django) - 我老婆,負責前端開發,用戶體驗和視覺設計。
    項目資金:10w,包含我們一年的生活費,和後期的服務器等,吃住在家裡(做寄生蟲)
    現在我們開始了一部分工作,但是是公司工作之餘,我們兩個人的人生經歷還少,我本身也沒有做過專業的python開發,所以想聽聽大家的意見:這個計劃*可行嗎* ? 但這裡討論的不是這個項目是否可行,我想說的如同以下兩位
    • @john :  「你是想賣茶葉啊,還是想賣網站?」
    • @誠子 : 「business or geek?」
    這真的值得作為開發人員要創業時仔細想一想,我也總會走進這樣的一個誤區,到底是在為了一項生意還是因技術追求而做呢?而且這種情況往往更多的發生在技術人員向產品轉型的人群,他們會以技術的角度去衡量一個項目,到底是用.net還是Java,是用HTML5還是HTML4,是使用Django還是Webpy等等……而忽略了去判斷商業的本身,商業計劃可行嗎?盈利模式可靠嗎?目標受眾呢?如何推進呢?…….

    Business是事業,geek是愛好。當然,我從不認為純技術流不能創造一家賺錢的互聯網公司,這樣的公司在互聯網行業中俯首皆是。而且還有部分互聯網公司出現的時候就不知道該怎麼盈利,也就總會有聽到說:「等我們的用戶數足夠大了,我們就會想到盈利方式。」但如果我們認為這些創業者們沒有想過怎麼去「Business」的話,那我想一定是我搞錯了一些東西,他們有想,他們有想得到的某個固定人群,通過技術和人群建立競爭壘壁,爭奪人群本身就是一項「商業」。所以他們選擇了快速開發讓市場去驗證終端需求,而絕對不會在技術的選擇上浪費太多的時間。
    繼續拿<REWORK>說事,」start with business, not a startup」說的就是這個意思,從一開始就想辦法賺錢,行動起來!別用10個月時間+不熟悉的python來做一個電子商店了,架個開源網店就開賣吧,兄弟。
    • 問某遊戲公司CEO:「你們都用什麼技術?」答:「沒關係,用最熟悉的PHP。」
    • 問手機遊戲開發者:「如何切入移動開發,iOS or Android?」答:「Android,因為我熟悉Java 。」
    但話說回來,如果目的就是為了學習一門技能或公司沒有生存壓力下,我個人喜愛並推崇項目嘗試使用最新的技術,而不是固步自封。

    [轉貼] 為何我們要從Node.JS遷移到Ruby on Rails

    原文

    聲明:這篇文章絕不是一篇討論Node.JS和Ruby on Rails孰優孰略的檄文。它描述的只是我們做決策過程中的一些思考、決策背後的原因。兩種框架都非常優秀,都出色的完成了它們的設計初衷,這也是為什麼我們部分的模塊仍然運行在Node.JS上的原因。

    我是Node.JS的大粉絲,認為這是一項讓人非常興奮的技術,相信它會變的越來越流行。我對這項技術非常的欣賞——儘管我們最近把Targeter App從Node.JS遷移到了Ruby on Rails。
    我們當時使用Node.JS開發它的原因很簡單。我有一個程序包,能很快的將我們的應用弄上線(我們花了54小時做這個事情),相比起Ruby,我更常使用的是JavaScript。因為我們的技術架構牽涉到MongoDB,我的這些特長只有在Node.JS環境裡才會有意義。然而,隨著應用規模的增長,我認識到,選擇Node.JS來實現這個應用是個錯誤的選擇。下面讓我來概述一下其中的原因。

    Node.JS很適合做那些有大量短生命期請求的應用。對於傳統的CRUD應用,它也很好,但不是非常的理想。在PHP,Ruby,Python語言裡都有很成熟、優化的很好的框架來處理這種應用。Node.JS裡的所有東西都異步執行的理念對於CRUD應用來說沒有任何效果。其它語言裡的流行的框架能提供非常好的緩存技術,你所有的需求都能滿足,包括異步執行。
    Node.JS是一種非常年輕的技術框架,它的周邊程序庫都不是很成熟。我說這些並沒有任何對那些代碼捐贈者冒犯的意思,他們很優秀,開發出來很多優秀的程序庫。然而,大部分程序庫需要改進,而Node.JS的這種快速成長的環境意味著每一版升級中都帶有大量的變化;當你使用一種前沿技術時,你十分有必要盡快的緊跟最新的版本。這給創業型的企業帶來了很多的麻煩。

    另外一個原因是關於測試。Node.JS裡的測試框架還不錯,但跟Django或RoR平台上的相比還是差一些。對於一個每天都有大量的代碼提交、並且在一兩天內就要發佈的應用來說,程序不能出問題是至關重要的,否則你為此辛苦的努力變得得不償失。沒有人願意花一天的時間改一些弱智的bug。 最後一點,我們需要的是一種能緩存一切的東西,並且要盡快的實現。儘管我們的應用在增長,每秒鐘有上萬次的hits,但絕不會出現很大量的訪問請求;這不是一個聊天程序!主程序最多時也就達到1000RPS,這樣的負載對於Ruby on Rails和Nginx來說算不了什麼。

    如果你現在還在讀這篇文章,那你已經看到了我所有要說的了,你也許非常堅持的想知道我們的應用什麼地方還在使用Node.JS。是這樣的,我們的應用由兩部分組成。一是界面,用戶看到的這部分,二是負責報表管理的部分,以及做日誌的功能。後者是Node.JS的一個最佳使用場景,存在有大量的短週期的請求。這部分的動作需要盡快的執行完成,甚至要在我們的數據推送還沒有完成之前。這很重要,當請求執行還未結束,瀏覽器繼續等待響應結束,這會影響用戶使用體驗。Node.JS的異步特性救了我們。數據要麼被存入數據庫,要麼被處理掉,當請求一旦執行完成,瀏覽器就可以開始做其它重要的事情了。

    [轉貼] C# Delegate/event 的簡單理解

    看這篇前可以先看這篇文章瞭解Delegate的語法與使用方式
    本篇原文=================
    簡單來說就是增加一層間接層來實現晚綁定,從而獲得更為鬆散的耦合

    為什麼要有Delegate? 
    假設有兩個不同類實現的對象A和B,A和B分別有方法a和b。現在需要a方法以一觸發,便執行對象B的方法b。 不使用delegate,我們可以在設計A類的時候在將B類作為參數傳入a方法,這樣可以實現我們的要求。但是這樣做有明顯的缺陷:   
        1. 我們需要事先知道B類。   
        2. 如果還存在C類、D類等的方法需要一併執行,這樣必須重新修改A類的代碼。 如果有了  Delegate,我們可以事先設計一個函數指針,這樣當對象A執行a方法的時候,就可以根據該指針指向的,符合該函數指針法則的方法進行執行。這樣,我們就不需要在設計A類的時候知道B類,只需要知道B的b方法的參數和返回值就可以了。如果還有C類、D類方法需要一併執行,也可以添加到該指針指向的數據結構中。


    示例代碼分析:
    代碼
        class Program
    {
    static void Main(string[] args)
    {
    A a
    = new A();
    B b
    = new B(a);
    a.Method_A();
    }
    }


    class A
    {
    public delegate void Delegate_A();
    public event Delegate_A da;
    public void Method_A()
    {
    da();
    }
    }

    class B
    {
    public B(A a)
    {
    a.da
    += new A.Delegate_A(Method_B);
    }
    public void Method_B()
    {
    Console.WriteLine(
    "B's method!");
    }
    }
    1. 先看Class A中   
    public delegate void Delegate_A(); 這個語句規定了函數指針指向的函數應該符合的規範:沒有返回值,沒有參數傳入。
    2. 接著實例化了一個 Delegate_A的對象da,這裡使用event關鍵字表明它是一個事件。 
    3. 再看Class B的的構造函數,可見B構造時,往對象a的事件中傳入了Method_B的函數地址(a.da += new A.Delegate_A(Method_B)
    4. 在Main函數中執行A對象的Method_A時,觸發了事件da,而da指向了B的方法。這樣即便A對象對B對象一無所知,也同樣能執行B的方法Method_B。這就是Delegate的好處。

    Event有什麼用:
    實際上在上例中將Event關鍵字刪除程序同樣能執行。之所以要加上Event,是因為在程序編譯時,加上Event以後該Delegate的對象會成為該類的私有成員,這樣外界只能通過 += 和 -=來對Delegate的對象da進行操作,而不能直接進行賦值。

    2012年6月26日 星期二

    C# 物件配置與記憶體議題

    對於實值型別(等同Java中稱的基本型別)一樣是放在stack
    在method中也同樣是以複製的形式傳遞
    也就是method中作的變動不會影響到原本的實值型別物件
    不過若要改變傳遞的形式則可以使用 ref、out 兩個關鍵字

    ref的用法為宣告使用call by reference
    如:public void swap(ref int x, ref int y){ ... //code for swap x&y }


    使用method時亦用ref,如:

    int a = 1;    int b = 2;
    swap(ref a, ref b);


    如此一來a與b的值就會在method呼叫過後改變了
    C#在配置物件(參考型別資料)時亦隱含了ref的宣告
    所以省略不寫也沒關係
    但若明白的了ref宣告
    則必須在參數、引述的地方都宣告,否則會引起編譯錯誤,如:

    public void dosth(ref int[,] intary){ .... }

    int[,] a = new int[]{{1,2},{3,4}}
    dosth(ref a);     //如果參數、引數只有其中一方宣告ref會引起錯誤


    out關鍵字的作用則是傳出值,如:
    public void assignValue(out int x, out int y){
        x = 6;  y = 10;                //必須在method中宣告值
    .........
    }

    int a; int b;                           //未賦予初始值
    assignValue(out a, out b);    //此時a = 6, b = 10


    不過用法雖然多樣化了,但似乎連Visual Studio Team也不鼓勵ref、out的使用
    畢竟若設計師不熟悉用法則容易出錯
    一般是建議找替代方法來達成目標

    struct的使用可以參考MSDN上的說明-使用結構
    使用上有無法繼承與多型的限制,但在僅用來儲存field時會有較class更好的效能
    其記憶體配置也是位在stack
    所以指派的時候也是使用複製,等同於創造出新的struct
    注意這點以避免疏失
    而struct作為參數傳遞時,可考慮使用ref,以優化性能(當然這時候要注意值的變化)

    C#的class有時候並不提供擴展,如第三方的類別等
    要封裝類別只要在class宣告前加入sealed關鍵字,如:sealed class ap{...}
    如果打算擴展這些類別,C#提供如下作法:

    static class secondAp{
        public static string getAns(this ap apobj, double fi1){...}
    }


    擴張方法必須是存在於靜態類別中的靜態方法
    this ap 指定擴充ap類別

    [轉載]強制使用javascript-檢查該頁面是否啟用javascript

    原文====================

    現在ajax的技術很流行,也用在很多web 2.0的網站上,但該技術是基於使用者端瀏覽器的javascript,萬一使用者把它關閉怎麼辦?還要為它們另外撰寫ajax或javascript特效的替代程式嗎?我不負責任的回答是:不要!

    大部份網頁瀏覽器,預設都是啟用javascript,那誰會停用?
    第一個是什麼都不懂,不小心關掉的人;第二個是很懂,非常非常在乎安全,或想亂搞的人。 我會和客戶說,這兩類的人,少之又少,又很難搞定,與其多花時間金錢來照顧它們寫替代程式,不如直接告訴它們請啟用javascript再來瀏覽此頁面。 方法很簡單:
    在想強制使用javascript的頁面,<head>和</head>間,插入這段通用的tag

    <noscript>
         <META HTTP-EQUIV="Refresh" CONTENT="0;URL=JS_Please.html">
    </noscript>


    如果有人瀏覽這頁面卻未啟用javascript,就會直接轉到JS_Please.html,接著在JS_Please.html上撰寫請啟用javascript的說明,或教不小心關掉的人要怎麼開啟它

    後記:
    php有個函式是get_browser,原以為是可以檢查瀏覽器所有的狀態,就很高興的拿來試試…沒想到只能得知瀏覽器是否有支援javascript,卻無法得知是否啟用;真是個讓人白高興一場的函式…
    網路上還有很多檢查javascript是否啟用的方法和資訊,但大多複雜難用;用noscript來轉頁,是最簡便的方法。

    2012年6月21日 星期四

    ASP.NET Web Application 離線方式

    這是我目前看ASP.NET中覺得最好的功能之一XD
    在網站需要進行維護等需離線的行為時
    為了避免讓User誤會而可以使用這個功能

    只要在Application的根目錄底下建立App_Offline.htm 並將訊息寫在這個頁面即可
    只要目錄底下有這個檔案
    .NET就會自動將網頁導覽到這個頁面
    如此就可以很方便的進行升級

    要注意的事項則是網頁大小必須超過512k
    且對於根目錄的權限要小心
    以免被惡意寫入這個檔案而導致整個Application不起作用

    2012年6月14日 星期四

    固定ASP.NET程式開發伺服器的Port for web網站

    在web application專案中可以使用專案工具控制([專案]>[屬性])
    而如果只是建置時選擇web網站的話,則用以下方式解決

    step1. 在visual studio的[工具]>[選項]>[專案和方案]>[一般]>取消"永遠顯示方案"
    step2.[工具]>[外部工具]>[加入]>輸入以下設定
              標題: sever port:8080       <--隨便打
              命令:C:\Program Files (x86)\Common Files\microsoft shared\DevServer\9.0\
                          WebDev.WebServer.EXE
              引述:/port:8080/path:$(ProjectDir)\
    step3.[工具]選項中就會出現剛才輸入的標題,執行以啟動固定的port
    step4.在網站上按右鍵>[屬性頁]>[起始選項]>[使用自訂伺服器]>基礎URL輸入
              http://localhost:8080

    測試:直接輸入http://localhost:8080/Default.aspx

    SSIS 心得與小技巧

    SSIS為Microsoft SQL Server裡Business Intelligence的一環
    功能是整合各個不同來源的資料
    以下環境處理使用SQL Server 2008
    注意雖然SQL Server2012提供了轉換的功能來相容2008的檔案
    但實際上還是很有可能會發生問題

     SSIS的檔案建立後會是dtsx檔
    實際上裡面的內容是XML檔,由SSIS引擎進行實際操作
    2008的版本有時候會發生問題是整個專案檔中作了變更
    而dtsx檔卻沒跟著做正確變更,特別是在連線相關元件的連線改變時
    有時候最好的情形甚至是將用到連線的元件砍掉重練
    如果碰到設定應該沒問題卻總是收到SSIS的失敗訊息,那就應該考慮這個作法
    假如遇到連線設定一直無法寫入連線元件(通常是連線的密碼)
    可以考慮使用全域變數來規避,或是重新開啟SSIS試看看
    在我接觸的經驗裡,連線設定是SSIS很弱的一個部分

    SSIS開啟時會還原到上次處理時的內容
    接著重新驗證載入內容裡面每一個處理過程,其中包含連線到裡面的資料來源
    這樣往往使得SSIS在開啟過程花費大量時間而延誤處理
    記得關閉SSIS前先關閉目前開啟的每一個dtsx檔

    另外開啟SSIS的專案檔可以先做一個設定
    上方工具列中的SSIS -> work offline
    這樣可以避免SSIS在載入較大的檔案時卡在驗證的流程
    當要用到取得網路上的資料時就取消work offine
    雖然麻煩了些,但卻是省下時間的一招

    雖然SSIS中有Script Component可以用來寫入程式運作
    但在維護的考量上,部門鼓勵的處理方式是盡量用SSIS元件進行處理
    使用元件能明顯的表達設計的意圖
    雖然有時可能會較使用程式的方式來的麻煩,但帶來的好處應該是更多


    以下紀錄曾碰過疑問的情境

    1.重覆資料判斷處理

    情境:想將重覆的資料判別出並寫進log,而不只是用sort工具將多出來的資料刪去
    問題:如何判別資料是duplicate instances
    解決:先利用derived column 新增欄位,再用aggregate元件裡的count物件來取得
                duplicate instance其數量,再將他寫入新欄位中,用conditional split處理

    2.對應空值處理

    情境:針對客戶給的資料做預防處理
    問題:資料有可能不會有該欄位或該欄位無值
    解決:conditional split裡 ISNULL(xxx) 和 TRIM(xxx) == "" 兩條件併用,
                TRIM()是預防給予的資料是數格空白

    3.Thread

    情境:欲建立物件來處理從data flow中經conditional split分支出來的資料
    問題:物件在同時處理兩端不同走向的資料時可能會有multi-thread處理的問題
    解決:先利用union all原件將所有要處理的資料先做統整,再用物件處理

    4.Thread-2

    情境:寫資料進error log碰到前端流程有太多錯誤資料要寫,使後端流程無法取得log檔
    問題:手動加入lock控管機制也碰到lock object的取得也有thread上的問題
    解決:經討論後直接濾掉前端的錯誤資料以避免整體流程發生錯誤
                實際上SSIS的線程管理是不常看到討論的一環,而且也容易有問題
                有這類需求時再考慮盡量避免掉資料存取衝突的可能,重新設計SSIS流程