PHP_APC+Ajax实现的监视进度条的文件上传

// load.js:

ADS.addEvent(window, 'load', function (event) { var fileList = ADS.$('fileList'); // 按照需要修改uoloadForm

    addProgressBar('uploadForm', function (response) { var files = response.filesProcessed; for (var i in files) { // 跳过空文件

            if (files[i] === null) { continue; } // 创建一个新的文件列表元素

            var li = document.createElement('li'); var a = document.createElement('a'); a.setAttribute('href', 'uploads/' + files[i]); a.appendChild(document.createTextNode(files[i])); li.appendChild(a); fileList.appendChild(li); } // 更新文件计数器

        var countContainer = ADS.$('fileCount'); ADS.removeChildren(countContainer); var numFiles = fileList.getElementsByTagName('li').length; countContainer.appendChild(document.createTextNode(numFiles)); }); }); // uploader.js: /** * User: lindongpeng * Date: 13-2-19 */



function verifyFileType(fileInput) { if (!fileInput.value || !fileInput.accept) { return true; } var extension = fileInput.value.split('.').pop().toLowerCase(), mimetypes = fileInput.accept.toLowerCase().split(','), type; for (var i = 0, len = mimetypes.length; i < len; i++) { type = mimetypes[i].split('/')[1]; if (type === extension || (type === 'jpeg' && extension === 'jpg')) { return true; } } return false; } var addProgressBar = function (form, modificationHander) { // 检查表单是否存在

    if (!(form = ADS.$(form))) { return false; } // 查找所有文件输入元素

    var allInputs = form.getElementsByTagName('input'); var input; var fileInputs = []; for (var i = 0; (input = allInputs[i]); i++) { if (input.getAttribute('type') === 'file') { fileInputs.push(input); } } // 如果没有文件输入元素则停止脚本

    if (!fileInputs.length) { return false; } // 添加change事件以基于MIME类型验证扩展名

    for (var j = 0, len = fileInputs[i]; i < len; j++) { // 使用change事件侦听器进行文件类型检查

        ADS.addEvent(fileInputs[i], 'change', function (event) { var ok = verifyFileType(this); if (!ok) { if (!ADS.hasClassName(this, 'error')) { ADS.addClassName(this, 'error'); } alert('Sorru, that file type is not allowed.Please select one of:' + this.accept.toLowerCase()); } else { ADS.removeClassName(this, 'error'); } }); } // 为上传而附加iframe元素

    // 在IE中,不能像下面这样通过DOM设置name属性,例如:

    // var uploadTargetFrame = document.createElement('iframe');

    // uploadTargetFrame.setAttribute('id', 'uploadTargetFrame');

    // uploadTargetFrame.setAttribute('name', 'uploadTargetFrame');

    // 为解决这个问题,需要创建一个div并使用其innerHTML属性

    // 从而确保在IE和其他浏览器中都能正确的设置name属性

    var uploadTargetFrame = document.createElement('div'); uploadTargetFrame.innerHTML = '<iframe name="uploadTargetFrame" id="uploadTargetFrame"></iframe>'; ADS.setStyleById(uploadTargetFrame, { 'width': 0, 'height': 0, 'border': 0, 'visibility': 'hidden', 'z-index': -1 }); document.body.appendChild(uploadTargetFrame); // 将表单的target属性修改为新iframe元素

    // 这样可以避免页面重载

    form.setAttribute('target', 'uploadTargetFrame'); // 创建一个唯一的ID以跟踪上传进度

    var uniqueID = 'A' + Math.floor(Math.random() * 1000000000000000); // 为APC_UPLOAD_PROGRESS键添加这个唯一ID

    // 这个字段必须添加到文件输入字段之前,以便

    // 服务器首先取得改键并触发存储进度信息的操作

    var uniqueIDField = document.createElement('input'); uniqueIDField.setAttribute('type', 'hidden'); uniqueIDField.setAttribute('value', uniqueID); uniqueIDField.setAttribute('name', 'APC_UPLOAD_PROGRESS'); form.insertBefore(uniqueIDField, form.firstChild); // 创建进度条的不同部分



    // 进度条

    var progressBar = document.createElement('div'); progressBar.className = 'progressBar'; // 内部的背景容器

    var progressBackground = document.createElement('div'); progressBackground.className = 'progressBackground'; ADS.setStyle(progressBackground, { 'height': '10px' }); progressBackground.appendChild(progressBar); // 检查已有的定位点

    // 必须是带有progressContainer类的span元素

    var progressContainer = ADS.getElementsByClassName( 'progressContainer', 'div' )[0]; // 如果该定位点不存在则创建一个并将其添加到表单中

    if (!progressContainer) { progressContainer = document.createElement('div'); progressContainer.className = 'progressContainer'; form.appendChild(progressContainer); } // 添加进度条的其余部分

 progressContainer.appendChild(progressBackground); // 同时也添加一个进度信息显示区域

    var progressMessage = document.createElement('div'); progressMessage.className = 'progressMessage'; progressContainer.appendChild(progressMessage); // 创建一个将由后面的进度监视方法使用

    // 的私有方法,以方便更新进度条和相应信息

    function updateProgressBar(percent, message, satus) { progressMessage.innerHTML = message; ADS.removeClassName(progressMessage, 'error complete waiting uploading'); ADS.addClassName(progressMessage, satus); // CSS样式和className将负责指示状态

 ADS.setStyle(progressBar, { 'width': percent }); } // 从0%和waiting开始初始化进度条

    updateProgressBar('0%', 'Waiting for upload', 'waiting'); // 为表单添加提交事件侦听器,用于

    // 验证表单信息和更新进度条

    ADS.addEvent(form, 'submit', function (event) { // 再次检查输入以确保

        // 其包含正确的扩展名

        var ok = true; var hasFiles = false; for (var i = 0, fileInput; (fileInput = fileInputs[i]); i++) { if (fileInput.value) { hasFiles = true; } if (!verifyFileType(fileInput)) { // 突出显示出错的文件输入元素

                if (!ADS.hasClassName(fileInput, 'error')) { ADS.addClassName(fileInput, 'error'); } ok = false; } } if (!ok || !hasFiles) { // 如果检查为通过则提示用户解决问题

 ADS.preventDefault(event); alert('Please select some valid files'); return false; } // 通过发出警告信息来禁用表单元素

        function warning(event) { ADS.preventDefault(event); alert('There is an upload in progress. Please wait.'); } for (var i = 0, input; (input = allInputs[i]); i++) { // input.setAttribute('disabled', 'disabled');

            ADS.addEvent(input, 'mousedown', warning); } // 创建一个函数以便在上传完成后重启表单

        // 该函数将在ajax事件侦听器内部被调用

        function clearWarnings() { // 从表单元素移除警告侦听器

            for (var i = 0, input; (input = allInputs[i]); i++) { ADS.removeEvent(input, 'mousedown', warning); } // 以新ID数值更新原ID和表单

            // 以确保下次上传不影响本次上传

            uniqueID = Math.floor(Math.random() * 100000000000000); uniqueIDFiled.setAttribute('value', uniqueID); } // 更新进度条

        updateProgressBar('0%', 'Waiting for upload', 'waiting'); // 为模拟脚本设置计数器

        var counter = 0; // 创建一个新方法以触发一次新的进度请求

        var progressWatcher = function () { // 使用唯一键来请求进度信息

            ADS.ajaxRequest(form.action + (form.action.indexOf('?') === -1 ? '?' : '&') + 'key=' + uniqueID + '&sim=' + (++counter), { // 服务器端脚本将返回适当的头部信息

                // 因此我们可以使用JSON侦听器

                jsonResponseListener: function (response) { // 检测响应以确认服务器端

                    // 脚本中是否存在错误



                    if (!response) { // 没有有效的响应

 updateProgressBar( '0%', 'Invalid response from progress watcher', 'error' ); // 请求完成故清除警告提示

 clearWarnings(); } else if (response.error) { // 服务器端报告了错误

                        updateProgressBar('0%', response.error, 'error'); // 请求完成故清除警告提示

 clearWarnings(); } else if (response.done === 1) { // POST请求已经完成

                        updateProgressBar('100%', 'Upload complete', 'complete'); // 请求完成故清除警告提示

 clearWarnings(); // 为提供更改处理程序的

                        // 用户传递新信息

                        if (modificationHander.constructor === Function) { modificationHander(response); } } else { // 更新进度条并返回结果

                        // 由于结果是null, 所以

                        // 返回会简单地停止执行

                        // 方法中其余的代码

 updateProgressBar( Math.round(response.current / response.total * 100) + '&', response.current + 'of'

                                + response.total + '. Uploading file: '

                                + response.currentFileName, 'uploading' ); // 再次执行进度监视程序

                        setTimeout(progressWatcher, 1000); } }, errorListener: function () { // ajax请求发生了错误

                    // 因此需要让用户知道

                    updateProgressBar('0%', this.status, 'error'); // 并清除警告提示

 clearWarnings(); } }); }; // 开始监视

        setTimeout(progressWatcher, 1000); }); }; // 主页

