編輯 | 究查 | 歷程 | 原始

IO 路徑與檔案操作分析

本報告詳列了 CB_IMGPSScanImp.pas.bk 檔案中定義的所有 IO 相關變數,以及執行目錄與檔案管理的關鍵方法,並定義了抽像化所需的介面原型。

壹、 關鍵路徑與檔案變數 (Variables)

程式中定義了多個全域或私有欄位,用來追蹤檔案在硬碟上的實體位置。

1. 核心路徑變數與動態組合邏輯

變數名稱 類型 組合邏輯 (Combination Logic) 用途說明
ImagePath String WORK_INF 的 LOCAL_PATH 參數值 本機基礎根路徑。所有暫存資料的起點。
ScaniniPath String ImagePath + FWork_No + '' + FUserUnit + '' 設定檔存放區。區分作業別與單位。
ImageSavePath String 同 ImagePath (初期) 或隨 FMode 變動 影像儲存基準路徑
ScanPath String ImageSavePath + ScanCaseno + '' + ScanDocdir + '' 當前掃描實體目錄。指向最末層的影像資料夾。
TransPath String ImageSavePath + CaseID + '\Upload' 準備上傳區。在傳送前將檔案結構扁平化組合於此。
DisplayPath String ImageSavePath + NowCaseNo + '' 預覽基準目錄。用於 UI 點選樹狀圖時讀取特定案件資料。
LngPath String GetLocalAppDir(Handle) + 'MPS\CB_IMGPS' 應用程式資源路徑。存放在使用者設定檔目錄以避開權限問題。
CheckXmlPath String ImagePath + 'OMRSITE' OMR 規則暫存區。存放從伺服器同步下來的 XML 定義。
SamplePath String ImagePath + 'Sample' + FWork_No + '' 範本影像存放區。按作業別區分。
SitePath String ImagePath + 'Site' 登打定位存放區
FFtpRootPath String 透過伺服器回傳值動態設定 FTP 伺服器根路徑。指定在遠端 FTP 伺育器上存放影像檔的起始位址。

2. 檔案名稱變數 (Filenames)

變數名稱 類型 用途說明
ScanSaveFilename String 下一張掃描影像預定的檔名(通常包含 FormID)。
PEFileName String 在 PageEnd 階段確定的最終完整檔案路徑。
AttName String 附件目錄名稱。根據 FIs_In_Wh 決定為 Attach 或 S_Attach。
Ext String 副檔名。預設為 .tif,彩色/灰階模式下可能切換為 .jpg。

貳、 關鍵 IO 操作方法 (Methods)

以下為需要進行抽像化設計的核心 IO 方法。

1. 目錄管理 (Directory Management)

方法名稱 作用 應用場景 建議抽象 IO 介面
_DelTree | 遞迴刪除整個目錄樹 | 上傳成功後清空暫存、模式切換初始化目錄 | RemoveDirRecursive(Path) |
| Str2Dir / ForceDirectories | 確保路徑以 \ 結尾並建立路徑 | 初始化或掃描前建立資料夾 | EnsureDirExists(Path) |
| GetNoNameCase | 搜尋尚未配號的臨時目錄 | 在本機搜尋如 未配號0001 到 9999 的可用路徑 | FindNextAvailablePath(Pattern) |
| GetLocalAppDir | 取得 Windows %LocalAppData% 路徑 | 確保在現代 Windows 系統中有權限寫入語言檔與 Log | GetBaseStoragePath() |

2. 存在性檢查與搜尋 (Existence & Search)

方法名稱 作用 應用場景 建議抽象 IO 介面
FileExists 檢查指定路徑檔案是否存在 在載入 .dat、.ini 或影像前進行安全檢查 IsFilePresent(Path)
DirectoryExists 檢查指定路徑資料夾是否存在 在建立掃描目錄或上傳前確認結構完整性 IsDirPresent(Path)
FindFirst / FindNext 搜尋符合特定格式的檔案 取得實體檔案大小 (FileRec.Size) 或遞迴搜尋檔案 ListFiles(Path, Pattern) / GetFileInfo(Path)

3. 檔案生命週期與刪除 (File Lifecycle & Deletion)

