請先閱讀前一篇的相關文章:
關於macOS 10.12、10.13、10.14從PHP呼叫sips指令處理RAW轉JPEG會產生全黑圖檔、破碎圖檔、錯誤訊息的問題
insoler網站的「RAW轉JPEG」功能,是使用macOS系統內建的sips指令來完成的。sips可以在終端機底下使用,也可以從PHP以執行外部指令的方式來執行。最簡單的PHP的「RAW轉JPEG」的sips.php程式碼就像這樣:
<? php
$cmd = "/usr/bin/sips -s format jpeg --setProperty formatOptions 40% 'IMG_1000.CR2' --out 'IMG_1000.jpg'";
shell_exec($cmd);
?>
但是這個超簡單的「RAW轉JPEG」的PHP程式碼只能在OS X El Capitan 10.11.6上正常執行,無法在macOS 10.12、10.13、10.14上執行。相同的程式碼會產生全黑圖檔、破碎圖檔,也可能不會產生任何圖檔。
如果想要知道sips.php執行sips指令來處理「RAW轉JPEG」檔案需要花費多少時間?可以在指令碼的前後加上讀取時間並計算時間差的程式碼,就像這樣:
<? php $time_start = microtime(true); $cmd = "/usr/bin/sips -s format jpeg --setProperty formatOptions 40% 'IMG_1000.CR2' --out 'IMG_1000.jpg'"; shell_exec($cmd); $time_end = microtime(true); $time = $time_end - $time_start; echo '轉換RAW檔案的時間:' . $time . '< br / >'; ?>
這個sips.php的執行結果是:
轉換RAW檔案的時間:2.1008329391479
並不是任何RAW照片檔案進行「RAW轉JPEG」都要2.1秒左右,如果是1000萬畫素左右的RAW檔案還會更快,但如果是4000萬畫素以上的數位相機的RAW檔案就會需要花費更多時間。這裡使用的是Canon EOS 6D,RAW檔案約2020萬畫素。
經過前面一篇文章的測試,我發現這個「RAW轉JPEG」的PHP程式碼在macOS 10.12、10.13、10.14的系統上由於macOS系統內建的PHP與sips有相容性的問題或是檔案權限的問題。只要另外自行安裝PHP,就可以正常執行這個程式碼。
但是,由於macOS Mojave 10.14的Server只剩下Xsan的網路磁碟機功能,因此網站伺服器主機最多只能升級到macOS High Sierra 10.13.6。請參考這篇文章:
macOS Mojave Server 10.14已不再是伺服器,insoler只能升級到macOS High Sierra 10.13.6
也許macOS High Sierra 10.13.6就算再過5年、10年都不需要升級macOS版本,只要升級PHP版本就可以繼續擔任網站伺服器主機的任務,但是如果是使用上面的PHP程式碼,在網站伺服器主機上直接處理「RAW轉JPEG」的工作,也許再過3年,就會面臨老舊的macOS 10.13內建的RAW功能,不支援幾年以後才發表的全新數位相機!
事實上,現在insoler網站還在使用的OS X El Capitan 10.11.6,就已經不支援2016年9月8日發表的Canon EOS 5D Mark IV等新的數位相機!
如果考慮到BOONEX的海豚系統正式支援PHP 7以後,insoler網站雖然可以升級到macOS 10.13,但已經無法升級到macOS 10.14以上的版本,因此就算升級到macOS 10.13,再過2-3年,很可能還是會遇到與macOS 10.11.6一樣的問題,老舊的macOS系統不支援新的數位相機。
因此,考量到insoelr網站就算再過5年、10年、20年,就算網站的Web Server不升級,仍然停留在macOS 10.13還是可以支援10年後、20年後的最新的數位相機,考量到「未來性」就必須改變在Web Server上直接處理「RAW轉JPEG」的做法才行。
雖然macOS 10.12、10.13、10.14都只能在終端機執行sips指令,macOS系統內建的PHP無法透過shell_exec來正常執行sips,或許最簡單的辦法是透過shell_exec來呼叫終端機,再利用終端機的指令來執行sips指令。
想到的辦法,就是執行osascript指令來開啟終端機。修改的新測試程式碼就像這樣:
<? php $photoPath = '/Library/Server/Web/Data/Sites/Default/sips/'; function shell_sips($photoRAW, $photoJPG) { $cmd = "osascript -e 'tell application ". '"Terminal"' . ' to do script "/usr/bin/sips -s format jpeg --setProperty formatOptions low ' . "'" . $photoRAW . "' --out '" . $photoJPG . "'; exit" . '"'. "'"; shell_exec($cmd); } shell_sips($photoPath . 'IMG_1000.CR2', $photoPath . 'IMG_1000.jpg');
?>
雖然可以正常打開終端機,再透過to do script指令,告訴終端機要執行sips指令來轉RAW檔案,但卻會遇到兩個新的問題:
第一個問題,可以在osascript指令後面追加exit指令,這樣就可以關閉終端機視窗畫面,而不會讓終端機視窗保持開啟中。
第二個問題就比較棘手。因為osascript只負責傳送那些指令,而終端機並不需要等待sips指令執行完畢以後,就可以直接關閉終端機。所以PHP程式碼會無法得知sips轉換RAW檔案是否成功?是否正常轉換JPEG檔案?
我能想到的最簡單的辦法,就是寫一個計時迴圈,每秒檢查一次是否產生JPEG檔案?如果經過30秒都找不到JPEG檔案,很可能是上傳了無法轉換JPEG的RAW檔案,甚至是一個偽造的RAW檔案。
加上計時迴圈的程式碼就像這樣:
<? php $photoPath = '/Library/Server/Web/Data/Sites/Default/sips/'; function shell_sips($photoRAW, $photoJPG) { $cmd = "osascript -e 'tell application ". '"Terminal"' . ' to do script "/usr/bin/sips -s format jpeg --setProperty formatOptions low '
. "'" . $photoRAW . "' --out '" . $photoJPG . "'; exit" . '"'. "'"; shell_exec($cmd); } function RAWtoJPEG($photoRAW, $photoJPG) { shell_sips($photoRAW, $photoJPG); $MyCount = 1; do { sleep(1); if (file_exists($photoJPG)) { break; } $MyCount++; } while ($MyCount < 30); if ($MyCount >= 30) { echo $MyCount . '< br / >'; echo "無法把RAW檔案轉成JPEG檔案:" . $photoJPG . "!不支援的RAW格式?磁碟機沒有可用容量?" . '< br / >'; } } RAWtoJPEG($photoPath . 'IMG_1000.CR2', $photoPath . 'IMG_1000.JPG');
?>
加上計時迴圈以後,看起來似乎是沒有什麼太大問題的程式碼,但是進行多次測試以後,會發現有時候會無法產生JPEG檔案!由於這個簡單的PHP程式碼並不處理檔案上傳功能,所以必須事先把IMG_1000.CR2檔案放在指定的$photoPath檔案路徑裡面。
有時候會無法順利轉換成IMG_1000.JPG的理由,絕對不是硬碟空間不足、不支援的RAW格式,更不可能是假的RAW檔案!
從Google搜尋各種資料以後,研判可能是osascript指令並不會管是否能正常開啟終端機,如果伺服器主機忙碌,一時無法立即開啟終端機,osascript指令會立即結束,當然就導致沒有開啟終端機的視窗畫面,更沒有執行過sips指令。
所以只好再加上一個 -e 'with timeout of 600 seconds' 指令,告訴osascript指令至少要等600秒,也就是至少要等待10分鐘,如果10分鐘以內都無法順利開啟終端機才會遇到「逾時」的問題。
<? php $photoPath = '/Library/Server/Web/Data/Sites/Default/sips/'; function shell_sips($photoRAW, $photoJPG) { $cmd = "osascript -e 'with timeout of 600 seconds' -e 'tell application ". '"Terminal"'
. ' to do script "/usr/bin/sips -s format jpeg --setProperty formatOptions 40% '
. "'" . $photoRAW . "' --out '" . $photoJPG . "'; exit" . '"'. "'" . " -e 'end timeout'"; shell_exec($cmd); } function RAWtoJPEG($photoRAW, $photoJPG) { shell_sips($photoRAW, $photoJPG); $MyCount = 1; do { sleep(1); if (file_exists($photoJPG)) { break; } $MyCount++; } while ($MyCount < 30); if ($MyCount >= 30) { echo $MyCount . '< br / >'; echo "無法把RAW檔案轉成JPEG檔案:" . $photoJPG . "!不支援的RAW格式?磁碟機沒有可用容量?" . '< br / >'; } } RAWtoJPEG($photoPath . 'IMG_1000.CR2', $photoPath . 'IMG_1000.JPG');
如果這樣就可以解決問題的話,當然就太好了!但是實際上並沒有這麼美好!我以為這樣就可以解決問題,因此修改insoler上傳RAW檔案的「非同步處理RAW轉JPEG」的PHP程式碼,就像這樣:
function shell_sips($photoRAW, $photoJPG) { //設定600秒(10分鐘)逾時是為了避免產生這個錯誤:execution error: Terminal got an error: AppleEvent timed out. (-1712) $cmd = "osascript -e 'with timeout of 600 seconds' -e 'tell application ". '"Terminal"'
. ' to do script "/usr/bin/sips -s format jpeg --setProperty formatOptions 40% ' . "'" . $photoRAW
. "' --out '" . $photoJPG . "'; exit" . '"'. "'" . " -e 'end timeout'"; shell_exec($cmd); return $cmd; } function RAWtoJPEG($raw, $size, $sFileName, $oFileName) { global $iMID; $cmd = shell_sips($sFileName, $oFileName); $MyCount = 1; do { sleep(1); if (file_exists($oFileName)) { break; } $MyCount++; } while ($MyCount < 60); if ($MyCount >= 60) { echofile("無法把RAW檔案轉成JPEG檔案:" . $oFileName . "!不支援的RAW格式?磁碟機沒有可用容量?"); echofile("發生錯誤的指令:" . $cmd); @unlink($sFileName); //刪除不支援的RAW檔 deletePhoto($iMID); //刪除上傳照片時新增的資料 return false; } return true; }
實際正式上傳檔案,還是會發生錯誤!會在echofile副程式的記錄檔案看到發生的錯誤指令!雖然指令碼本身是正確的,但是經過多次的重複測試,有時候會遇到無法順利RAW轉JPEG的問題!因為並不是使用上傳的檔案,而是使用早就放在指定的$photoPath路徑的測試用IMG_1000.CR2檔案,應該要100%絕對沒有任何問題才對!如果連測試都會遇到問題,就算問題發生的機率只有1%,也絕對不允許發生!
雖然加上 -e 'with timeout of 600 seconds' 指令,有時候會在Apache的error_log錯誤記錄裡面會看到這樣的錯誤訊息:
execution error: Terminal got an error: AppleEvent timed out. (-1712)
我上網搜尋一下,發現也有別人遇到類似的問題,雖然他們執行的不是終端機的Terminal指令。
Avoiding AppleEvent Timed Out Error -1712 - Apple Community
因為絕對沒有任何問題的IMG_1000.CR2測試用照片檔案,就算重複轉換100次、1000次、1萬次,都絕對可以正常轉成JPEG才對,不應該會有任何無法轉換成功的問題!
我並不確定問題是出在哪裡?為了避免遇到無法轉JPEG的問題,只好放棄使用osascript指令,再想想看有沒有什麼其他的更好辦法?
就算osascript指令可以正常執行,也沒有任何錯誤訊息,還是會遇到一個重大的問題與一個隱憂,那就是:
因此,就算可以在macOS High Sierra 10.13上正常執行osascript指令,再過2-3年,終究還是會遇到老舊的macOS 10.13不支援未來新的數位相機的RAW檔案格式,無法轉換JPEG的問題。
至於第二個問題主要是,如果網站上有許多會員同時上傳RAW照片檔案的話,很可能會因為Mac mini主機的CPU忙著處理RAW轉JPEG,導致網頁的處理速度變慢。
要同時解決以上2個問題,最好的辦法就是不要在Web Server主機上進行「RAW轉JPEG」的工作,應該要把需要處理的RAW檔案傳到另外一台執行macOS Mojave 10.14甚至是10.15、10.16等新的作業系統的Mac mini主機。由另一台Mac mini主機來擔任「RAW轉JPEG」任務的話,就不會遇到老舊macOS系統不支援新的數位相機的RAW檔案格式的問題,也不會因為忙著處理RAW轉JPEG,導致Web Server主機沒空處理網站上的會員。
事實上,只要把任務分開到2台Mac mini主機,就能同時解決上面的兩個問題。而且就算再過10年,也許macOS High Sierra 10.13都還可以繼續使用。雖然現在已經有新的HTTP/3,但在短時間內應該還無法普及。
只用一台Mac mini主機的時候,只需要一個簡單的sips指令,就可以完成「RAW轉JPEG」的工作,要從PHP程式碼把需要處理的RAW傳給另外一台Mac電腦,命令另外一台Mac mini執行sips指令,再把轉換好的JPEG檔案傳回到Web Server主機,就會變成「困難的任務」(雖然不是不可能的任務)。
要開啟本機的終端機視窗,可以直接使用osascript指令,但是要開啟另外一台電腦的終端機視窗,就不能使用osascript指令,唯一可以想到的就是SSH。因為SSH的Secure Shell技術,本來就是利用加密與帳號密碼認證來登入到另外一台電腦的終端機,簡單的說SSH就是遠端終端機的連線指令。雖然說是「遠端電腦」但其實也可以就是放在Web Server旁邊的另外一台Mac mini主機。
只要在PHP加裝SSH2模組,就可以讓PHP也可以執行SSH的相關指令:
使用這5個ssh2指令寫的測試用ssh1.php程式碼是:
<? php function sshConnectServer() { global $sshConnect; $host = '10.0.0.20'; //遠端主機的IP位址 $port = 22; //ssh的網路埠 $user = 'insolerbrent'; //遠端主機的登入帳號 $pass = 'insoler'; //遠端主機的登入密碼 $sshConnect = ssh2_connect($host, $port); if($sshConnect !== false) { ssh2_auth_password($sshConnect, $user, $pass); return true; } else return false; } function RAWtoJPEG($PhotoName, $sFileName, $oFileName, $RAWFile, $JPGFile) //主檔名、本機RAW路徑、本機JPG路徑、RAW檔名、JPG檔名 { global $sshConnect; $RamDiskFolder = '/Volumes/MacDisk/'; //遠端伺服器的圖檔位置 $sshRAW = $RamDiskFolder . $RAWFile; $sshJPG = $RamDiskFolder . $JPGFile; if (sshConnectServer()) { //連接遠端伺服器 $time_start = microtime(true); ssh2_scp_send($sshConnect, $sFileName, $sshRAW); $time_end = microtime(true); $time = $time_end - $time_start; echo '透過SSH傳送RAW檔案的時間:' . $time . '< br / >'; $cmd = '/usr/bin/sips -s format jpeg --setProperty formatOptions 40% "' . $sshRAW . '" --out "' . $sshJPG . '";'; ssh2_exec($sshConnect, $cmd); $count = 0; do { usleep(300000); //等0.3秒 $ssh = ssh2_scp_recv($sshConnect, $sshJPG, $oFileName); $count++; } while ($ssh == false && $count < 30); if ($ssh) { $cmd = '/bin/rm ' . $RamDiskFolder . $PhotoName . '.* ; logout;'; ssh2_exec($sshConnect, $cmd); return true; } else { echo '無法從遠端伺服器傳送JPEG檔!!!' . '< br / >'; return false; } } else { echo '無法連接遠端伺服器執行RAW轉JPEG檔!!!' . '< br / >';
';
return false; } } $sshConnect = 0; $RAWfolder = 'RAW/'; //本機的圖檔位置 $time_start = microtime(true); $PhotoName = '152301'; $PhotoRAW = $PhotoName . '.CR2'; $PhotoJPG = $PhotoName . '.JPG'; $pFileName = $RAWfolder . $PhotoRAW; $jFileName = $RAWfolder . $PhotoJPG; if (RAWtoJPEG($PhotoName, $pFileName, $jFileName, $PhotoRAW, $PhotoJPG) == false) { echo '呼叫 MySQL 伺服器執行RAW轉JPEG檔失敗!' . '< br / >'; } $time_end = microtime(true); $time = $time_end - $time_start; echo '轉換RAW檔案的時間:' . $time . '< br / >'; ?>
ssh1.php的執行結果是:
透過SSH傳送RAW檔案的時間:1.0418560504913
轉換RAW檔案的時間:6.2987110614777
因為ssh2_exec與ssh2_scp_recv是非同步執行,也就是說,ssh2_exec送出指令以後,就會繼續執行ssh2_scp_recv指令。由於sips至少需要1秒~4秒左右的時間來處理「RAW轉JPEG」的轉檔動作。如果在執行ssh2_exec之後,立即執行ssh2_scp_recv的話,會因為遠端的Mac mini主機還沒有產生JPEG檔案,絕對會收到錯誤訊息!
前面的簡單sips程式碼只需要2.1秒,為什麼改用SSH會變成6.3秒?這是因為透過SSH還需要花0.6秒左右來登入遠端的終端機並認證帳號、密碼。而且使用ssh2_scp_send與ssh2_scp_recv來傳送與接收檔案的速度也比較慢,光是透過SSH傳送RAW與JPEG檔案,就需要3秒左右的時間:
另外一台遠端終端機主機(其實是放在Web Server旁邊的MySQL Server主機)必須事先打開「系統偏好設定→共享→遠端登入」選項,這樣就會看到「遠端登入:開啟」以及【若要從遠端登入此電腦,請輸入「ssh insolerbrent@raw.insoler.com」】的提示訊息。
雖然這個ssh2.php可以正常完成「RAW轉JPEG」的任務,但是SSH為了安全性,使用加密技術,透過SSH來傳送檔案,需要花費1.2秒左右的傳送時間。因此「傳RAW過去+傳JPEG回來」至少就需要2秒左右的時間!
這似乎太慢了,應該要想辦法找更好的方式。另外,ssh2_scp_recv有時候會傳送失敗,導致雖然在遠端伺服器主機上有轉好的JPEG檔案,卻無法傳送回到Web Server,這樣還是等於轉檔失敗!
我不知道為什麼ssh2_scp_recv會傳送失敗,就算使用迴圈等待9秒,還是會有無法傳送檔案的問題!只能猜想我使用的是比較老舊的PHP 5.5.38相容的SSH2模組,而不是最新的PHP 7.3搭配的SSH2模組。為了避免正式上線時會遇到問題,因此ssh2_scp_send與ssh2_scp_recv都不能使用!
由於SSH本身就是使用Secure Shell加密的遠端連線技術,為了提高網路安全性,犧牲一些傳輸速度是可以被接受。但是這裡只需要透過SSH來完成「RAW轉JPEG」的任務,並不是真正的遠端連線(SSH不區分近端連線或是跨國際的遠端連線),因此不需要全部都透過SSH指令,其實可以把傳送檔案的指令改成透過「SMB網路磁碟機」(也就是Windows的網路上的芳鄰)來解決。雖然在Mac上還有Apple獨家的AFP網路磁碟機可以使用,但是AFP相當老舊,不支援目前最新的APFS磁碟機格式,要連接到APFS磁碟機的話,就只能透過SMB網路磁碟機。
雖然我希望架設成「第1台主機→第2台主機」從第1台Mac mini主機,透過SSH呼叫第2台Mac mini主機來執行sips指令,理所當然在第2台Mac mini主機上不會有,也不存在需要處理的RAW照片檔案。因此:
當然是必然需要的動作。但其實從第2台Mac mini主機來思考,也可以透過「SMB網路磁碟機」直接存取第1台Mac mini主機的檔案,這樣就可以變成:
雖然這兩個思考方式的結果都差不多,都需要透過網路傳送RAW檔案與轉換的JPEG檔案。但是前面的做法,就會像SSH一樣,需要類似ssh2_scp_send的「cp」的檔案copy的指令,把第1台電腦的RAW檔案copy到第2台電腦,以及相當於ssh2_scp_recv的「cp」的檔案copy的指令,第2台電腦的JPEG檔案copy回到第1台電腦,然後再使用「rm」或是「unlink」指令刪除第2台電腦主機上的RAW與JPEG檔案。
第二個做法,就完全不需要執行任何的「cp」「rm」指令,只要指令sips直接開啟SMB網路磁碟機上的RAW檔案,轉換好的JPEG檔案也直接儲存在SMB網路磁碟機即可。雖然還是需要透過網路傳輸RAW與JPEG檔案,但卻可以再節省透過SSH執行「cp」與「rm」指令的動作。
<? php define ('SSH_DIRECTORY_PATH', '/Volumes/insoler/modules/boonex/photos/data/files/'); $sshConnect = 0; function sshConnectServer() { global $sshConnect; $host = '10.0.0.20'; //遠端主機的IP位址 $port = 22; //ssh的網路埠 $user = 'insolerbrent'; //遠端主機的登入帳號 $pass = 'insoler'; //遠端主機的登入密碼 $sshConnect = ssh2_connect($host, $port); if($sshConnect !== false) { ssh2_auth_password($sshConnect, $user, $pass); return true; } else return false; } function RAWtoJPEG($sFileName, $oFileName) { global $iMID, $sshConnect; if (sshConnectServer()) { $cmd = '/usr/bin/sips -s format jpeg --setProperty formatOptions 40% "' . $sFileName . '" --out "' . $oFileName . '"; logout;'; $ssh = ssh2_exec($sshConnect, $cmd); return true; } else return false; } $time_start = microtime(true); $RAW = SSH_DIRECTORY_PATH . '152301.CR2'; $jpg = SSH_DIRECTORY_PATH . '152301.JPG'; RAWtoJPEG($RAW, $jpg); echo '測試從遠端伺服器處理「RAW轉JPEG」檔案!RAW檔案的路徑是:' . $RAW . '< br / >'; $time_end = microtime(true); $time = $time_end - $time_start; echo '轉換RAW檔案的時間:' . $time . '< br / >'; ?>
ssh2.php的執行結果是:
測試從遠端伺服器處理「RAW轉JPEG」檔案!RAW檔案的路徑是:/Volumes/insoler/insoler/photos/ssh/RAW/152301.CR2
轉換RAW檔案的時間:4.327220916748
你可以看到「轉換RAW檔案的時間」從ssh1.php的6.3秒左右,加速到只要4.3秒左右就可以完成相同的動作!
雖然使用SMB網路磁碟機來傳送RAW與JPEG檔案(讀取RAW、寫入JPEG)比SSH快很多,但是4.3秒的處理時間,相較於在Web Server的本機上直接執行sips指令只要2.1秒,還是需要「4.3秒 - 2.1秒 = 2.2秒」。因為SSH遠端登入終端機,大約需要0.6秒。透過SMB網路磁碟機傳送RAW與JPEG檔案大約需要1.6秒。
想要知道sshConnectServer副程式需要花多少時間連接遠端主機,可以修改一下程式碼:
function RAWtoJPEG($sFileName, $oFileName) { global $iMID, $sshConnect; $time_start = microtime(true); if (sshConnectServer()) { $time = microtime(true) - $time_start; echo '透過SSH連接遠端終端機的時間:' . $time . '< br / >'; $cmd = '/usr/bin/sips -s format jpeg --setProperty formatOptions 40% "' . $sFileName . '" --out "' . $oFileName . '"; logout;'; $ssh = ssh2_exec($sshConnect, $cmd); return true; } else return false; }
ssh2.php的執行結果是:
透過SSH連接遠端終端機的時間:0.62959504127502
測試從遠端伺服器處理「RAW轉JPEG」檔案!RAW檔案的路徑是:/Volumes/insoler/insoler/photos/ssh/RAW/152301.CR2
轉換RAW檔案的時間:4.327220916748
總共的轉換時間需要4.3秒,扣除sips需要花2.1秒進行「RAW轉JPEG」,再扣除SSH連接遠端終端機需要0.6秒,透過SMB網路磁碟機傳送RAW與JPEG檔案,大約需要1.6秒。
如果你想要知道透過ssh2_exec指令執行系統的sips指令,要花多少時間,可以再追加一下時間的計算程式:
function RAWtoJPEG($sFileName, $oFileName) { global $iMID, $sshConnect; $time_start = microtime(true); if (sshConnectServer()) { $time = microtime(true) - $time_start; echo '透過SSH連接遠端終端機的時間:' . $time . '< br / >'; $time_start = microtime(true); $cmd = '/usr/bin/sips -s format jpeg --setProperty formatOptions 40% "' . $sFileName . '" --out "' . $oFileName . '"; logout;'; $ssh = ssh2_exec($sshConnect, $cmd); $time = microtime(true) - $time_start; echo 'ssh2_exec 的執行時間:' . $time . '< br / >'; return true; } else return false; }
ssh2.php的執行結果是:
透過SSH連接遠端終端機的時間:0.6076979637146
ssh2_exec 的執行時間:0.020769119262695
測試從遠端伺服器處理「RAW轉JPEG」檔案!RAW檔案的路徑是:/Volumes/insoler/insoler/photos/ssh/RAW/152301.CR2
轉換RAW檔案的時間:4.2820799350739
你可以看到ssh2_exec的執行時間竟然只有0.02秒!無論是哪一種相機的RAW檔案,都絕對不可能在短短的0.02秒左右完成「RAW轉JPEG」的動作!電腦再怎麼快,目前至少都需要花費1秒以上的時間,轉換一個RAW檔案到JPEG圖檔,花2-5秒是很正常的事情。
因此ssh2_exec這個指令其實是非同步執行,也就是ssh2_exec會告知遠端電腦的終端機要執行sips指令,但ssh2_exec並不會等待sips指令執行完畢,就會節省時間,直接回到PHP繼續執行其他的PHP程式碼,所以才會看到執行時間只有0.02秒的結果。
所謂「Synchronous同步執行」就是按照程式的先後順序,循序執行,一次只能執行一個指令,電腦會等到這個指令執行完畢,才能繼續執行下一個指令。所以「同步執行」其實是「非同時執行」。因為「同步執行」很容易誤解為「同時執行」,你可以把「同步」解讀為「電腦是在進行同一個動作、只能像是按「下一步」按鈕一樣,一步緊接一步的執行每一個指令」。
如果希望可以電腦程式可以同時執行多個指令,多工處理多個不同的指令,例如同時執行「RAW轉JPEG」、「WAV音樂轉MP3」、「DOC轉PDF」、「處理使用者上傳檔案」... 等,就稱為「Asynchronous非同步執行」或是「異步執行」。簡單的說「異步執行」就是「同時執行」。同樣的,無論是「非同步執行」或是「異步執行」都很容易被字面上的意思誤解,你可以把「非同步」或是「異步」解讀為「電腦不是在進行同一個動作,而是像幾個人同時各走各的路,執行多個不同的指令」。
因此簡單的說:
目前insoler網站在處理「上傳照片」的動作,就是採取Asynchronous的異步執行,也就是「同時執行」的方式處理。無論使用者上傳的是RAW檔案或是JPEG檔案,在上傳完一個照片檔案之後,會使用「異步處理」方式,交給「同時處理程式」繼續處理「RAW轉JPEG」,以及「JPEG縮圖」等動作。但是原本「上傳照片」的程式並不會因此停止或是等待之前上傳的照片「RAW轉JPEG」完成再繼續執行,而是直接繼續處理下一個要上傳的照片,直到user上傳的所有照片都全部上傳完成為止。
用文字說明,可能會比較難以理解,用一個簡單的圖表(雖然還是文字)來說明,應該會比較簡單:
上傳第1個照片檔案 → 上傳第2個照片檔案 → 上傳第3個照片檔案(同步執行)
↓ ↓ ↓ (非同步、異步執行)
第1個照片RAW轉JPEG 第2個照片RAW轉JPEG 第3個照片RAW轉JPEG
在「非同步、異步執行」的「第2個照片RAW轉JPEG」其實是一個完全是獨立執行的程式(程式內部是簡單的同步執行),不需要等待或是理會「上傳照片」的處理動作,更不需要等待前面「第1個照片RAW轉JPEG」執行完畢,就可以單獨執行。
目前「上傳照片檔案」與「RAW轉JPEG」是由2台Mac mini來負責處理:
由於「RAW轉JPEG」是一個對CPU負擔很重的工作,平均轉換一個RAW檔案大約需要2-4秒的時間。而MySQL Server資料庫伺服器也是一個對CPU負擔很重的工作,如果insoler將來有很多會員常常上傳RAW檔案,就必須把「RAW轉JPEG」的工作轉移到現在的備用Mac mini主機。也就是會架設成:
在Web Server網站伺服器主機,會透過SSH連線到MySQL Server資料庫伺服器主機,或是專用的RAW照片處理主機。
MySQL Server資料庫伺服器主機或是專用的RAW照片處理主機,則會透過AFP或是SMB網路磁碟機連接到Web Server網站伺服器主機,直接存取網站伺服器主機上的RAW檔案,並將轉換好的JPEG檔案,直接儲存在Web Server網站伺服器主。