2020年9月18日 星期五

Published 星期五, 9月 18, 2020 by with 0 comment

頒獎用程式【分享】

 


頒獎用顯示程式(含原始碼 pbw)
開發工具:Powerbuilder 10.5
版本:2.0.2.6
版本時間:2019/3/5
平台:Windows

程式說明:
因應公司於大型會場需要頒獎時,能將得獎者名單顯示在會場投射螢幕上,由於公司參加者眾多超過300人,部分獎項得獎者可能多達50人以上,若主持人連續抽出得獎者並說出得獎者名字或編號時,現場吵雜情況下,得獎者會不知道自己已經得獎,因此透過投射螢幕可以把得獎者清單列於畫面上,讓得獎者不會錯過。

Read More
    email this       edit

2020年6月19日 星期五

Published 星期五, 6月 19, 2020 by with 0 comment

EXCEL Convert to CSV File Tool

Tool Name:  EXCEL2CSV.exe
Verson: 1.0.0.0
Development Environment: MS-Visual Studio 2017
FrameWork: .Net 3.5

Description:

this is a console tool to convert Excel files (eg: xls , xlsx , csv) to a uniform comma separated format data file (.CSV) without quotes between each field.

if excel cell data include Enter/TAB characters or double will be replace by empty.

if excel cell data include comma character , it will be replace by dot .

this tool is third party for data process application (eg: EDI)to import pure data.

this tool package include donet FrameWork 3.5 installer.

OS: Windows XP/Windows 7/Windows 8/Windows 10

download: there

usage:

excel2csv.exe [full source filename] [full destination file name]

if source file name or destination file name not with full path , it will use caller environment path locate.

if destination file failed to create is an error on convert duration.

full source file name or full destination file name need warp by double-Quotes if it include space.

example:

excel2csv D:\datasource\importData.xls C:\temp\import.csv

excel2csv "C:\Documents and Settings\admin\My Documents\importData.xls" "C:\temp files\import.csv"
Read More
    email this       edit

2020年5月26日 星期二

Published 星期二, 5月 26, 2020 by with 0 comment

使用 NPOI 設定欄位(CELL)樣式與寬度


如何使用 NPOI 來設定CELL(欄位)的樣式:邊框、底色、字形,等等

底下是個 樣式類別檔案,把常用的欄位形態定義好,以後直接取用

DefXSSFstyle.cs
using System.Drawing;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using NPOI.HSSF.Util;

namespace XssSTYLE
{
    /// <summary>
    /// 已經定義好的欄位樣式,如果需要新樣式,複製程式碼並賦與新名稱,修改樣式產生程式碼即可使用
    /// </summary>
    class DefXSSFstyle
    {
        /// <summary>
        /// 抬頭樣式 - 01
        /// 灰色底色,白色字,正黑體,無邊框
        /// </summary>
        /// <param name="wb"></param>
        /// <returns></returns>
        public XSSFCellStyle Banner01(IWorkbook wb)
        {
            XSSFCellStyle csh = (XSSFCellStyle)wb.CreateCellStyle();
            csh.Alignment = HorizontalAlignment.Center;
            csh.VerticalAlignment = VerticalAlignment.Center;
            csh.WrapText = true;
            csh.FillForegroundXSSFColor = new XSSFColor(Color.FromKnownColor(KnownColor.Gray));
            csh.FillPattern = FillPattern.SolidForeground;

            IFont font1 = wb.CreateFont();
            font1.FontName = "正黑體";
            font1.FontHeightInPoints = 14;
            font1.Color = HSSFColor.White.Index;

            csh.SetFont(font1);

            return csh;
        }

        /// <summary>
        /// 標題樣式 - 01
        /// 亮黃色底色,黑色字,正黑體,有邊框
        /// </summary>
        /// <param name="wb">來源的IWorkbook</param>
        /// <returns></returns>
        public XSSFCellStyle Hander01(IWorkbook wb)
        {
            XSSFCellStyle csh = (XSSFCellStyle)wb.CreateCellStyle();
            csh.Alignment = HorizontalAlignment.Center;
            csh.VerticalAlignment = VerticalAlignment.Center;
            csh.WrapText = true;
            csh.FillForegroundXSSFColor = new XSSFColor(Color.FromKnownColor(KnownColor.LightYellow));
            csh.FillPattern = FillPattern.SolidForeground;

            csh.BorderLeft = BorderStyle.Thin;
            csh.BorderTop = BorderStyle.Thin;
            csh.BorderRight = BorderStyle.Thin;
            csh.BorderBottom = BorderStyle.Thin;

            csh.LeftBorderColor = HSSFColor.DarkBlue.Index;
            csh.TopBorderColor = HSSFColor.DarkBlue.Index;
            csh.RightBorderColor = HSSFColor.DarkBlue.Index;
            csh.BottomBorderColor = HSSFColor.Black.Index;

            IFont font1 = wb.CreateFont();
            font1.FontName = "正黑體";
            font1.FontHeightInPoints = 12;
            font1.Color = HSSFColor.Automatic.Index;

            csh.SetFont(font1);

            return csh;
        }

        public XSSFCellStyle Hander09(IWorkbook wb)
        {
            XSSFCellStyle csh = (XSSFCellStyle)wb.CreateCellStyle();
            csh.Alignment = HorizontalAlignment.Center;
            csh.VerticalAlignment = VerticalAlignment.Center;
            csh.WrapText = true;
            csh.FillForegroundXSSFColor = new XSSFColor(Color.FromKnownColor(KnownColor.LightGreen));
            csh.FillPattern = FillPattern.SolidForeground;

            csh.BorderLeft = BorderStyle.Thin;
            csh.BorderTop = BorderStyle.Thin;
            csh.BorderRight = BorderStyle.Thin;
            csh.BorderBottom = BorderStyle.Thin;

            csh.LeftBorderColor = HSSFColor.DarkBlue.Index;
            csh.TopBorderColor = HSSFColor.DarkBlue.Index;
            csh.RightBorderColor = HSSFColor.DarkBlue.Index;
            csh.BottomBorderColor = HSSFColor.Black.Index;

            IFont font1 = wb.CreateFont();
            font1.FontName = "正黑體";
            font1.FontHeightInPoints = 12;
            font1.Color = HSSFColor.Automatic.Index;

            csh.SetFont(font1);

            return csh;
        }

        public XSSFCellStyle Field01(IWorkbook wb)
        {
            XSSFCellStyle csh = (XSSFCellStyle)wb.CreateCellStyle();

            csh.BorderLeft = BorderStyle.Thin;
            csh.BorderTop = BorderStyle.Thin;
            csh.BorderRight = BorderStyle.Thin;
            csh.BorderBottom = BorderStyle.Thin;

            csh.LeftBorderColor = HSSFColor.Black.Index;
            csh.TopBorderColor = HSSFColor.Black.Index;
            csh.RightBorderColor = HSSFColor.Black.Index;
            csh.BottomBorderColor = HSSFColor.Black.Index;

            IFont font1 = wb.CreateFont();
            font1.FontName = "新細明體";
            font1.FontHeightInPoints = 12;
            font1.Color = HSSFColor.Automatic.Index;

            csh.SetFont(font1);

            return csh;
        }