方法名稱 作用 應用場景 建議抽象 IO 介面
ReSortFileName 影像重新編序命名 刪除或插入影像後,修正 001_xxx 等序號連續性 RenameFile(Old, New)
DeleteImageFile 刪除實體影像檔 單張影像刪除並觸發 .dat 內容同步更新 DeleteFile(Path)
DeleteDocNoFile 刪除特定文件代號下所有影像 整批文件(DocNo)的物理刪除與序號重整 DeleteFilesByPattern(Pattern)
DeleteShowFile 刪除 UI 顯示中的影像清單 根據介面選取狀態進行批次物理刪除 DeleteFileList(List)
RenameFile / MoveFile 檔案搬移或更名 用於「分案」、「移動頁數」或「變更歸類」 MoveFile(Src, Dest)
CopyFile 複製實體檔案 影像搬移至上傳區或引用舊案件影像 CopyFile(Src, Dest)

4. 資料紀錄與序列化 (Metadata IO)

方法名稱 作用 應用場景 建議抽象 IO 介面
DeleteCustomDocDir 移除自訂文件定義 從 CustomDocNo.ini 中移除特定自訂文件的節點 Storage.IniDeleteSection(Section)

5. 網路傳輸 IO 方法 (Network Retrieval - dnFile_Get)

專案中使用 dnFile_Get 從伺服器端同步必要的環境檔案或規則定義。

抓取對象 (目標路徑) 遠端請求 Action 用途說明
CheckXmlPath + filename GetCheckXml 下載 OMR 檢核定義檔。同步伺服器端最新的 XML 座標規則,用於自動判斷影像區域。 https
SamplePath + filename GetSampleImg 下載範本影像。用於在 UI 介面上提供給操作員對照的標準範例圖檔。 https
SitePath + filename GetSiteImg 下載定位定義圖。下載與登打、校對位置相關的輔助影像。 https
LngPath + 'Language.ini' GetLanguage 同步多國語言檔。確保用戶端介面文字(繁體、英文、越南語等)與伺服器同步。 https

參、 資料紀錄與序列化 (Metadata IO)

專案中使用了大量的 .dat 與 .ini 檔案來記錄目錄內的結構資訊與使用者偏好。

1. .dat 檔案 (結構紀錄)

檔案名稱 組合路徑邏輯 (Path Logic) 內容與用途
Context.dat ImageSavePath + CaseID + '' + DocDir + '\Context.dat' 影像清單。紀錄該資料夾內所有影像檔名的正確順序。
DocDir.dat ImageSavePath + CaseID + '\Upload\DocDir.dat' 目錄對應表。紀錄 Upload 下每個檔名所屬的原始 DocDir
CaseDocNo.dat ImageSavePath + CaseID + '\CaseDocNo.dat' 文件目錄索引。紀錄案件內已建立的實體資料夾名稱。
CaseDocNo_Copies.dat ImageSavePath + CaseID + '\CaseDocNo_Copies.dat' 份數紀錄。對應 CaseDocNo.dat 中的資料夾分別有多少份。
CaseIndex.dat ImageSavePath + CaseID + '\CaseIndex.dat' 案件屬性。如是否為「授信卷」(Case_loandoc)。
EditedDocDir.dat ImageSavePath + CaseID + '\EditedDocDir.dat' 異動清單。紀錄當次操作中被修改過的文件目錄。
Scan_Memo.dat DisplayPath + 'Scan_Memo.dat' 使用者註記。紀錄操作員手動輸入的備註內容。
CaseList.dat ImageSavePath + 'CaseList.dat' 案件總表。紀錄目前本機快取中存在的所有 CaseID。

2. .ini 檔案 (設定與偏好)

檔案名稱 路徑邏輯 回寫 (Write-back) 內容與用途 寫入關聯
Scan.ini ScaniniPath + 'Scan.ini' 是 (頻繁) 掃描器硬體設定。儲存最後一次選取的 DPI、雙面、色彩模式、亮度、對比等參數。
CustomDocNo.ini ScaniniPath + 'CustomDocNo.ini' 是 (中等) 自訂文件代號。儲存操作員定義的文件分類名稱與內部 ID 映射。 DeleteCustomDocDir
CB_IMGPSScan.ini LngPath + 'CB_IMGPSScan.ini' 是 (低頻) 環境配置。儲存伺服器 IP、Port、自動更新版本資訊等全域參數。
Language.ini LngPath + 'Language.ini' 否 (唯讀同步) 多國語言包。僅透過 dnFile_Get 從伺服器抓取更新,OCX 不自行修改。