index.php: <?php // 循环遍历uploads文件夹 // 以便取得已经上传的文件

$uploads = new DirectoryIterator('./uploads'); $files=array(); foreach($uploads as $file) { // 跳过,并。。。

    if(!$file->isDot() && $file->isFile()) { // 添加到数组,。稍后,该数组

        // 将在HTML中被连接起来

        $files[]=sprintf( '<li><a href="uploads/%s">%s</a> <em>%skb</em></li>', $file->getFilename(), $file->getFilename(), round($file->getSize()/1024)

 ); } } // 输出页面

?>

<!DOCTYPE html>

<html lang="en">

<head>

    <title>Image Uploader with Progress (php5-APC)</title>



    <!-- Inclue some CSS style sheet to make everything look a little nicer -->

    <link rel="stylesheet" href="http://www.cnblogs.com/../shared/source.css" />

    <link rel="stylesheet" href="http://www.cnblogs.com/chapter.css" />

    <link rel="stylesheet" href="style.css" />



    <!-- Your ADS library with the common JavaScript objects -->

    <script src="http://www.cnblogs.com/../ADS.js"></script>



    <!-- Progress bar script -->

    <script src="uploader.js"></script>



    <!-- load script -->

    <script src="load.js"></script>



</head>

<body>

<h1>Image Uploader with Progress (php5-APC)</h1>

