Post view

insoler網站解決使用sips指令上傳RAW轉JPEG會產生全黑圖檔、破碎圖檔、錯誤訊息的問題

請先閱讀前一篇的相關文章:

關於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」的做法才行。

透過終端機的osascript來執行sips指令

雖然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檔案,但卻會遇到兩個新的問題:

  • 會不斷開啟新的終端機視窗
  • 不知道sips什麼時候才會完成「RAW轉JPEG」檔案的動作。

第一個問題,可以在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

68:87: execution error: System Events got an error: AppleEvent timed out. (-1712) · Issue #45 · Teamwork/node-auto-launch · GitHub

因為絕對沒有任何問題的IMG_1000.CR2測試用照片檔案,就算重複轉換100次、1000次、1萬次,都絕對可以正常轉成JPEG才對,不應該會有任何無法轉換成功的問題!

我並不確定問題是出在哪裡?為了避免遇到無法轉JPEG的問題,只好放棄使用osascript指令,再想想看有沒有什麼其他的更好辦法?

透過加密的SSH遠端終端機來執行sips指令

就算osascript指令可以正常執行,也沒有任何錯誤訊息,還是會遇到一個重大的問題與一個隱憂,那就是:

  • macOS Mojave 10.14已經不再是Server,只剩下網路磁碟機功能。
  • 直接在Web Server主機上進行RAW轉JPEG會佔用許多CPU時間。

因此,就算可以在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主機沒空處理網站上的會員。

  • 第一台Mac mini:執行macOS High Sierra 10.13,擔任Web Server的任務。
  • 第二台Mac mini:執行macOS Mojave 10.14或是10.15...等新的macOS系統,擔任RAW轉JPEG的任務。

事實上,只要把任務分開到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的相關指令:

  • ssh2_connect:透過SSH連接遠端電腦主機。遠端的另外一台主機必須開放TCP 22 port網路埠。
  • ssh2_auth_password:登入遠端主機的帳號、密碼。
  • ssh2_scp_send:透過SSH傳送檔案到遠端主機,也就是把檔案copy到遠端主機。
  • ssh2_exec:透過SSH在遠端主機上執行指令。
  • ssh2_scp_recv:透過SSH接收遠端主機上的檔案,也就是把檔案從遠端主機copy回來。

使用這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秒左右的時間:

  • 透過sshConnectServer連接遠端的終端機:0.6秒
  • 透過ssh2_exec執行sips指令:2.1秒
  • 使用ssh2_scp_send與ssh2_scp_recv來傳送與接收檔案:3.6秒

另外一台遠端終端機主機(其實是放在Web Server旁邊的MySQL Server主機)必須事先打開「系統偏好設定→共享→遠端登入」選項,這樣就會看到「遠端登入:開啟」以及【若要從遠端登入此電腦,請輸入「ssh insolerbrent@raw.insoler.com」】的提示訊息。

利用AFP或是SMB的「網路磁碟機」來提高ssh2_exec與sips指令的執行速度

雖然這個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網路磁碟機。

  • ssh2_connect:透過SSH連接遠端電腦主機。遠端的另外一台主機必須開放TCP 22 port網路埠。
  • ssh2_auth_password:登入遠端主機的帳號、密碼。
  • 網路磁碟機:透過SMB網路磁碟機,把RAW檔案copy到遠端主機。
  • ssh2_exec:透過SSH在遠端主機上執行指令。
  • 網路磁碟機:透過SMB網路磁碟機,把JPEG檔案copy回到網站主機。

雖然我希望架設成「第1台主機→第2台主機」從第1台Mac mini主機,透過SSH呼叫第2台Mac mini主機來執行sips指令,理所當然在第2台Mac mini主機上不會有,也不存在需要處理的RAW照片檔案。因此:

  • 網路磁碟機:透過SMB網路磁碟機,把原始的RAW檔案copy到遠端主機。
  • 網路磁碟機:透過SMB網路磁碟機,把轉換的JPEG檔案copy回到網站主機。

當然是必然需要的動作。但其實從第2台Mac mini主機來思考,也可以透過「SMB網路磁碟機」直接存取第1台Mac mini主機的檔案,這樣就可以變成:

  • 網路磁碟機:透過SMB網路磁碟機,sips直接開啟第1台Mac mini主機的RAW檔案。
  • 網路磁碟機:透過SMB網路磁碟機,sips轉換存檔的JPEG檔案直接寫入第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同步執行與Asynchronous非同步執行

所謂「Synchronous同步執行」就是按照程式的先後順序,循序執行,一次只能執行一個指令,電腦會等到這個指令執行完畢,才能繼續執行下一個指令。所以「同步執行」其實是「非同時執行」。因為「同步執行」很容易誤解為「同時執行」,你可以把「同步」解讀為「電腦是在進行同一個動作、只能像是按「下一步」按鈕一樣,一步緊接一步的執行每一個指令」。

如果希望可以電腦程式可以同時執行多個指令,多工處理多個不同的指令,例如同時執行「RAW轉JPEG」、「WAV音樂轉MP3」、「DOC轉PDF」、「處理使用者上傳檔案」... 等,就稱為「Asynchronous非同步執行」或是「異步執行」。簡單的說「異步執行」就是「同時執行」。同樣的,無論是「非同步執行」或是「異步執行」都很容易被字面上的意思誤解,你可以把「非同步」或是「異步」解讀為「電腦不是在進行同一個動作,而是像幾個人同時各走各的路,執行多個不同的指令」。

因此簡單的說:

  • Synchronous同步執行   = 一次只能「下一步」執行一個指令,不能同時執行多個指令。
  • 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來負責處理:

  • Web Server網站伺服器的Mac mini:安裝OS X El Capitan 10.11.6,擔任網站的Web網頁與上傳照片的任務。
  • MySQL Server資料庫伺服器的Mac mini:安裝最新的macOS Mojave 10.14.1,負責資料庫伺服器主機與「RAW轉JPEG」的任務。

由於「RAW轉JPEG」是一個對CPU負擔很重的工作,平均轉換一個RAW檔案大約需要2-4秒的時間。而MySQL Server資料庫伺服器也是一個對CPU負擔很重的工作,如果insoler將來有很多會員常常上傳RAW檔案,就必須把「RAW轉JPEG」的工作轉移到現在的備用Mac mini主機。也就是會架設成:

  • Web Server網站伺服器的Mac mini:安裝OS X El Capitan 10.11.6,擔任網站的Web網頁與上傳照片的任務。
  • MySQL Server資料庫伺服器的Mac mini:安裝最新的macOS Mojave 10.14.1,負責資料庫伺服器主機。
  • 處理RAW照片的Mac mini:安裝最新的macOS Mojave 10.14.1,專門負責處理「RAW轉JPEG」的任務。

在Web Server網站伺服器主機,會透過SSH連線到MySQL Server資料庫伺服器主機,或是專用的RAW照片處理主機。

MySQL Server資料庫伺服器主機或是專用的RAW照片處理主機,則會透過AFP或是SMB網路磁碟機連接到Web Server網站伺服器主機,直接存取網站伺服器主機上的RAW檔案,並將轉換好的JPEG檔案,直接儲存在Web Server網站伺服器主。

蘇言霖 2018/11/20 0 599
Comments
Order by: 
Per page:
 
  • There are no comments yet
Rate
0 votes
Post info
蘇言霖
「超級懶貓級」社群網站站長
2018/11/20 (674 days ago)
Actions