肆、 串流與讀寫操作 (Stream & File I/O)

源碼中頻繁使用 .LoadFromFile 與 .SaveToFile 來實現資料的持久化。

1. 使用 TStringList 讀寫 .dat 結構

操作對象 讀取場景 (Load) 寫入場景 (Save) 建議抽象 IO 介面
結構清單 (.dat) LoadImgFile 載入以重建樹狀結構 PageEnd 掃描完成或結構增刪後即時存檔 ReadTextFile(Path) / WriteTextFile(Path, Content)
備註文件 (Memo) 進入備註編輯器時載入舊有內容 WNoteBtnClick 完成編輯後儲存內容 ReadTextFile(Path) / WriteTextFile(Path, Content)
上傳包定義 N/A CreateFormID_FormName 產生上傳包所需的定義檔 WriteTextFile(Path, Content)

2. 使用 TImageScrollBox / TDibGraphic 讀寫影像

操作對象 讀取場景 (Load) 寫入場景 (Save) 建議抽象 IO 介面
掃描影像原始檔 view_image_... 載入顯示於預覽窗 OnAcquire 將記憶體影像 (Dib) 持久化至磁碟 ReadBinaryFile(Path) / WriteBinaryFile(Path, Blob)
影像處理覆蓋 ImageReSize_FormID 讀取進行定位縮放 處理完成後覆蓋原始影像檔 ReadBinaryFile(Path) / WriteBinaryFile(Path, Blob)
旋轉/後製處理 點擊旋轉按鈕後從磁碟重新讀取 旋轉完成後保存更新內容回實體檔案 ReadBinaryFile(Path) / WriteBinaryFile(Path, Blob)

伍、 檔案路徑構造邏輯示例

程式中典型的路徑構造方式如下(以 PageEnd 為例):

// 1. 基礎路徑

ScanPath := ImageSavePath + ScanCaseno + '';

// 2. 進入特定文件目錄 (DocDir)

ScanPath := ScanPath + ScanDocdir + '';

// 3. 確保實體目錄存在

Str2Dir(ScanPath);

// 4. 構造最終影像路徑

PEFileName := ScanPath + Add_Zoo(PageCount, 3) + '_' + FormID + ext;

陸、 IO 方法具體實作邏輯細部說明 (Detailed Implementation Logic)

本節針對 CB_IMGPSScanImp.pas.bk 內部如何透過 Windows API 實作這些 IO 操作進行細部解說。

1. Str2Dir(Path: string) 的運作機制

這個方法的主要作用是 「路徑標準化與實體資料夾建立」

  • 字串處理:它會先檢查傳入的 Path 字串最後一個字元是否為 \。如果不是,會自動補上。
  • 建立資料夾:**是的,它會在內部建立資料夾**。它會呼叫 Delphi 的 ForceDirectories(Path) 函式。
  • ForceDirectories 的特性:與一般的 CreateDir 不同,ForceDirectories 會「遞迴」建立路徑中所有不存在的父目錄(例如若路徑為 C:\A\B\C,即使 A 和 B 都不存在,它也會一併建立)。

**2. _DelTree(DirName: string) 的遞迴邏輯**

由於 Delphi 早期版本沒有提供單一函式來刪除包含檔案的資料夾,此方法透過 Windows 的搜尋機制實現:

  • 遍歷搜尋:使用 FindFirst 和 FindNext 找出資料夾內的所有物件。
  • 檔案處理:若搜尋到的是一般檔案,直接呼叫 DeleteFile。
  • 遞迴處理:若搜尋到的是子資料夾,則「自己呼叫自己」 (_DelTree) 先清空該子資料夾。
  • 物理移除:當資料夾內的所有內容都清空後,呼叫 RemoveDir 將空的資料夾從磁碟移除。

3. FindFirst 與 FindNext 的搜尋與疊代邏輯