        public XSSFCellStyle Field09(IWorkbook wb)
        {
            XSSFCellStyle csh = (XSSFCellStyle)wb.CreateCellStyle();

            csh.FillForegroundXSSFColor = new XSSFColor(Color.FromKnownColor(KnownColor.LightGreen));
            csh.FillPattern = FillPattern.SolidForeground;

            csh.BorderLeft = BorderStyle.Thin;
            csh.BorderTop = BorderStyle.Thin;
            csh.BorderRight = BorderStyle.Thin;
            csh.BorderBottom = BorderStyle.Thin;

            csh.LeftBorderColor = HSSFColor.Black.Index;
            csh.TopBorderColor = HSSFColor.Black.Index;
            csh.RightBorderColor = HSSFColor.Black.Index;
            csh.BottomBorderColor = HSSFColor.Black.Index;

            IFont font1 = wb.CreateFont();
            font1.FontName = "新細明體";
            font1.FontHeightInPoints = 12;
            font1.Color = HSSFColor.Automatic.Index;

            csh.SetFont(font1);

            return csh;
        }
    }
}

這個類別裏面定義了5個樣式分別是: Banner01、Hander01、Hander09、Field01、Field09


使用方式如下:

static public void test2(IWorkbook wb)
{
    // 樣式元件
    DefXSSFstyle style = new DefXSSFstyle();

    XSSFCellStyle css1 = style.Banner01(wb);
    XSSFCellStyle css2 = style.Field01(wb);
    XSSFCellStyle css3 = style.Field09(wb);
    XSSFCellStyle css4 = style.Hander01(wb);
    XSSFCellStyle css5 = style.Hander01(wb);

    // 建立分頁賦予分頁名稱
    ISheet ws1 = wb.CreateSheet("樣式表");

    // 新增Row
    IRow row1 = ws1.CreateRow(0);

    // 新增Cell
    ICell cell0 = row1.CreateCell(0);
    ICell cell1 = row1.CreateCell(1);
    ICell cell2 = row1.CreateCell(2);
    ICell cell3 = row1.CreateCell(3);
    ICell cell4 = row1.CreateCell(4);

    // 設定文字與樣式與寬度
    cell0.SetCellValue("Banner01");
    cell0.CellStyle = css1;
    // 自動寬度
    ws1.AutoSizeColumn(0);

    cell1.SetCellValue("Field01");
    cell1.CellStyle = css2;
    // 欄寬10
    ws1.SetColumnWidth(1, (int)((10 + 0.71) * 256));

    cell2.SetCellValue("Field09");
    cell2.CellStyle = css3;
    // 欄寬11
    ws1.SetColumnWidth(2, (int)((11 + 0.71) * 256));

    cell3.SetCellValue("Hander01");
    cell3.CellStyle = css4;
    // 欄寬13
    ws1.SetColumnWidth(3, (int)((13 + 0.71) * 256));

    cell4.SetCellValue("Hander01");
    cell4.CellStyle = css5;
    // 欄寬14
    ws1.SetColumnWidth(4, (int)((14 + 0.71) * 256));

}

上面分別產生5個CELL,並設定上那5種 STYLE
在設定 CELL 寬度的公式 =

(int)((欄寬 + 0.71) * 256)

可以得到正確寬度

例如:

ws1.SetColumnWidth(1, (int)((10 + 0.71) * 256));

則如下:




Read More
    email this       edit

2020年5月24日 星期日

Published 星期日, 5月 24, 2020 by with 0 comment

關於 NPOI 取得 列 (Row) 和 欄(Cell) 數量




關於 NPOI 裏面如何取得 列(Row) 與欄(Cell)的數量。

NPOI 對於 IRow 或 ICell 沒有類似集合數量的 count 或是 length 可以取得,
可是相對的有 FirstRowNum / LastRowNum / FirstCellNum / LastCellNum 可以用。

它代表是 首列位置 / 末列位置 / 首欄位置 / 末欄位置

但是非常奇異的是,這兩種的取出值卻完全不是這麼一回事

正常來說,以位置來看的話, C# 計算是從 0 開始

因此以上圖 EXCEL來看應該是:

FirstRowNum = 0
LastRowNum = 10
FirstCellNum  = 0
LastCellNum  = 10

列(Row) 和 欄(Cell) 和都 各11個

但是,我們實際把它取出來看卻發現詭異事情了

Console.WriteLine("FirstRowNum = " + ws1.FirstRowNum.ToString() + " ,LastRowNum = " + ws1.LastRowNum.ToString());
Console.WriteLine("FirstCellNum = " + ws1.GetRow(0).FirstCellNum.ToString() + " ,LastCellNum = " + ws1.GetRow(0).LastCellNum.ToString());

結果:



看! LastCellNum 居然是 11 

NPOI 有太多小問題真的很想吐槽了

這個問題去年被發現,但是至今尚未改善

如要參照這個屬性一定要非常小心,哪天突然修正好,你的程式可能也要跟著更新與修正了。
Read More
    email this       edit

2020年5月22日 星期五

Published 星期五, 5月 22, 2020 by with 0 comment

關於 NPOI 的 ISheet.RemoveRow 會清除到錯誤的列

移除到錯誤位置


關於 NPOI 套件裏面的 ISheet.RemoveRow 會移除到錯誤的列 的問題

如上圖,我使用如下指令:

// 移除最末行
ws1.RemoveRow(ws1.GetRow(10));

正確來說,它應該會移除第11行的 統計列,但是卻把第10列移除了?

ISheet.GetRow 的陣列是從 0 開始的。

如果你用

// 移除第一行
ws1.RemoveRow(ws1.GetRow(0));

則甚麼事都不會發生,也就是RemoveRow處理的位置是錯誤的。
這個問題好像 在2016年就有人提出了,到現在還沒改善....

使用上還是要小心
Read More
    email this       edit

2020年5月20日 星期三

Published 星期三, 5月 20, 2020 by with 0 comment

C# 使用NPOI 把資料寫到excel檔案(xlsx)


10X10矩陣統計與列表


利用 NPOI 套件產生如上圖的內容到一個excel檔案
本案例 C# 為 .Net 4.5 ,NPOI 為 2.5.1 版

程式碼如下:

// 樣式元件
DefXSSFstyle style = new DefXSSFstyle();

IWorkbook wb = new XSSFWorkbook();
// 建立分頁賦予分頁名稱
ISheet ws1 = wb.CreateSheet("統計表");
// 建立數字陣列 10 X 10
for(int i = 1; i<= 10; i++)
{
    IRow row0 = ws1.CreateRow(i - 1);
    for (int j = 1; j <= 10; j++)
    {
        ICell cell0 = row0.CreateCell(j - 1);
        cell0.SetCellValue(((i - 1) * 10) + j);
    }
}