<div id="content">

    <form action="actions/" enctype="multipart/form-data" method="post" id="uploadForm">



        <fieldset>

            <legend>Upload a new image</legend>

            <p>Only jpg/gif/png files less than 100kb allowed.</p>

            <div class="fileSelector">

                <label for="newFile1">File 1</label>

                <input type="file" id="newFile1" name="newFile1" accept="image/jpeg,image/gif,image/png"/>

            </div>

            <div class="fileSelector">

                <label for="newFile2">File 2</label>

                <input type="file" id="newFile2" name="newFile2" accept="image/jpeg,image/gif,image/png"/>

            </div>

            <div class="fileSelector">

                <label for="newFile3">File 3</label>

                <input type="file" id="newFile3" name="newFile3" accept="image/jpeg,image/gif,image/png"/>

            </div>

            <input id="submitUpload" name="submitUpload" type="submit" value="Upload Files" />

        </fieldset>



    </form>



    <div id="browserPane">

        <h2>

                <span id="fileCount">

                    <?php echo count($files); ?>

                </span>

            Existing Files in <em>uploads/</em>

        </h2>

        <ul id="fileList">

            <?php echo join($files,"\n\t\t\t\t"); ?>

        </ul>

    </div>

</div>



<div id="where-from"> From <a href="http://advanceddomscripting.com" title="AdvancED DOM Scripting">AdvancED DOM Scripting</a>

    | <a href="http://www.amazon.com/exec/obidos/ASIN/1590598563/jeffreysamb05-20" title="Buy it on Amazon">Paperback</a>

</div>

</body>



</html>





// 表单action指向的php文件

action/index.php: 

 <?php //check if the request is using the ADS.ajaxRequest() method.

