// 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;
}
?>