背景:
周末看到了一篇原公司同事的文章,講的是關於新的互聯網形勢下的PACS系統。正好上一篇專欄文章也提到了有想搭建一個worklist服務器的沖動,所以就翻箱倒櫃將原本學生時代做課題時搭建的簡易Web PACS找了出來,借着再次搭建的機會學習一下Web PACS相關的技術,例如WADO標准、CGI或者FastCGI等技術。
WEB PACS技術淺談:
WEB PACS是一種利用互聯網技術,跨越了醫院和地域限制的,可隨意查詢和獲取DICOM對象的PACS系統。目前常見的方式有兩種:第一種是通過Web服務器提供查詢定位,將對應DICOM影像存儲服務器(通常為FTP)的地址與路徑返回,客戶端再向DICOM影像存儲服務器請求對象;第二種是通過Web服務器統一提供查詢及返回請求對象。兩者各有利弊,第一種通過分別部署Web服務器和FTP文件服務器,減輕了Web服務器的負擔,加快了響應時間,但是該方案也存在着諸多缺點,例如獲取圖像需要發送兩次請求,FTP服務器安全性維護代價高,要求在瀏覽器下載能夠解析DICOM圖像的插件等等;第二種Web服務器整合了查詢與返回,去除了FTP服務器,部署方便,它的缺點是由於Web服務器返回的數據是真實DCM轉換過來的BMP或JPEG文件,因此某些依賴於DICOM文件其他信息的操作(例如窗寬窗位調整)需要重新請求服務端,要求更新數據,因此對服務器的處理能力消耗較大,對帶寬也有一定要求。
兩種方式的示意圖如下:
(摘自文獻《DICOM WADO原理及應用研究》)
1)WADO
WADO(Web Access to DICOM Persistent Object),是DICOM標准中的一部分,提供了一種通過HTTP或HTTPS協議並利用DICOM的標識符從HTML頁或XML文檔中存取與重現DICOM對象的機制,用於解決在互聯網環境下訪問DICOM對象——這也可以認為是Web PACS的終極目標。與標准的基於HTTP或HTTPS的網絡訪問方式相同,用戶在瀏覽器地址欄中輸入URL,向服務器發送WEB請求,服務器接收到請求后根據URL中提供的參數,在服務端定位要求的圖像或報告回送給瀏覽器,示意圖如下(摘自DICOM標准第18章),
其實WADO標准就是定義了客戶端和服務端之間交互的規則,可以簡單的理解為雙向交互時的參數約定,即服務端可以根據瀏覽器端發送參數的不同來實現常見的單機版PACS的C-FIND、C-MOVE、C-STORE等功能。DICOM標准中關於WADO部分的介紹也主要是各種參數規則的說明,以及部分URL實例,這里截取一個來簡單的說明一下:
如上圖所示,URL采用常見的GET方式,將傳統的PACS系統客戶端發送查詢時的參數發送給服務端,例如studyUID、seriesUID和objectUID(其實就是DICOM圖像中的SOP Instance UID)。
2)CGI
CGI(Common Gateway Interface),是WWW技術中最重要的技術之一,有着不可取代的重要地位。CGI定義了外部應用程序(CGI程序)與Web服務器之間的接口標准,獨立於開發語言,給用戶提供了一種從網頁瀏覽器向執行在服務器上的程序請求數據的方式——為Web PACS的實現提供了一種途徑。
為了理解CGI的含義,必須要搞清楚WEB開發中常見的前端和后端。前端就是Web應用中用戶可以看得見碰的着的東西,服務端接收到請求后大多直接將數據傳輸到瀏覽器;后端更多的是用戶看不到的(這里指的看不到不是操作后的結果看不到,而是操作的流程看不到),接收到請求后需要服務端進一步操作的,例如查詢數據庫、算法運算等等。而CGI就是實現這種由瀏覽器的輸入觸發在WEB服務器上運行的程序的標准。
實際環境搭建:
正如博文第一部分所述,由於第一類Web PACS需要瀏覽器安裝第三方插件,需要單獨部署FTP服務器,因此在課題起初沒有采用該方案。第二種Web PACS服務端在接受請求后會再向影像服務器發送請求,這正是上文中提到的CGI技術的一種很好的應用場景。下面就具體介紹一下如何搭建CGI應用環境:
1)WampServer+FastCGI
Web服務器搭建:
WampServer安裝包下載http://www.wampserver.com/en/#wampserver-64-bits-php-5-5
安裝過程中有可能會遇到“缺少msvcr110.dll,程序無法啟動”錯誤,可以參照http://jingyan.baidu.com/article/ed2a5d1f3303d709f7be1776.html中給出的方法解決,需要提醒的是下載的Visual C++ Redistributable for VisualStudio 2012 Update 4版本不是由電腦的操作系統類型(32位or64位)來決定,而是應該根據WampServer安裝包的類型來選擇。安裝完Visual C++ Redistributable for VisualStudio 2012 Update 4后需要重新安裝WampServer。
配置FastCGI環境:
參考http://www.admin10000.com/Document/53.html和http://my.oschina.net/Twitter/blog/210044對Apache服務器進行配置。在配置完成后重啟WampServer竟然失敗,出現如下錯誤:
更悲劇的是查看Apache的ErrorLog竟然沒有提示,所以只能對修改的httpd.conf配置文件逐行排查,通過逐行注釋的本方法最后找到了問題所在,由於修改DirectoryIndex引發的錯誤,恢復到原本的順序后,重啟WampServer竟然奇跡般的成功了,小有成就感啊,至於具體的原因后續在慢慢查找,確定了再補充上來。
2)C語言CGI實例
配置完開發環境后,給出一個簡單的測試,由於電腦中沒有安裝PHP,所以這里就討巧一下,直接利用Apache自帶的cgi來調用一下C語言開發的程序,關於C語言CGI的配置比較簡單,在Apache目錄下的modules中已經包含了cgi模塊,只需要在httpd.conf配置文件中指定c-cgi運行的目錄即可,添加如下代碼:
ScriptAlias /cgi-bin/"C:/wamp/www/c-cgi/" AddHandler cgi-script .exe .pl .cgi <Directory"C:/wamp/www/c-cgi/"> Options Indexes FollowSymLinks ExecCGI AllowOverride all Order allow,deny Allow from all Require local </Directory>具體的配置可參考 http://blog.sina.com.cn/s/blog_66ec4d660100rd2h.html,實例的話就不要用該博文中的了,用我下面給出的完整示例。
GET方法實例源碼
#include <stdio.h> #include <stdlib.h> int main(void) { char *data; char a[10],b[10]; printf("Content-Type:text/html\n\n"); printf("<HTML>\n"); printf("<HEAD>\n<TITLE >Get Method</TITLE>\n</HEAD>\n"); printf("<BODY>\n"); printf("<div style=\"font-size:12px\">\n"); data = getenv("QUERY_STRING"); if(data==NULL) return 1; if(sscanf(data,"a=%[^&]&b=%s",a,b)!=2){ printf("<DIV STYLE=\"COLOR:RED\">Error parameters should be entered!</DIV>\n"); } else{ printf("<DIV STYLE=\"COLOR:GREEN; font-size:15px;font-weight:bold\">a + b = %d</DIV>\n",atoi(a)+atoi(b)); } printf("<HR COLOR=\"blue\" align=\"left\" width=\"100\">"); printf("<input type=\"button\" value=\"Back CGI\" onclick=\"javascript:window.location='../cgitest-c.html'\">"); printf("</div>\n"); printf("</BODY>\n"); printf("</HTML>\n"); return 0; }利用VS編譯后的可執行文件為gettest.exe,放到/www/cgitest-c目錄下。
POST方法實例源碼
#include <stdio.h> #include <stdlib.h> int main(void){ int len; char *lenstr,poststr[20]; char m[10],n[10]; printf("Content-Type:text/html\n\n"); printf("<HTML>\n"); printf("<HEAD>\n<TITLE >post Method</TITLE>\n</HEAD>\n"); printf("<BODY>\n"); printf("<div style=\"font-size:12px\">\n"); lenstr=getenv("CONTENT_LENGTH"); if(lenstr == NULL) printf("<DIV STYLE=\"COLOR:RED\">Error parameters should be entered!</DIV>\n"); else{ len=atoi(lenstr); fgets(poststr,len+1,stdin); if(sscanf(poststr,"m=%[^&]&n=%s",m,n)!=2){ printf("<DIV STYLE=\"COLOR:RED\">Error: Parameters are not right!</DIV>\n"); } else{ printf("<DIV STYLE=\"COLOR:GREEN; font-size:15px;font-weight:bold\">m * n = %d</DIV>\n",atoi(m)*atoi(n)); } } printf("<HR COLOR=\"blue\" align=\"left\" width=\"100\">"); printf("<input type=\"button\" value=\"Back CGI\" onclick=\"javascript:window.location='../cgitest-c.html'\">"); printf("</div>\n"); printf("</BODY>\n"); printf("</HTML>\n"); fflush(stdout); return 0; }利用VS編譯后的可執行文件為posttest.exe,同樣放到/www/cgitest-c目錄下。
測試網頁源碼
<html> <head> <title>CGI Testing</title> </head> <body> <table width="200" height="180" border="0" style="font-size:12px"> <tr><td> <div style="font-weight:bold; font-size:15px">Method: GET</div> <div>please input two number:<div> <form method="get" action="./c-cgi/gettest.exe"> <input type="txt" size="3" name="a">+ <input type="txt" size="3" name="b">= <input type="submit" value="sum"> </form> </td></tr> <tr><td> <div style="font-weight:bold; font-size:15px">Method: POST</div> <div>please input two number:<div> <form method="post" action="./c-cgi/posttest.exe"> <input type="txt" size="3" name="m">* <input type="txt" size="3" name="n">= <input type="submit" value="resu"> </form> </td></tr> <tr><td><input type="button" value="Back Home" onclick='javascript:window.location="./index.php"'></td></tr> </table> </body> </html>放到Apache服務器根目錄下,在瀏覽器中通過localhost/cgitest-c.html可以訪問到。
實際運行結果
上述通過一個簡單的計算來演示了CGI技術的實現和開發流程,當然對於簡單的數字計算WEB前端自己就搞定了,此處只是為了說明CGI流程。至此整個Web PACS的Web服務端就已經搭建的差不多了,利用CGI或FastCGI我們可以使用服務端的其他語言開發的程序來實現我們想要的功能,那么后續的工作就跟開發C/S模式的PACS一樣了,用C/C++、C#或JAVA等高級語言開發PACS服務相關的程序供Web服務器調用即可。本博文中演示的是調用exe可執行文件,這種方式有風險,后續我會介紹在FastCGI模式下的利用PHP調用C++或C#或JAVA動態庫的更安全的實現方式。