這兩個方法是 Delphi 處理「集合式 IO 操作」(如掃描目錄、批次刪除)的基礎,通常搭配 TSearchRec 結構使用。

  • FindFirst(Path, Attr, SearchRec)
  • 動作:啟動搜尋任務,將找到的第一個檔案資訊填入 SearchRec。
  • 應用例:在 PM104Click (匯入影像) 時,程式會先執行 FindFirst(FName, faAnyfile, FileRec) 來確認使用者選取的實體檔案是否存在,並藉由 FileRec.Size 取得位元組大小,用以判斷是否超過系統設定的匯入限制(例如 5MB)。
  • FindNext(SearchRec)
  • 動作:繼續尋找下一個符合條件的檔案。
  • 疊代模式:通常包裹在 repeat ... until FindNext(SR) <> 0 迴圈中。
  • 應用例:在 _DelTree 實作中,程式利用此疊代邏輯將資料夾內「所有的」子目錄與檔案清空,直到 FindNext 回傳非零值(代表搜尋結束)。
  • FindClose(SearchRec)
  • 重要性:釋放搜尋控制代碼(Handle)。若未呼叫,頻繁搜尋會導致系統資源洩漏。

4. GetNoNameCase(Path: string) 的搜尋邏輯

這是一個典型的 「可用名稱查表」 實作:

  • 迴圈範圍:從 1 迭代到 9999。
  • 格式組合:使用 Add_Zoo 函式將數字轉為四位數(如 0001),並加上前綴字「未配號」。
  • 磁碟檢查:呼叫 DirectoryExists 檢查 Path + '未配號000x'。
  • 結果回傳:第一個回傳 False(代表該資料夾尚不存在)的名稱即為結果,程式會立即跳出迴圈並選定此名稱。

5. ReSortFileName(Path: string) 的連續性維護

為了保證 UI 上顯示的頁碼順序與磁碟檔名一致,此方法採用「先暫存、後改名」的策略:

  • 防止衝突:先將目錄內所有符合格式的影像更名為帶有臨時前綴(如 @)的檔名。
  • 按序更名:讀取 Context.dat 內的清單,依照目前的順序,將檔案逐一更名為 001_..., 002_... 等格式。
  • 同步索引:最後將更名後的結果重新寫入 Context.dat。

6. GetLocalAppDir(Handle: HWND) 的權限繞過

這是一個系統級別的 IO 位置定位方法:

  • API 呼叫:呼叫 SHGetSpecialFolderPath (Windows Shell API)。
  • 定位 CSIDL_LOCAL_APPDATA:指向 C:\Users\使用者名稱\AppData\Local\。
  • 必要性:這能解決 OCX 程式被安裝在 Program Files 下時,因為 UAC 權限無法在安裝目錄寫入 Log 或語言檔的問題。

🎯 移植至 Web 平台的建議

當您將這些邏輯移植到 Web 時:

  • Str2Dir 應對應到 IndexedDB 的 Object Store 建立或 File System Access API 的 getDirectoryHandle(搭配 create: true)。
  • FindFirst / FindNext 在 Web 端應對應到 Directory Handle 的 entries() 異步疊代器。
  • .dat 序列化文件在 Web 端建議直接轉為 JSON 格式存儲於瀏覽器的 localStorage 或 IndexedDB 中。

柒、 設定檔回寫邏輯 (Write-back Logic)

本節說明專案中哪些 .ini 檔案會由 OCX 主動更新資料及其觸發時機。

檔案名稱 回寫內容 (Content) 觸發時機 (Trigger)
Scan.ini DPI、Duplex、亮度、對比、影像模式 掃描前設定變更或啟動掃描時(SaveScanPara)。
CustomDocNo.ini 自訂文件名稱與 ID 映射 操作員新增自訂分類或呼叫 DeleteCustomDocDir 時。
CB_IMGPSScan.ini 版本號、伺服器 IP/Port 自動更新完成後更新版號,或手動修改伺服器設定時。

捌、 注意事項

  • 硬編碼路徑:ActiveFormCreate 中存在寫死的 C:\Temp\IMGPSScan,適合作為最終保險。
  • 權限問題:作為 ActiveX,這些 IO 操作強烈依賴瀏覽器的「安全站台設定」或 IE 的「保護模式」。使用 GetLocalAppDir 是現代化重構中解決 UAC 權限問題的重要手段。