// 建立合計
ASCIIEncoding aSCII = new ASCIIEncoding();
XSSFCellStyle cssf = style.Field09(wb);
IRow subRow = ws1.CreateRow(ws1.LastRowNum + 1);
for(int k = 1; k <= 10; k++)
{
    // 設定公式
    string headChar = aSCII.GetString(new byte[] { (byte)(64 + k) });
    ICell subCell = subRow.CreateCell(k - 1);
    subCell.SetCellFormula("SUM(" + headChar + "1:" + headChar + ws1.GetRow(0).LastCellNum.ToString() + ")");
    // 加入樣式
    subCell.CellStyle = cssf;
}

// 儲存
string saveFile = savePath + "\\NPOI_RES.xlsx";
// 使用檔案複寫
FileStream fs = new FileStream(saveFile, FileMode.Create, FileAccess.Write);
wb.Write(fs);








上面順序為:
1.產生 WorkBook 
2.利用 WorkBook 產生 WorkSheet 
3.利用 WorkSheet 產生 IRow 
3.利用 IRow 產生 ICell
4.在 Cell 上設定資料內容(文字/數值/公式),或加上樣式(Style) 

Read More
    email this       edit

2020年3月10日 星期二

Published 星期二, 3月 10, 2020 by with 0 comment

如何在網頁中呼叫windows本地執行程式 How to run windows application (.exe) in HTML

有時候我們希望在網頁上開啟本地端的應用程式,但是受限於安全性的關係,通常流覽器都不允許執些開啟本地『執行程式』