if($_SERVER['HTTP_X_ADS_AJAX_REQUEST']) { // Return the progress information as a JSON string

    

    // Send some headers to prevent caching of the progress request

    header('Expires: Fri, 13 Dec 1901 20:45:54 GMT') ; header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT') ; header('Cache-Control: no-store, no-cache, must-revalidate') ; header('Cache-Control: post-check=0, pre-check=0', false) ; header('Pragma: no-cache') ; // This will be a JavaScript JSON response

    header('Content-Type:application/json; charset=utf-8' ) ; // Retrieve the status using the getStatus() function below 

    echo json_encode(getStatusAPC($_GET['key'])); die(); } // Process any files in the PHP $_FILES[] array and return to the // main script if everything went well. Otherwise we'll display a // error page.

 $allowedExtensions = array('jpg','jpeg','gif','png'); $errorMessage = null ; $storedFiles = array(); if(count($_FILES) > 0) { try { // Process each file

        foreach($_FILES as $key=>$info) { if($_FILES[$key]['name']) { // storeFile() throws exceptions

                $file = storeFile($key,'../uploads/',$allowedExtensions); } else { $file = null; } // Keep track of stored files incase you need to

            // remove them.

            $storedFiles[$key] = $file['basename']; } if($_POST['APC_UPLOAD_PROGRESS'] && function_exists('apc_store')) { // Store the file information so it can be 

            // retrieved in the progress watcher 

            apc_store('upload_finished_'.$_POST['APC_UPLOAD_PROGRESS'],$storedFiles); // Die. This message will display in the iframe 

            die('Upload complete.'); } // Everything was successful so redirect back 

        // to the main index page

        header('Location: ../'); die(); } catch (Exception $e) { // There was an error so remove any files that were uploaded

 foreach($storedFiles as $file) { if(is_file($file)) unlink('uploads/'.$file); } $storedFiles = array(); if($_POST['APC_UPLOAD_PROGRESS'] && function_exists('apc_store')) { // Store the error message so it can be 

            // retrieved in the progress watcher

 apc_store( 'upload_error_'.$_POST['APC_UPLOAD_PROGRESS'], $e->getMessage() ); // Die. This message will display in the iframe

            die('There was an error'); } else { // Get the error message

            $errorMessage = sprintf( '<p>%s failed: %s</p>', $key, $e->getMessage() ); // Display a simple error page with a 

            // link back to the main index file

 echo <<<XHTML <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

    <title>Oops!</title>

</head>

<body>

    <h1>Error</h1>

    <p>The system reported an error with the file(s) you were trying to upload:</p>

 {$errorMessage} <p><a href="../">Return to the upload page</a></p>



<div id="where-from"> From <a href="http://advanceddomscripting.com" title="AdvancED DOM Scripting">AdvancED DOM Scripting</a> 

    | <a href="http://www.amazon.com/exec/obidos/ASIN/1590598563/jeffreysamb05-20" title="Buy it on Amazon">Paperback</a>

</div>

</body>



</html>

XHTML; } } } /** * Store a file uploaded through HTTP on the server * * This function will access the global $_FILES array to retrieve the * information: * * The original name of the file on the client machine. * $_FILES['userfile']['name'] * * The mime type of the file, if the browser provided this information. * An example would be 'image/gif'. This mime type is however not checked * on the PHP side and therefore don't take its value for granted. * $_FILES['userfile']['type'] * * The size, in bytes, of the uploaded file. * $_FILES['userfile']['size'] * * The temporary filename of the file in which the uploaded file was stored on the server. * $_FILES['userfile']['tmp_name'] * * The error code associated with this file upload. * This element was added in PHP 4.2.0 * $_FILES['userfile']['error'] * * @param string $key The key in $_FILES that represents the file you wish to * store. This is generally the name attribute from the form. * @param string $where The directory on the server where you wish to store * the file. This can be absolute or relative to the location of execution. * @param array $extensions An array of acceptable extensions. (white list) * @param int $maxBytes The maximum number of bytes * @return array */

