uploadprogress擴展需要依靠JS獲取服務器提供的進度,這裡的進度是服務器接收的文件進度。
而在PHP5.4之後,我們可以在不添加擴展的情況下,從session數據中獲取了文件上傳的進度。uploadprogress擴展和PHP5.4的session擴展都能獲取上傳的進度,其是否有相同的地方呢?
我們先來看uploadprogress擴展,下載源碼包,解圧,直接打開文件,我們可以在example中找到一個簡單的示例。在info.php文件中,uploadprogress_get_info函數用來獲取上傳文件進度。upploadprogress.c文件存儲了擴展的實現過程。uploadprogress擴展實現的關鍵在於其模塊寢化函數:
PHP_MINIT_FUNCTION(uploadprogress) { REGISTER_INI_ENTRIES(); php_rfc1867_callback = uploadprogress_php_rfc1867_file; return SUCCESS; }
此函數的核心就是設置php_rfc1867_callback為uploadprogress_php_rfc1867_file。
設置這個函數指針有什麼用呢?
在前面的文章PHP內核中文件上傳類型的獲取過程中我們了解到PHP處理POST請求的函數是SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)(main/rfc1867.c)。在這裡, 我們發現了若干個php_rfc1867_callback的調用,從調用的第一個參數來看,它可以分為六個事件,或者說有六個回調更新點。
如果此時我們查看PHP5.4的的session擴展的實現文件session.c時,搜索php_rfc1867_callback,你會發現在模塊初始化函數中也有與擴展類似的賦值操作:
php_rfc1867_callback = php_session_rfc1867_callback;
同樣,在php_session_rfc1867_callback函數中有與uploadprogress同樣的六個事件的處理,這六個事件相當於六個鉤子程序,分別對應POST請求的處理的六個不同的位置,在PHP5.4中他們的作用分別是:
1、MULTIPART_EVENT_START 在處理所有的請求實體之前,初始化上傳進度信息,比用於記錄上傳進度相關信息的progress結構體信息(如content-length)
2、MULTIPART_EVENT_FORMDATA 對於每個multipart包含的控制,執行此步初始化操作,以此之前會解析Content-Disposition相關屬性,並初始化progress的其它信息,如session_id,以及整個上傳活動的key,這裡表示整個上傳進度准備好了。
3、MULTIPART_EVENT_FILE_START 開始處理上傳的文件信息,如果progress的data不存在,則會創建此結構,並初始化session中存儲的對於此次文件上傳的start_time、content_length、bytes_processed、files等信息。然後處理單個文件的上傳屬性,如field_name、tmp_name等。對於tmp_name等字段這裡是執行初始化操作。這一步的時候獲取session 的值才會開始有上傳進度的相關信息。
4、MULTIPART_EVENT_FILE_DATA 更新上傳文件的長度,在一堆的文件相關信息檢測和臨時文件寫入之前,也是在將數據寫入到$_FILES之前。
5、MULTIPART_EVENT_FILE_END 單個文件上傳結束,此時會更新這個文件相關的一些信息,比如error, tmp_name,tmp_name字段在start時是null。當然這裡還有針對當前文件的done字段的更新。
6、MULTIPART_EVENT_END 更新session數組的最後的一些結信息 比如done字段 並清空progress的信息,
這裡的六個事件是相同的,而uploadprogress擴展和PHP5.4的session擴展在事件處理過程中中間存儲結構和最後的返回內容與方式上存在一些差異。uploadprogress擴展的存儲結構為一個按照擴展制定的規則生成的臨時文件,最後是通過擴展函數uploadprogress_get_info返回上傳進度的數組。PHP5.4的存儲結構為SESSION的存儲方式,或者是文件,或者是memcache,這個按session的設置來,其最終是通過$_SESSION返回相關數組。
除了uploadprogress擴展外,APC也以設置php_rfc1867_callback = apc_rfc1867_progress,提供了類似的解決方案,啟動此功能需要在php.ini中設置apc.rfc1867項為啟用,並且在表單中加一個隱藏域 APC_UPLOAD_PROGRESS,這個域的值可以隨機生成一個hash,以確定此次上傳操作的唯一性。通過Ajax調用服務端顯示進度的接口,在接口中通過apc_fetch函數獲取APC緩存的文件上傳進度。比如print_r(apc_fetch(”upload_$_POST[APC_UPLOAD_PROGRESS]“));可以得到如下結果:
Array ( [total] => 1142543 [current] => 1142543 [rate] => 1828068.8 [filename] => test [name] => file [temp_filename] => /tmp/php8F [cancel_upload] => 0 [done] => 1 )