在 IE 底下,其實可以透過 ActiveXObject 內建物件建立起 wscript.shell 介面,利用此介面來呼叫本地端程式(其實不一定是程式,已註冊文件類型都可以透過 wscript shell 來開啟。

常見方法如下:


<html>

<head>
<script language="javascript">
function Run(strPath)
{
var objShell = new ActiveXObject("wscript.shell");
objShell.exec(strPath);
objShell = null;
}
</script>
</head>

<body>
<h1>Call target file : D:\Apps\procexp.exe</h1>
<BUTTON class=button onclick="Run('D:\Apps\procexp.exe')">執行 Procexp</BUTTON>
</body>

</html>


上面就是利用 wscript.shell 呼叫指定程序 procexp.exe

問題是...

它是 IE Only

而且,受限於IE版本的關係,比較新版的 IE 必須要去作安全設定才能整常使用,相當不實用。

那,是否有其他方式呼叫本地應用程式呢?

有的,那就是使用自定義的協定讓瀏覽器接受,例如:eD2k:// VeryCD://等等



如上圖如果利用自訂義的協定就可以讓瀏覽器向註冊表請求自定義協定的動作

因此,必須自己創建協定(不可以和目前使用的協定名稱相同)

例如我要自定義一個協定名稱叫 ZzTest:// ,接下來作法如下

一、開啟註冊登錄表

      使用命令列模式(使用者必須有系統管理權限) 輸入 Regedit ,然後 Enter

二、建立自定義協定

      在 \HKEY_CLASS_ROOT\ 下建立一個機碼,這個機碼名稱 將來就是作為『協定』的名稱。我在這裡使用了 ZzTest 。

      然後在 ZzTest 下繼續建立 DefaultIcon 和 shell 機碼
      在 shell 下建立 open 機碼
      在 open 底下建立 command 機碼


      然後,繼續在 ZzTest 下修改(預設值) 內容 = zztest protocol (這只是協定名稱,隨便取無所謂)

      接下來繼續新增一個字串值 = URL Protocol (內容保持空白),要注意這個字串值名稱不可以亂取,因為它是 Windows 的識別關鍵字,用來告訴系統,這裡有個自定義的 URL 協定,協定名稱就是 ZzTest (機碼名)

三、建立協定動作

      有了協定名稱,接下來要定義呼叫協定後的動作。

      首先在 DefaultIcon 內的預設值修改成 程式&路徑,假如我要呼叫的程式是 D:\Apps\procexp.exe,那就輸入(記得要加雙引號)
"D:\Apps\procexp.exe"
      如果有參數輸入就增加參數
"D:\Apps\procexp.exe" "%1"


      繼續在 \ZzTest\shell\open\command 下修改預設值為
"D:\Apps\procexp.exe"
      如果有參數輸入就增加參數
"D:\Apps\procexp.exe" "%1"



      完成上述動作後,此協定就可以被瀏覽器呼叫了


四、HTML內的呼叫方法

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5" />
<title>Call Local executable file</title>
<style>
.myButton {
box-shadow: 3px 4px 0px 0px #1564ad;
background:linear-gradient(to bottom, #79bbff 5%, #378de5 100%);
background-color:#79bbff;
border-radius:5px;
border:1px solid #337bc4;
display:inline-block;
cursor:pointer;
color:#ffffff;
font-family:Arial;
font-size:17px;
font-weight:bold;
padding:12px 44px;
text-decoration:none;
text-shadow:0px 1px 0px #528ecc;
}
.myButton:hover {
background:linear-gradient(to bottom, #378de5 5%, #79bbff 100%);
background-color:#378de5;
}
.myButton:active {
position:relative;
top:1px;
}
</style>
</head>

<body>
<h1>Call target file : D:\Apps\procexp.exe</h1>
<a href="Zztest://"><div class="myButton">Press here to test</div></a>
<body>
</html>

說實在,重點只有一行
<a href="Zztest://" />

五、呼叫結果

IE:
FF:
Chrome:






















Read More
    email this       edit

2020年1月3日 星期五

Published 星期五, 1月 03, 2020 by with 0 comment

java.lang.NoSuchMethodError : org.apache.poi.poifs.filesystem.POIFSFileSystem.hasPOIFSHeader(Ljava/io/InputStream;)Z 問題解決方法


這個是在使用 POI 讀取 EXCEL 的 xlsx 檔案造成的

LOCAL.WorkBook = WorkBookFactory.create(FileStream);


使用 WorkBookFactory.create 時內部會呼叫 POIFSFileSystem.hasPOIFSHeader 函式。
 如果呼叫失敗就出現這個錯誤訊息。

所以正常來說 POIFSFileSystem.class 裡面是包含 hasPOIFSHeader 函式的。

看大部分網路上的論壇討論結果或是意見,通常是 POI 所需使用的 jar 檔案使用了不匹配/不同版本的套件導致。

但這個問發現在我另一個 jetty 版的 Railo 4.0.4.001 伺服器上卻不會發生。

為什麼?兩個伺服器版本都是一樣的啊?裡面套件也應該相同的啊?

為此,我便進行研究了一下。

1、發生這個錯誤的伺服器是以 Tomcat7 執行的伺服器,而另一個不會發生這個錯誤的是以 jetty 執行的伺服器。而兩方的版本都是當初官方公佈的 4.0.4.001 版本。

2、比對兩邊使用 POI 的 jar 檔案,雙方的檔案尺寸是相同的,檔案數量也相同。


    在此處 POI 相關的 jar 共有4個,這些都是同時隨伺服器版本一起發佈的,不是我另外安裝的,應該不會存在版本問題吧。

3、如果 POIFSFileSystem.class 不包含 hasPOIFSHeader 函式,理論上 jetty 伺服器也會出現錯誤。結果卻沒有發生這個錯誤,代表 POIFSFileSystem.class 是正確的。

但,為甚麼?

所以我用解壓縮軟體來觀察 jar 檔案的成員。



發現了奇怪的的是在 apache-poi.jar 和 apache-poi-tm-extractors.jar 裡面都出現了成員 POIFSFileSystem.class ,而且檔案尺寸完全不一樣!

而且路徑都在 org.apache.poi.poifs.filesystem 下,我猜這就是貓膩了。

因為在這種情況下只會有一個成員會被載入成功,至於是哪一個先被載入要看伺服器核心的 javaloader 處理規則。

因此,我把這兩個 jar 檔案分別解開,然後看看這兩個 POIFSFileSystem.class 是不是都包含有 hasPOIFSHeader 函式。


我利用 HEX 編輯器檢查這兩個 Class 是否包含了 hasPOIFSHeader 文字



啊啊啊,
很明顯這個 apache-poi-tm-extractors.jar 裡面沒有 hasPOIFSHeader 文字,也代表沒有函式存在。

既然如此,代表 javaloader 在 jetty 版本一定是先載入 apache-poi.jar 才能正常工作,而 Tomcat7 則是先載入了  apache-poi-tm-extractors.jar 。

至於有沒有辦法變更 javaloader 的順序呢?這個很難說。

因為我有做了下面的實驗:

在 jetty 伺服器下,我把  apache-poi-tm-extractors.jar 更改名稱  apache-o-poi-tm-extractors.jar 讓它的檔案名稱正排序往前:


此時,啟動 jetty 服務後,再度執行 xlsx 檔案讀取就發生錯誤了,這代表 jetty 的 javaloader 是依照檔案名稱的正序來載入的,當載入重複的類別時,第二次的類別實例就會失敗。

但是,依照這種方式使用在 Tomcat7 伺服器時卻是無效的,既然不是名稱順序,那也很難猜出是用甚麼載入順序了方式。

所以,對於 Tomcat7 的修正方式,我是採用把  apache-poi-tm-extractors.jar 裡面的  POIFSFileSystem.class  移除,重新封裝成 jar 放回原目錄後,重新啟動 Tomcat7 ,然後就成功了。







Read More
    email this       edit

2019年12月31日 星期二

Published 星期二, 12月 31, 2019 by with 0 comment

coldfusion component (CFC) 另一種用法


一般 CFC 的寫法大概會像上面這樣:

而呼叫方式如下:


<cfinvoke component="COM.extfunc" method="myescape" returnvariable="escstr">
    <cfinvokeargument name="sdata" value="#myForm.note#">
</cfinvoke>

<!--- 返回 escstr --->
<cfoutput>#escstr#</cfoutput>


上面這種方式屬於純 TAG 寫法,寫起來超麻煩,尤其參數很多時就會寫出一堆  <cfinvokeargument>,真是快昏倒了。

其實,也可以使用創建物件方式來使用這個 Component,會方便許多。

我們把上面的 cfc 改成下面方式


<cfcomponent displayname="extfunc" hint="擴充功能">



     <cffunction name="Init" access="public" returntype="QueryUtility" output="false"
          hint="Returns an initialized QueryUtility instance.">

          <!--- Return This reference. --->
          <cfreturn THIS />
     </cffunction>


     <cffunction name="myescape" access="public" returntype="string" hint="跳脫字元處理"
     description="轉換要顯示在HTML上的特殊字元,這是CF用的,若使用AJAX請使用jquery-extend.js裡面的myescape">
   
          <cfargument name="sdata" type="string" required="yes" hint="來源字串">
     
          <cfset Escaped=Replace(sdata,"<","&lt;","All")>
        <cfset Escaped=Replace(Escaped,">","&gt;","All")>
        <cfset Escaped=Replace(Escaped,"'","""","All")>
        <!---由於是CFOUTPUT使用所以要加上#字處理--->
        <cfset Escaped=Replace(Escaped,"##","####","All")>

        <cfreturn Escaped>
     
     </cffunction>
 
</cfcomponent>


增加了 Init 這個函式,裡用此函式將物件傳回使用。

使用方式就會變成下面方式


<cfset extf = CreateObject("component","COM.extfunc").Init() >

<cfset escstr = extf .myescape(myForm.note) >

<cfoutput>#escstr#</cfoutput>


使用起來真的直覺多了,也方便閱讀,至少不用寫一堆 <cfinvokeargument>



Read More
    email this       edit

2019年12月25日 星期三

Published 星期三, 12月 25, 2019 by with 0 comment

Coldfusion 隱蔽式下載



一般下載時,通常會使用 <a href="URL" /> 這種方式使用者下載,而 URL 大多的做法是指明檔案所在的位置例如  <a href="http://MySite.com/download/picture/myPicture.png">。

這種方式有幾個問題:

1、如果使用者取得此聯結位址後,可以單獨由此取得檔案或盜連,故被直接拿到的風險會很高,因為這個 URL 就是一個開放的位址。

2、由於直接指定檔案位置,很多時候檔案就會被瀏覽器直接開啟,而非下載存檔,對於大部分使用者來說,這是一個貼心的模式,但是對於很多需要下載的商業用途場合就很麻煩,使用者還必須在聯結上按『右鍵』選擇『另存檔案』的方式,操作相當不便。

3、無法設定檢查使用者取得檔案的條件。

因此,透過隱蔽式下載就是一種很好的方法。

程式碼:

<cfsetting enablecfoutputonly="yes">
<!--- 避免下載檔案遇到中文檔名無法正常下載問題而使用的功能,也可以避免使用者知道實際檔案儲存位置 --->
<!---
Arguments : file = 指定檔案名稱
--->
<cftry>
<!--- 判斷使用者來源聯結是否來自本伺服器 或是直接下載,當直接下載時 REFERER 會是 空值--->
    <cfif findnocase(CGI.SERVER_NAME,CGI.HTTP_REFERER) LTE 0>
    <cfthrow type="RefererError" message="#CGI.HTTP_REFERER#:不允許存取">
    </cfif>
<!--- 判斷參數是否存在 --->
<cfif not isdefined("file")>
    <cfthrow type="Arguments" message="參數錯誤!!">
    </cfif>
    <!--- 實際檔案存在位置 --->
<cfset path = expandpath("/Downloads/Pictures")>
    <!--- 檢查檔案是否存在 --->
    <cfset fufile = path & "/" & file>
    <cfif not FileExists(fufile)>
    <cfthrow type="CHECK" message="檔案不存在!!">
    </cfif>
 
    <cffile action="readBinary" file="#fufile#" variable="data1">
 
<cfheader name="Content-Disposition" value="attachment; filename=""#file#""" />
    <cfcontent type="application/octet-stream; charset=big5" variable="#data1#" />
<cfcatch type="any">
 <!--- 發生錯誤時以網頁顯示 --->
 <cfcontent type="text/html" >
 <cfoutput>
  <html>
   <head>
    <meta http-equiv="Content-Type" content="text/html; charset=big5" />
   </head>
   <body>
    <div>
     Type = #cfcatch.Type#
    </div>
    <hr />
    <div>
    Message = #cfcatch.Message#
    </div>
   </body>
  </html>
 </cfoutput>
 <cfabort />
</cfcatch>
</cftry>



使用方式:

<a href="http://MySite.com/Download.cfm?file=2015-8-19  上午 10-42-36.png">

說明:

<cfif findnocase(CGI.SERVER_NAME,CGI.HTTP_REFERER) LTE 0>

這段是用來查驗使用者是否透過本伺服器的網頁正常聯結傳遞過來的。

當然也可以檢查 Cookie ,或是使用其他參數,檢查帳號等等....

<cfset path = expandpath("/Downloads/Pictures")>

實際檔案位置在使用者端看不到,避免直接盜連


<cfheader name="Content-Disposition" value="attachment; filename=""#file#""" />

這段可以強制瀏覽器以下載檔案方式處理,避免在瀏覽器上直接開啟。

<cfcontent type="application/octet-stream; charset=big5" variable="#data1#" />

由於下載時檔案種類繁多,無法一一去處理各種 Content Type 形態,所以直接以 octet-stream 來處理當成串流資料

Read More
    email this       edit

2019年12月24日 星期二

Published 星期二, 12月 24, 2019 by with 0 comment

凹凸世界 - 艾比 的裝備 - 草稿圖

這是凹凸世界裡的角色 - 艾比 的相關配件,純粹幫忙畫稿
角色介紹:看這裡


艾比的翅膀 - 出場機會太少目前只有一幕,只能放慢動畫,慢慢畫出來 QQ



艾比的武器 - 弓與箭




Read More
    email this       edit

2019年12月19日 星期四

Published 星期四, 12月 19, 2019 by with 0 comment

Coldfusion (Railo 3.3.4)使用 POI 元件 寫出 XLSX 檔案

補充前一篇 Coldfusion (Railo 3.3.4) 使用 POI 元件 讀取 xls/xlsx 修改記錄

我在 POIUtility.cfc 內加入一個 Function 用來處理寫出 XLSX 格式檔案

cfc 程式碼(增加):


<!--- 自訂寫出 XLSX 的 EXCEL 檔案--->
    <cffunction name="WriteExcelX" access="public" returntype="void" output="false"
        hint="Takes an array of 'Sheet' structure objects and writes each of them to a tab in the Excel file.">

        <!--- Define arguments. --->
        <cfargument
            name="FilePath"
            type="string"
            required="true"
            hint="This is the expanded path of the Excel file."
            />

        <cfargument
            name="Sheets"
            type="any"
            required="true"
            hint="This is an array of the data that is needed for each sheet of the excel OR it is a single Sheet object. Each 'Sheet' will be a structure containing the Query, ColumnList, ColumnNames, and SheetName."
            />

        <cfargument
            name="Delimiters"
            type="string"
            required="false"
            default=","
            hint="The list of delimiters used for the column list and column name arguments."
            />

        <cfscript>

            // Set up local scope.
            var LOCAL = StructNew();
 
<!--- 使用 XSSFWorkbook 處理 XLSX 格式 --->
var WorkBookFactory = CreateObject("java"," org.apache.poi.xssf.usermodel.XSSFWorkbook").Init();

           
LOCAL.WorkBook = WorkBookFactory;


            // Check to see if we are dealing with an array of sheets or if we were
            // passed in a single sheet.

            if (IsArray( ARGUMENTS.Sheets )){

                // This is an array of sheets. We are going to write each one of them
                // as a tab to the Excel file. Loop over the sheet array to create each
                // sheet for the already created workbook.

                for (
                    LOCAL.SheetIndex = 1 ;
                    LOCAL.SheetIndex LTE ArrayLen( ARGUMENTS.Sheets ) ;
                    LOCAL.SheetIndex = (LOCAL.SheetIndex + 1)
                    ){


                    // Create sheet for the given query information..
                    WriteExcelSheet(
                        WorkBook = LOCAL.WorkBook,
                        Query = ARGUMENTS.Sheets[ LOCAL.SheetIndex ].Query,
                        ColumnList = ARGUMENTS.Sheets[ LOCAL.SheetIndex ].ColumnList,
                        ColumnNames = ARGUMENTS.Sheets[ LOCAL.SheetIndex ].ColumnNames,
                        SheetName = ARGUMENTS.Sheets[ LOCAL.SheetIndex ].SheetName,
                        Delimiters = ARGUMENTS.Delimiters
                        );

                }

            } else {

                // We were passed in a single sheet object. Write this sheet as the
                // first and only sheet in the already created workbook.

                WriteExcelSheet(
                    WorkBook = LOCAL.WorkBook,
                    Query = ARGUMENTS.Sheets.Query,
                    ColumnList = ARGUMENTS.Sheets.ColumnList,
                    ColumnNames = ARGUMENTS.Sheets.ColumnNames,
                    SheetName = ARGUMENTS.Sheets.SheetName,
                    Delimiters = ARGUMENTS.Delimiters
                    );

            }


            // ASSERT: At this point, either we were passed a single Sheet object
            // or we were passed an array of sheets. Either way, we now have all
            // of sheets written to the WorkBook object.


            // Create a file based on the path that was passed in. We will stream
            // the work data to the file via a file output stream.

            LOCAL.FileOutputStream = CreateObject(
                "java",
                "java.io.FileOutputStream"
                ).Init(

                    JavaCast(
                        "string",
                        ARGUMENTS.FilePath
                        )

                    );

            // Write the workout data to the file stream.
            LOCAL.WorkBook.Write(
                LOCAL.FileOutputStream
                );

            // Close the file output stream. This will release any locks on
            // the file and finalize the process.

            LOCAL.FileOutputStream.Close();

            // Return out.
            return;

        </cfscript>
    </cffunction>



測試程式:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=big5" />
<title>無標題文件</title>
</head>

<body>
<!--- 建立並初始化POI物件 --->
<cfset POIobj = CreateObject("component","COM.POIUtility").Init() >

<!--- 取得我要測試的Excel檔案路徑. --->
<cfset myFile = ExpandPath( "./exercises.xls" ) >
<cfoutput>#myFile#<br /></cfoutput>

<!--- 測試一:透過POI物件讀取Excel檔案,並指定此excel檔案有標題列(第一行) --->
<cfset myExcel = POIobj.ReadExcelX(FilePath = myFile,HasHeaderRow = true) >

<!--- 傾印出內容 --->
<cfdump var="#myExcel#">

<!--- 一個 Excel 檔案裡面有多個 Sheet ---->
<!--- 產生一個空的陣列用來接收多個 Sheet 資料 --->

<cfset newExcel = ArrayNew()>
<cfset sheets = 0>
<cfscript>

for(sheet in myExcel){
sheets = sheets + 1;
objSheet = POIobj.GetNewSheetStruct();

objSheet.ColumnList = sheet.QUERY.ColumnList;
objSheet.ColumnNames = ArrayToList(sheet.COLUMNNAMES,",");
objSheet.Query = sheet.Query;
objSheet.SheetName = sheet.NAME;
newExcel[sheets] = objSheet;
}

POIobj.WriteExcelX(FilePath ="C:/Temp/exercises-1.xlsx",
   Sheets = newExcel
);
</cfscript>

<!--- 如果只有一個 Sheet 就不需要產生 Array --->
<!--- 我只從來源取出一個 Sheet 來輸出 --->

<cfscript>
  objSheet = POIobj.GetNewSheetStruct();

  objSheet.ColumnList = myExcel[1].QUERY.ColumnList;
  objSheet.ColumnNames = ArrayToList(myExcel[1].COLUMNNAMES,",");
  objSheet.Query = myExcel[1].Query;
  objSheet.SheetName = myExcel[1].NAME;

  POIobj.WriteExcelX(FilePath ="C:/Temp/exercises-2.xlsx",
   Sheets = objSheet
);
</cfscript>

</body>
</html>


原始檔案內容:



輸出檔案內容:
exercises-1.xlsx


exercises-2.xlsx


這樣確實可以輸出,

只是要注意的是從 ReadExcelX 讀出來的 myExcel 物件不可以直接丟到 WriteExcelX 去,畢竟這兩個 Function 產出和輸入的結構上有些不一樣,所以必須重新處理過才能使用。

還有一點就是 ReadExcelX 可以自動辨識 XLS 或 XLSX 檔案,但是 WriteExcelX 只能寫出 XLSX 檔案,所以標題少了 XLS 字樣。

原因是讀取時使用 org.apache.poi.ss.usermodel.WorkbookFactory 可以自動識別來源格式,但是寫出檔案時,必須指明使用 HSSF 還是 XSSF 來產生檔案,才能確定是產出 XLS 或 XLSX 格式。

而 WriteExcelX 內部是使用 XSSF 來處理,因此只能輸出 XLSX 檔案。
Read More
    email this       edit

2019年12月18日 星期三

Published 星期三, 12月 18, 2019 by with 0 comment

Coldfusion (Railo 3.3.4) 使用 POI 元件 讀取 xls/xlsx 修改記錄



這是配合 上一篇 『Coldfusion直接讀取/產生Excel檔案 (使用POI元件)』進階改版

主要原因是上一篇的功能只能讀取 XLS檔案,但是對於XLSX卻沒有辦法處理。

如果升級 POI的版本到最新版,會造成 RAILO 內建的 Apache 無法支援該套件版本。

經過一翻苦戰,終於搞定如何處理:

首先,RAILO 3.3.4(註一) 以後因為有支援 cfspreadsheet 指令,所以內建了 POI 套件,但是這個指令僅能處理 XLS 檔案,所以可想而知這個套件是非常陽春的,更不用說要能夠讀取 XLSX 檔案了。

Read More
    email this       edit

2019年12月16日 星期一

Published 星期一, 12月 16, 2019 by with 0 comment

12V LED 電壓設計

因為有不少朋友問道:

使用電壓 12V下 LED 的電路設計如何處理,所以這裡我簡單的講解一下電路上的設計方式好了。

其實這是基本電學複習,沒有很特別,讓一些不太懂電學朋友參考。

一般我在設計車用LED電路時,對於LED的電路大概會用下面的方式,當然有的人會使用多少LED來串連是事情況而定,沒有絕對。

Figure 1:

R1 = (12 V - (1.6 Vled x 2)) / 15 mA = 0.5866... ≒ 580 Ω (可以買到的電阻)

Figure 2:

R2 = (12 V - (1.6 Vled x 3)) / 15mA = 0.48 ≒ 470 Ω (常用電阻)

Ω 也可用 Ohm 表示,唸成『歐姆』

這裡有幾個要注意的地方:

1、LED 的電壓,LED 是一顆二極體,電壓順向時有一定的偏壓存在,發光 LED 因應顏色不同,其化學材料也不同,所以每一家生產的 LED 也會有不同的偏壓,最好使用時要使用同一家且同一種型號的 LED 比較不會燒壞 LED。我使用的 LED 偏壓大約 1.6 V,所以計算時我註記成 1.6 Vled。

2、一般 LED 的電流(這裡指 5Φ 左右的 LED),可以承受到 25 mA,但這個電流值幾乎接近熱衰竭電流(也就是在這個電流下長時間工作會讓 LED 內的材料受熱損壞),所以通常我們都會設計在 15 mA ~ 20 mA 左右,以維持 LED 壽命。

3、在這電路上 Q1、Q2 作為 SWITCH (開關) 使用,CE 導通偏壓約莫 0.7 V,通常我會忽略不計。

4、電阻 瓦數可用 1/4W (0.25W) 大小, 依照 P=VI ,Figure 1 上的電阻偏壓約 8.8 V 所以
W = 8.8 V x 15 mA = 0.132 W,1/4W足夠使用了。

5、如果 LED 發光不同不建議串接使用,會發生偏壓較大的 LED 反而不太亮的問題,如果有不一樣顏色的 LED 建議單獨使用不同的電阻來偏壓。


Read More
    email this       edit

2019年5月27日 星期一

Published 星期一, 5月 27, 2019 by with 0 comment

檔案編碼轉換小工具


檔案: FileEncode.exe
運行環境: .NET 2.0 Runtime

注意,這是編碼轉換,不是翻譯喔!!

某些時候在一些新舊系統文件上就是會遇到編碼文件需求不同而傷腦筋,在某些舊程式上就是如此,也無法再改動或是更新等問題,文件本身的編碼就會導致文件交易相當不順暢。

這支小程式就是用來解決這些問題的。

但要注意的是,UNICODE萬國編碼的文字一定會比舊式編碼大得多,可能會遇到某些文字無法轉換問題,例如簡體中文(GB)轉繁體中文(BIG5)。

對於英數字文件轉換是有一定實用性的。
通常新系統會使用UTF-8編碼,舊系統會使用 BIG5編碼/ANSI編碼/ISO8895-1編碼之類的。

我這邊最多用到的是 BIG5 (950) 轉 UTF-8 (65001)

需要用的自己下載去玩吧。

下載點

使用方式:



編碼類型:
37 - IBM037
437 - IBM437
500 - IBM500
708 - ASMO-708
720 - DOS-720
737 - ibm737
775 - ibm775
850 - ibm850
852 - ibm852
855 - IBM855
857 - ibm857
858 - IBM00858
860 - IBM860
861 - ibm861
862 - DOS-862
863 - IBM863
864 - IBM864
865 - IBM865
866 - cp866
869 - ibm869
870 - IBM870
874 - windows-874
875 - cp875
932 - shift_jis
936 - gb2312
949 - ks_c_5601-1987
950 - big5
1026 - IBM1026
1047 - IBM01047
1140 - IBM01140
1141 - IBM01141
1142 - IBM01142
1143 - IBM01143
1144 - IBM01144
1145 - IBM01145
1146 - IBM01146
1147 - IBM01147
1148 - IBM01148
1149 - IBM01149
1200 - utf-16
1201 - unicodeFFFE
1250 - windows-1250
1251 - windows-1251
1252 - Windows-1252
1253 - windows-1253
1254 - windows-1254
1255 - windows-1255
1256 - windows-1256
1257 - windows-1257
1258 - windows-1258
1361 - Johab
10000 - macintosh
10001 - x-mac-japanese
10002 - x-mac-chinesetrad
10003 - x-mac-korean
10004 - x-mac-arabic
10005 - x-mac-hebrew
10006 - x-mac-greek
10007 - x-mac-cyrillic
10008 - x-mac-chinesesimp
10010 - x-mac-romanian
10017 - x-mac-ukrainian
10021 - x-mac-thai
10029 - x-mac-ce
10079 - x-mac-icelandic
10081 - x-mac-turkish
10082 - x-mac-croatian
12000 - utf-32
12001 - utf-32BE
20000 - x-Chinese-CNS
20001 - x-cp20001
20002 - x-Chinese-Eten
20003 - x-cp20003
20004 - x-cp20004
20005 - x-cp20005
20105 - x-IA5
20106 - x-IA5-German
20107 - x-IA5-Swedish
20108 - x-IA5-Norwegian
20127 - us-ascii
20261 - x-cp20261
20269 - x-cp20269
20273 - IBM273
20277 - IBM277
20278 - IBM278
20280 - IBM280
20284 - IBM284
20285 - IBM285
20290 - IBM290
20297 - IBM297
20420 - IBM420
20423 - IBM423
20424 - IBM424
20833 - x-EBCDIC-KoreanExtended
20838 - IBM-Thai
20866 - koi8-r
20871 - IBM871
20880 - IBM880
20905 - IBM905
20924 - IBM00924
20932 - EUC-JP
20936 - x-cp20936
20949 - x-cp20949
21025 - cp1025
21866 - koi8-u
28591 - iso-8859-1
28592 - iso-8859-2
28593 - iso-8859-3
28594 - iso-8859-4
28595 - iso-8859-5
28596 - iso-8859-6
28597 - iso-8859-7
28598 - iso-8859-8
28599 - iso-8859-9
28603 - iso-8859-13
28605 - iso-8859-15
29001 - x-Europa
38598 - iso-8859-8-i
50220 - iso-2022-jp
50221 - csISO2022JP
50222 - iso-2022-jp
50225 - iso-2022-kr
50227 - x-cp50227
51932 - euc-jp
51936 - EUC-CN
51949 - euc-kr
52936 - hz-gb-2312
54936 - GB18030
57002 - x-iscii-de
57003 - x-iscii-be
57004 - x-iscii-ta
57005 - x-iscii-te
57006 - x-iscii-as
57007 - x-iscii-or
57008 - x-iscii-ka
57009 - x-iscii-ma
57010 - x-iscii-gu
57011 - x-iscii-pa
65000 - utf-7
65001 - utf-8









Read More
    email this       edit

2019年5月12日 星期日

Published 星期日, 5月 12, 2019 by with 0 comment

FirebirdSQL 新增資料庫使用者與資料表權限



Firebird 資料庫有個最高權限的 SYSDBA 帳號,正常上線使用下最好將它的密碼進行變更,然後再新增一個使用者並賦予資料表的權限,這樣比較安全。

這些調整指令都透過使用 isql 介面來完成

※注意,使用 isql 調整資料庫相關設定前,先把 Firebird 服務/引擎 先關閉,以免發生占用衝突。

假設我們資料庫是在 D:\NEWDB\test.fdb

首先,變更 SYSDBA 的密碼
C:\FB3>isql -user SYSDBA D:\NEWDb\test.fdb↵
Database: D:\NEWDb\test.fdb, User: SYSDBA

SQL> ALTER USER SYSDBA password 'mustKey';↵
SQL> COMMIT;↵
SQL>

這樣就把密碼變更為 mustKey 了 (注意,Firebird 對帳號沒有大小寫分別,但是密碼有分別)


一、新增使用者

SQL> CREATE USER user01 password 'userPass01';↵

或是創建時同時賦予角色權限(例如管理者權限)

SQL> CREATE USER user01 password 'userPass01' GRANT ADMIN ROLE;↵

※參考 SQL user management commands
※參考 ROLE
※參考 The RDB$ADMIN role

二、對使用者賦予資料表權限
假設我有一個資料表 EMPL

SQL> GRANT SELECT , INSERT , UPDATE ON TABLE EMPL TO USER user01;↵

這代表我只賦予 user01 對 EMPL 只能操作 SELECT、INSERT、UPDATE 三種權限。

基本上,資料表操作權限只有 SELECT、INSERT、UPDATE、DELETE、REFERENCES 這四種,要 CREATE/DROP/ALTER 資料表只能由 SYSDBA 來做。

REFERENCES 這權限是用來讀取資料表的 關聯 資訊(foreign key)的。

如果我要給與全部的權限也可以用 ALL 取代那串長長的命令

SQL> GRANT ALL ON TABLE EMPL TO USER user01;↵

當這些都完成以後,Client APP 就只要使用新增的 user 權限來存取資料表就好了。

※參考 SQL Privileges


Read More
    email this       edit

2019年5月10日 星期五

Published 星期五, 5月 10, 2019 by with 0 comment

FirebirdSQL 以單次執行


這個是繼上篇文章之後,如果不希望這資料庫一直以服務狀態執行,而是希望 Client App 啟動時才執行的方法。

其實,就跟前一篇一樣的設定,就是不要做 install_service 的動作。

只是有幾個要先做的步驟還是少不了

一、要註冊 Firebird  到 Register 裡面
執行 instreg,這個動作在一部 電腦只需要做一次就好
D:\FB3\instreg install -z


二、在客戶端程式執行時 啟動 Firebird 引擎
呼叫方法大致分兩種

(1) 經典模式
D:\FB3\firebird -a

(2)超級伺服器模式
D:\FB3\firebird -a -m

※兩種模式參考說明書

其實在 Windows 下這兩種模式都差不多,選擇一種就可以,啟動後可以在工作列圖式看到這個圖案。

這個引擎會一直開著直到 Windows 關閉為止,或是手動 Shutdown 它。






Read More
    email this       edit

2019年5月9日 星期四

Published 星期四, 5月 09, 2019 by with 0 comment

FireBirdSQL 小型(崁入型)資料庫 初探、下載、建立、設定


常常會有些時候,開發一些工具程式會需要自帶資料庫的時候。
在10年前,那時候最好的一些開發工具選擇可能還是 Foxpro 吧。
但是自從微軟不再對 Foxpro 做更新時,就已經是末路之流了。

近年來,桌機上的商用主流依然是 Windows 無誤,開發工具從我孰悉的 powerbuilder 漸漸轉向了 C# 。

雖然 powerbuilder 沒辦法使用 Embedded Database ,然而業務開發又需要提供具有資料庫能力的『單機版』程式,雖然說是單機版,但實際使用上通常都會在 3 ~ 5人左右的程式。

因為公司是希望開發好的程式/資料庫可以打包一起,然後是由不是很懂 IT 的業務人員去客戶那邊安裝(最好能一鍵安裝或傻瓜安裝就搞定)的。

MS SQL express 是不錯的選擇,但是太過肥大,安裝又是囉哩叭縮,要包成自動安裝難度太高,如果又要網路共享需要比較懂得人才有辦法去設定啊,也不是一般業務人員可以搞定啊。

雖然,MySQL、 、MariaDB 也是不錯,但還要安裝 ODBC 來分享這也不好搞的。

SQL lite 太過單機,小型群組分享資料庫是做不到了,還得透過 API 指令。

Access DB、Excel table 等太過精簡,操作一個不小心未正常關檔,檔案就掛了,欲哭無淚。Excel table 資料庫沒有加密,等於裸奔狀態。

游網之餘,看到了這個 FireBirdSQL ,其前身是 Interbase 也算元老級的,但支援小群組共同使用,支援加密、StoreProcedure、可以當 Embedded DB 也可以當 C/S 資料庫,雖然效能不怎樣,但是這樣在小程式上真的綽綽有餘,所以趕快來研究看看吧。

FireBirde官方網站

一、下載主程式
本篇討論使用的是 Firebird 3.0.4 版 ,由於需要能夠被打包所以就下載這個 ZIP 壓縮檔版本


為什麼下載 32-bit 版本? 因為沒辦法保證客戶使用的 Windows 平台是 64-bit 的,所以保險起見就用 32-bit 來執行。

下載 ZIP 檔案回來後解壓縮後(通常會放在 C:\FB3 ,實際依需求去放),就可以開始設定了。

二、建立一個放資料檔案的路徑 (我的範例是 D:\NEWDB)

三、建立資料庫檔案
到 FB3 路徑下執行 isql
C:\FB3\isql↵
Use CONNECT or CREATE DATABASE to specify a database


然後創建資料庫,創件資料庫時會使用系統管理帳號SYSDBA,而它的密碼預設則是 masterkey,當正式上線前記得要變更密碼避免被人猜到。
SQL>CREATE DATABASE 'D:\NEWDB\test.fdb' page_size 8192↵
CON>user 'SYSDBA' password 'masterkey';↵

完成後,驗證一下資料庫是否可以查詢
SQL>SELECT * FROM RDB$RELATIONS;↵

此時畫面會跑出許多內容,這些內容並不是資料表,而是一個資料庫最基本的內容- Metadata。
確認沒問題以後,就可以離開 isql 指令模式
SQL>QUIT;↵

※參考 Creating a database using isql

此時,資料庫就會有一個預設帳號 SYSDBA,這個 SYSDBA 擁有非常大的權限,屆時如果要操作資料庫內的資料表,就比較建議另外再建立一個新的 user 帳號來使用比較保險。

這時候資料庫雖然已經建立好,但裡面是『空』的,沒有任何資料表,因此我們接下來就是先把資料庫掛起來,再來建立資料表。

四、設定資料庫
在 FB3 路徑下會有一個 Firebird.conf 的設定檔,裡面有個設定 WireCrypt 要先改一下
# Should connection over the wire be encrypted?
# Has 3 different values: Required, Enabled or Disabled. Enabled behavior
# depends on the other side's requirements. If both sides are set to Enabled,
# the connection is encrypted when possible. Note that Wirecrypt should be set
# to Enabled when running a Firebird server with legacy authentication.
#
# Attention: default depends upon connection type: incoming (server)
#            or outgoing (client).
#
# Per-connection configurable.
#
# Type: string (predefined values)
#
#WireCrypt = Enabled (for client) / Required (for server)
WireCrypt = Enabled

由於我門設這資料庫會使用 TCP/IP來連線或是提供其他電腦來連線,所以連線時會走加密資料因此必須設定 WireCrypt 開啟。

五、安裝資料庫服務
由於我們希望這個資料庫能夠開機後自動執行,因此必須把它掛載為服務器。

在 FB3 底下 有個 install_service.bat 的批次檔,請用 具有 windows 管理者身分的使用者來執行才能正常安裝為服務。
C:\FB3\install_service↵
instreg version WI-V3.0.4.33054 Firebird 3.0
Firebird has been successfully installed in the registry.
instsvc version WI-V3.0.4.33054 Firebird 3.0
Service "Firebird Server - DefaultInstance" successfully created.
Service "Firebird Server - DefaultInstance" successfully started.

上面指令執行後,會在 Windows 服務中多出一個服務 Firebird Server - DefaultInstance


它預設使用 DefaultInstance 當作實例名稱。

如果,你希望使用自己的實例名稱 ,例如 MyInstance,可以把名稱加在 install_service 後面
C:\FB3\install_service MyInstance↵

如果要移除服務,可以用 uninstall_service.bat 來做
C:\FB3\uninstall_service↵
Service "Firebird Server - DefaultInstance" successfully stopped.
instsvc version WI-V3.0.4.33054 Firebird 3.0
Service "Firebird Server - DefaultInstance" successfully deleted.
instreg version WI-V3.0.4.33054 Firebird 3.0
Firebird has been successfully deleted from the registry.

或帶實例名稱的移除
C:\FB3\install_service MyInstance↵

服務安裝完後會自動啟動該服務,你可以從 netstat -a 來查看通訊埠口是否開啟,Firebird  的監聽埠號預設為 3050。

如果希望變更埠號,可以先停用 Firebird 服務後,修改 Firebird.conf 的 RemoteServicePort
# ----------------------------
# TCP Protocol Settings
#
# The TCP Service name/Port number to be used for client database
# connections.
#
# It is only necessary to change one of the entries, not both.  The
# order of precendence is the 'RemoteServiceName' (if an entry is
# found in the 'services.' file) then the 'RemoteServicePort'.
#
# Per-connection configurable.
#
# Type: string, integer
#
#RemoteServiceName = gds_db
RemoteServicePort = 3051

重新啟用服務後,就會以新的設定執行。

六、 Windows 防火牆
如果你的 Windows 自帶的防火牆有開啟,可能會阻礙其他電腦跟 Firebird 的連線,所以要調整防火牆設定。

加入新的『輸入規則』

選擇 TCP/IP 協定

設定 TCP 連接埠 3050 ,這是Firebird SQL server 的預設埠口

然後允許這個埠口的連線

設定連線來源,依照使用範圍慎選!

設定名稱

設定好以後就可以在規則裡面看到了,此時就可以讓其他電腦連線進來


七、建立資料表
嘛.....我算是個懶人,不太想用 isql 指定建立,所以我用了 Dbeaver 這套第三方軟體來建立。
本篇不是講 Dbeaver 所以不會介紹太多它的用法。

Dbeaver 官方

開啟 Dbeaver,然後新增一個 Firebird 連線

輸入連線資訊,此時我用的帳號就是 test.fdb 資料庫的建立帳號 SYSDBA 來連線

如果沒有問題,就可以連線成功,此時就可以建立自己的資料表




八、關於封裝
當把 這個 Firebird 3 設定好之後就可以把整個 Firebird 資料夾壓縮起來,和自建的資料庫目錄一起打包到封裝軟體上。到客戶端解封裝時可以一並解開到指定路徑去,然後讓封裝程式到 Firebird 資料夾內去執行 install_service.bat 即可完成單機部屬。

若是要讓其他電腦連線,主要要檢查防火牆的設定並適當修改即可使用。


算是相當簡便的小型資料庫。



Read More
    email this       edit