function storeFile($key,$where,$extensions,$maxBytes=null) { try { // Check for the file

        if(!$_FILES[$key]) { throw new Exception('The specified key does not exist in the $_FILES array'); } // Check the uplod location 

        if(!$where) { throw new Exception('Upload location not specified. If the current directory is desired, use "."'); } elseif ($where[strlen($where)-1] != DIRECTORY_SEPARATOR) { $where .= DIRECTORY_SEPARATOR; } // Check for permissions

        if(!is_writeable($where)) { throw new Exception('This page can not access the specified upload directory.'); } // convert the extensions to an array

        // (if a single extension as a string was supplied)

        settype($extensions,'array'); //check for extensions

        if(count($extensions) == 0) { throw new Exception('No valid extensions were specified.'); } // Convert ini to bytes and store in maxBytes if required

        $maxBytes = ($maxBytes ? $maxBytes : preg_replace_callback( '/([0-9]+)([gmk])/i', 'toBytes', ini_get('upload_max_filesize') )); // check PHP upload errors 

        switch ($_FILES[$key]['error']) { case UPLOAD_ERR_OK: // everything was fine. Proceed

                break; case UPLOAD_ERR_INI_SIZE: throw new Exception('The uploaded file exceeds the upload_max_filesize directive ('.ini_get('upload_max_filesize').') in php.ini.'); break; case UPLOAD_ERR_FORM_SIZE: throw new Exception('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.'); break; case UPLOAD_ERR_PARTIAL: throw new Exception('The uploaded file was only partially uploaded.'); break; case UPLOAD_ERR_NO_FILE: throw new Exception('No file was uploaded.'); break; case UPLOAD_ERR_NO_TMP_DIR: throw new Exception('Missing a temporary folder.'); break; case UPLOAD_ERR_CANT_WRITE: throw new Exception('Failed to write file to disk'); break; default: throw new Exception('Unknown PHP File Error'); } // Check if the files size is greater than the 

        // file size in the arguments

        if($_FILES['userfile']['size'] > $maxBytes) { throw new Exception('The uploaded file exceeds the maximum size specified in the application.'); } // Sanitize the file name

        $cleanName = str_replace(' ','-',$_FILES[$key]['name']); $cleanName = preg_replace('/-+/','-',$cleanName); $cleanName = preg_replace('/[^a-z0-9_.\/-]/i','',$cleanName); $fileNameParts = pathinfo($cleanName); // Verify the sanitized name is good

        $fileNameParts['filename'] = str_replace('.','_',$fileNameParts['filename']); if(!$fileNameParts['filename']) { throw new Exception('The desired file name contains no valid characters.'); } // Verify the extension is valid

        $fileNameParts['extension'] = strtolower($fileNameParts['extension']); if(!in_array($fileNameParts['extension'], $extensions)) { throw new Exception('The file extension is not one of: '.join($extensions,', ')); } // Postfix the file with a counter to avoid duplicates

        $count = 0; $postfix = ''; while(file_exists($uploadLocation = $where.$fileNameParts['filename'].$postfix.'.'.$fileNameParts['extension'])) { $postfix = '-'.++$count; } // Move the upload into place

        if(!move_uploaded_file($_FILES[$key]['tmp_name'], $uploadLocation)) { throw new Exception('Failed to move uploaded tmp file.'); } } catch (Exception $e) { // Catch exceptions for garbage collection and error storage



        // Remove the temp file

        if($_FILES[$key] && is_uploaded_file($_FILES[$key]['tmp_name'])) { @unlink($_FILES[$key]['tmp_name']); } // Throw the exception again for developers to catch

        throw $e; } // Return the information about the new file using pathinfo

    $return = pathinfo($uploadLocation); $return['rawname'] = basename($_FILES[$key]['name']); return $return; } function toBytes($matches) { switch(strtolower($matches[2])) { case "k": return $matches[1] * 1024; break; case "m": return $matches[1] * 1048576; break; case "g": return $matches[1] * 1073741824; break; } } /** * PHP 5.2 has a new set of hooks for checking the progress of a file upload * with APC 3.5 * * http://viewcvs.php.net/viewvc.cgi/pecl/apc/INSTALL?revision=3.53&view=markup * * apc.rfc1867 * RFC1867 File Upload Progress hook handler is only available * if you compiled APC against PHP 5.2.0 or later. When enabled * any file uploads which includes a field called * APC_UPLOAD_PROGRESS before the file field in an upload form * will cause APC to automatically create an upload_ * user cache entry where is the value of the * APC_UPLOAD_PROGRESS form entry. * (Default: 0) * */

function getStatusAPC($key) { $response = false; // will return false if not found

    if($status = apc_fetch('upload_'.$_GET['key'])) { /* status { "total":2676099, "current":102685, "filename":"test_large.jpg", "name":"test_file", "done":0 } */ $response = array( 'total' => $status['total'], 'current' => $status['current'], 'currentFileName' => $status['filename'], 'currentFieldName' => $status['name'], 'filesProcessed' => null, 'error' => null, 'done' => $status['done'], 'debug'=>null ); if($message = apc_fetch('upload_error_'.$_GET['key'])) { $response['error'] = $message; $response['debug'] = 'There was an error'; } else if ($status['done']==1 && ($filesProcessed = apc_fetch('upload_finished_'.$_GET['key']))) { //wait until the last file processed matches the one in status

            $response['debug'] = 'Files were processed '; if(($last = array_pop(array_keys(((array)$filesProcessed)))) == $status['name']) { $response['filesProcessed'] = $filesProcessed; $response['debug'] .= ' - all'; } else { // Override the done state because the upload 

                // has finished but the server is still processing

                // the files

                $response['done']= 0; $response['debug'] .= " - \"$last\" != \"{$status['name']}\""; } } } return $response; } ?>

 

你可能感兴趣的:(Ajax)