我在文件上传进度方面遇到了一些问题 . 我在xampp,windows 7上使用zend framework 2.2 .
Form (SignupForm) :
namespace Application\Form;
use Zend\Form\Form;
use Zend\Form\Element;
class SignupForm extends Form
{
public function __construct($name = null)
{
$this->add(array(
'name' => 'username',
'type' => 'Text',
'options' => array(
'label' => 'Username',
'label_attributes' => array(
'class' => 'formlabel',
),
),
));
$this->add(array(
'type' => 'Zend\Form\Element\File',
'name' => 'fileupload',
'attributes' => array(
'id' => 'fileupload',
),
'options' => array(
'label' => 'Photo',
'label_attributes' => array(
'class' => 'formlabel',
),
),
));
}
}
Controller (IndexController) :
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Model\Signup;
use Application\Form\SignupForm;
class IndexController extends AbstractActionController {
protected $userTable;
protected $userTablee;
public function getSignTable($table, $object) {
if (!$this->$object) {
$sm = $this->getServiceLocator();
$this->$object = $sm->get($table);
}
return $this->$object;
}
public function indexAction() {
return new ViewModel();
}
public function signupAction() {
$form = new SignupForm();
$form->get('submi')->setValue('Submit');
$request = $this->getRequest();
if ($request->isPost()) {
$album = new Signup();
$t = $this->getSignTable('dbAdapter\dbtable=user', 'userTablee');
$form->setInputFilter($album->getInputFilter($t));
$data = array_merge_recursive(
$this->getRequest()->getPost()->toArray(),
$this->getRequest()->getFiles()->toArray()
);
$form->setData($data);
if ($form->isValid()) {
$album->exchangeArray($form->getData());
$this->getSignTable('Album\Model\AlbumTable\dbtable=user', 'userTable')->saveUser($album);
//--------------------------------------------------file upload progress--------------------
// Form is valid, save the form!
if (!empty($post['isAjax'])) {
return new JsonModel(array(
'status' => true,
'redirect' => $this->url()->fromRoute('upload-form
/success'),
'formData' => $album,
));
} else {
// Fallback for non-JS clients
return $this->redirect()->toRoute('upload-form
/success');
}
} else {
if (!empty($post['isAjax'])) {
// Send back failure information via JSON
return new JsonModel(array(
'status' => false,
'formErrors' => $form->getMessages(),
'formData' => $form->getData(),
));
}
$filter = new \Zend\Filter\File\RenameUpload("./public/photo/" . $album->username);
$filter->setUseUploadExtension(true);
$filter->setRandomize(true);
$filter->filter($data['fileupload']);
}
}
return array('form' => $form);
}
public function uploadProgressAction()
{
$id = $this->params()->fromQuery('id', null);
$progress = new \Zend\ProgressBar\Upload\SessionProgress();
return new \Zend\View\Model\JsonModel($progress->getProgress($id));
}
}
Model (Signup) :
namespace Application\Model;
use Zend\Http\PhpEnvironment\Request;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
use Zend\Validator;
class Signup implements InputFilterAwareInterface {
public $username;
public $fileupload;
protected $inputFilter;
public function exchangeArray($data) {
$this->username = (!empty($data['username'])) ? $data['username'] : null;
$this->fileupload = (!empty($data['fileupload'])) ? $data['fileupload'] : null;
}
public function getArrayCopy() {
return get_object_vars($this);
}
public function setInputFilter(InputFilterInterface $inputFilter) {
throw new \Exception("Not used");
}
public function getInputFilter($adapter = null) {
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$inputFilter->add(array(
'name' => 'fileupload',
'required' => true,
));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
View (index) :
<?php
$this->plugin('basePath')->setBasePath('/zendtest/public');
$title = 'Signup';
$this->headTitle($title);
?>
<h1><?php echo $this->escapeHtml($title); ?></h1>
<div class="signupform">
<?php
$form->prepare();
$form->setAttribute('action', $this->url('signup', array('action' => 'signup')));
$form->setAttribute('method', 'post');
$form->setAttribute('class', 'signform');
$form->setAttribute('enctype', 'multipart/form-data');
echo $this->form()->openTag($form);
$errmsg = $form->getMessages();
?>
<!-- ----------------------------------------username -->
<div class="signupformrow">
<?php
echo $this->formLabel($form->get('username'));
echo $this->formInput($form->get('username'));
if ($errmsg) {
if (isset($errmsg['username'])) {
foreach ($errmsg['username'] as $key => $value) {
?>
<span class="formerror">
<?php
if ($key == "isEmpty") {
echo"Username required";
} else {
echo $value;
}
?>
</span>
<?php
}
} else {
?>
<span class="formins"><img src="<?php echo $this->basePath('/images/tick.png'); ?>" /></span>
<?php
}
} else {
?>
<span class="formins">(Username must be 5-69 characters and alphanumeric only)</span>
<?php
}
?>
</div>
<!-- ----------------------------------------file -->
<?php echo $this->formFileSessionProgress(); ?>
<div class="signupformrow">
<?php
echo $this->formLabel($form->get('fileupload'));
echo $this->formFile($form->get('fileupload'));
if ($errmsg) {
if (isset($errmsg['fileupload'])) {
print_r($errmsg['fileupload']);
foreach ($errmsg['fileupload'] as $key => $value) {
?>
<span class="formerror">
<?php
if ($key == "isEmpty") {
echo'Photo required';
} else {
echo $value;
}
?>
</span>
<?php
}
}
}
?>
</div>
<!-- ----------------------------------------submit -->
<div class="signupformrow">
<label class="formlabel"></label>
<button>Submit</button>
</div>
<?php
echo $this->form()->closeTag();
?>
<!-- ---------------------------------------file upload progressbar-------------------------------------- -->
<div id="progress" class="help-block">
<div class="progress progress-info progress-striped">
<div class="bar"></div>
</div>
<p></p>
</div>
<script src="<?php echo $this->basePath('/js/jquery.min.js'); ?>"></script>
<script src="<?php echo $this->basePath('/js/jquery.form.js'); ?>"></script>
<script>
var progressInterval;
function getProgress() {
// Poll our controller action with the progress id
var url = '<?php echo $this->url('signup') ?>/upload-progress?id=' + $('#progress_key').val();
$.getJSON(url, function(data) {
if (data.status && !data.status.done) {
var value = Math.floor((data.status.current / data.status.total) * 100);
showProgress(value, 'Uploading...');
} else {
showProgress(100, 'Complete!');
clearInterval(progressInterval);
}
});
}
function startProgress() {
showProgress(0, 'Starting upload...');
progressInterval = setInterval(getProgress, 900);
}
function showProgress(amount, message) {
$('#progress').show();
$('#progress .bar').width(amount + '%');
$('#progress > p').html(message);
if (amount < 100) {
$('#progress .progress')
.addClass('progress-info active')
.removeClass('progress-success');
} else {
$('#progress .progress')
.removeClass('progress-info active')
.addClass('progress-success');
}
}
$(function() {
// Register a 'submit' event listener on the form to perform the AJAX POST
$('#signup').on('submit', function(e) {
e.preventDefault();
if ($('#fileupload').val() == '') {
// No files selected, abort
return;
}
// Perform the submit
//$.fn.ajaxSubmit.debug = true;
$(this).ajaxSubmit({
beforeSubmit: function(arr, $form, options) {
// Notify backend that submit is via ajax
arr.push({ name: "isAjax", value: "1" });
},
success: function (response, statusText, xhr, $form) {
clearInterval(progressInterval);
showProgress(100, 'Complete!');
// TODO: You'll need to do some custom logic here to handle a successful
// form post, and when the form is invalid with validation errors.
if (response.status) {
// TODO: Do something with a successful form post, like redirect
// window.location.replace(response.redirect);
} else {
// Clear the file input, otherwise the same file gets re-uploaded
// http://stackoverflow.com/a/1043969
var fileInput = $('#fileupload');
fileInput.replaceWith( fileInput.val('').clone( true ) );
// TODO: Do something with these errors
// showErrors(response.formErrors);
}
},
error: function(a, b, c) {
// NOTE: This callback is *not* called when the form is invalid.
// It is called when the browser is unable to initiate or complete the ajax submit.
// You will need to handle validation errors in the 'success' callback.
console.log(a, b, c);
}
});
// Start the progress polling
startProgress();
});
});
</script>
</div>
module.config :
<?php
return array(
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/zendtest/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'index',
),
),
),
'application' => array(
'type' => 'Literal',
'options' => array(
'route' => '/zendtest/application',
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Index',
'action' => 'index',
),
),
'may_terminate' => true,
'child_routes' => array(
'default' => array(
'type' => 'Segment',
'options' => array(
'route' => '/[:controller[/:action]]',
'constraints' => array(
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
),
'defaults' => array(
),
),
),
),
),
//===================================signup================================================================
'signup' => array(
'type' => 'segment',
'options' => array(
'route' => '/zendtest/application/signup[/][:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'signup',
),
),
),
), // routes end
), // router ends
'service_manager' => array(
'factories' => array(
'translator' => 'Zend\I18n\Translator\TranslatorServiceFactory',
),
),
'translator' => array(
'locale' => 'en_US',
'translation_file_patterns' => array(
array(
'type' => 'gettext',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.mo',
),
),
),
'controllers' => array(
'invokables' => array(
'Application\Controller\Index' => 'Application\Controller\IndexController'
),
),
'view_manager' => array(
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => array(
'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',
'application/index/index' => __DIR__ . '/../view/application/index/index.phtml',
'application/index/signup' => __DIR__ . '/../view/application/signup/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index' => __DIR__ . '/../view/error/index.phtml',
),
'template_path_stack' => array(
__DIR__ . '/../view',
),
),
);
我的注册页面在“/ zendtest / application / signup /”打开 .
当我点击提交按钮时,没有任何反应 .
Update :
如果我使用以下代码,请告诉我如何使用“Zend \ ProgressBar \ Upload \ UploadProgress”将文件上传进度条添加到我的表单 . 我应该如何更改我的控制器或模型或视图?
controller :
if ($form->isValid()) {
$album->exchangeArray($form->getData());
$album->act_code = Rand::getString(5);
$album->dkey = Rand::getString(5);
$this->getSignTable('Album\Model\AlbumTable\dbtable=user', 'userTable')->saveUser($album);
//==================================================file========================================
$filter = new \Zend\Filter\File\RenameUpload("./public/photo/" . $album->username);
$filter->setUseUploadExtension(true);
$filter->setRandomize(true);
$filter->filter($data['fileupload']);
}
}
model for filter and validation (Signup.php) :
$inputFilter->add(array(
'name' => 'fileupload',
'required' => true,
'validators' => array(
$fileValidator->attach(new \Zend\Validator\File\UploadFile(array(
'messages' => array(
\Zend\Validator\File\UploadFile::NO_FILE => 'Photo required',
),
)
), true)
),
));
view :
<?php echo $this->formFileSessionProgress(); ?>
<div class="signupformrow">
<?php
echo $this->formLabel($form->get('fileupload'));
echo $this->formFile($form->get('fileupload'));
if ($errmsg) {
if (isset($errmsg['fileupload'])) {
print_r($errmsg['fileupload']);
foreach ($errmsg['fileupload'] as $key => $value) {
?>
<span class="formerror">
<?php
if ($key == "isEmpty") {
echo'Photo required';
} else {
echo $value;
}
?>
</span>
<?php
}
}
}
?>
</div>
Update 2:
View :
<div class="signupform">
<?php
$form->prepare();
$form->setAttribute('action', $this->url('signup', array('action' => 'signup')));
$form->setAttribute('method', 'post');
$form->setAttribute('class', 'signform');
$form->setAttribute('id', 'signup');
$form->setAttribute('enctype', 'multipart/form-data');
echo $this->form()->openTag($form);
$errmsg = $form->getMessages();
?>
<!-- ----------------------------------------username -->
<div class="signupformrow">
<?php
echo $this->formLabel($form->get('username'));
echo $this->formInput($form->get('username'));
if ($errmsg) {
if (isset($errmsg['username'])) {
foreach ($errmsg['username'] as $key => $value) {
?>
<span class="formerror">
<?php
if ($key == "isEmpty") {
echo"Username required";
} else {
echo $value;
}
?>
</span>
<?php
}
} else {
?>
<span class="formins"><img src="<?php echo $this->basePath('/images/tick.png'); ?>" /></span>
<?php
}
} else {
?>
<span class="formins">(Username must be 5-69 characters and alphanumeric only)</span>
<?php
}
?>
</div>
<!-- ----------------------------------------file -->
<div class="signupformrow">
<?php
echo $this->formLabel($form->get('fileupload'));
?>
<div class="filediv">
<?php
echo $this->formFile($form->get('fileupload'));
?>
<a onclick="select_file()" class="pure-button upbutton">Choose an Image</a>
<!--image preview-->
<img id="upimg" name="upimg" src="" style="">
</div>
<?php
if ($errmsg) {
if (isset($errmsg['fileupload'])) {
foreach ($errmsg['fileupload'] as $key => $value) {
?>
<span class="formerror">
<?php
if ($key == "isEmpty") {
echo'Photo required';
} else {
echo $value;
}
?>
</span>
<?php
}
}
}
?>
</div>
<!-- ----------------------------------------submit -->
<div class="signupformrow">
<label class="formlabel"></label>
<?php
echo $this->formSubmit($form->get('submi'));
?>
</div>
<?php
echo $this->form()->closeTag();
?>
</div>
<!--progress bar-->
<div class="progress">
<div class="barrr"></div>
<div class="percenttt">0%</div>
</div>
<script src="<?php echo $this->basePath('/js/jquery.min.js'); ?>"></script>
<script src="<?php echo $this->basePath('/js/jquery.form.min.js'); ?>"></script>
<script type="text/javascript">
$(document).ready(function() {
/* variables */
var preview = $('#upimg');
var status = $('.status');
var percent = $('.percenttt');
var bar = $('.barrr');
/* only for image preview */
$("#fileupload").change(function(){
preview.fadeOut();
/* html FileRender Api */
var oFReader = new FileReader();
oFReader.readAsDataURL(document.getElementById("fileupload").files[0]);
oFReader.onload = function (oFREvent) {
preview.attr('src', oFREvent.target.result).fadeIn();
};
});
/* submit form with ajax request */
$('#signup').ajaxForm({
/* set data type json */
dataType:'json',
/* reset before submitting */
beforeSend: function() {
status.fadeOut();
bar.width('0%');
percent.html('0%');
},
/* progress bar call back*/
uploadProgress: function(event, position, total, percentComplete) {
var pVel = percentComplete + '%';
bar.width(pVel);
percent.html(pVel);
},
/* complete call back */
complete: function(data) {
preview.fadeOut(800);
status.html(data.responseJSON.status).fadeIn();
}
});
});
function select_file(){
document.getElementById('fileupload').click();
return false;
}
</script>
Form validation model(Signup.php):
class Signup implements InputFilterAwareInterface {
public $username;
public $fileupload;
protected $inputFilter;
protected $usernameValidator;
protected $fileValidator;
protected $adapter;
public function exchangeArray($data) {
$this->username = (!empty($data['username'])) ? $data['username'] : null;
$this->fileupload = (!empty($data['fileupload'])) ? $data['fileupload'] : null;
}
public function getArrayCopy() {
return get_object_vars($this);
}
public function setInputFilter(InputFilterInterface $inputFilter) {
throw new \Exception("Not used");
}
public function getInputFilter($adapter = null) {
$this->adapter = $adapter;
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$usernameValidator = new \Zend\Validator\ValidatorChain();
$fileValidator = new \Zend\Validator\ValidatorChain();
$inputFilter->add(array(
'name' => 'username',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
),
'validators' => array(
$usernameValidator->attach(
new \Zend\Validator\NotEmpty(array()), true)
->attach(new \Zend\Validator\Regex(array(
'pattern' => '/^[a-z]+[a-z0-9_]+$/',
'messages' => array(
\Zend\Validator\Regex::INVALID => 'Username is not valid',
\Zend\Validator\Regex::NOT_MATCH => 'Only small alphabet, digit and underscore are allowed. Username must start with an alphabet',
),
)), true)
)
));
$inputFilter->add(array(
'name' => 'fileupload',
'required' => true,
'validators' => array(
$fileValidator->attach(new \Zend\Validator\File\UploadFile(array(
'messages' => array(
\Zend\Validator\File\UploadFile::NO_FILE => 'Photo required',
),
)
), true)
),
));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
它在表单有效时有效,但如果表单有错误(假设'用户名'字段为空)则它不显示错误消息(应该来自模型'Signup.php')并且进度条仍然显示文件上传进度甚至实际上没有上传的文件 .
如果我从'index.phtml'剪切以下行并将它们添加到'layout.phtml'的'head'然后表单验证工作但文件上传进度条不起作用(表单提交像普通的PHP表单) . 但是,当选择图像文件时,jquery会显示图像 .
//index.phtml
<script src="<?php echo $this->basePath('/js/jquery.min.js'); ?>"></script>
<script src="<?php echo $this->basePath('/js/jquery.form.min.js'); ?>"></script>
//layout.phtml
<!-- Scripts -->
<?php echo $this->headScript()->prependFile($this->basePath('/js/html5.js'), 'text/javascript', array('conditional' => 'lt IE 9',))
->prependFile($this->basePath('/js/bootstrap.min.js'))
->prependFile($this->basePath('/js/jquery.min.js'))
->prependFile($this->basePath('/js/jquery.form.min.js')) ?>
</head>
3 回答
更新状态的JavaScript在哪里?除非有你没有发布的东西(或者我错过了),否则你实际上错过了重要的一块 .
您在参考的教程中提到了这一点:
你有三个选择:
1 - 继续刷新要求服务器进度的页面 - 基本上它返回一个页面说“上传10%完成....”等等,直到整个表单被提交,然后你处理和处理 .
2 - 更容易在iFrame中进行轮询/更新(因此请不断刷新iFrame)
3 - 使用javascript调用(通过JSON上传 - 您的视图返回JSON反对HTML并且您不需要 Headers )然后通过JavaScript更新屏幕 .
所以你需要的是将其中一种方法挂钩到你的表单中 .
Caveat:
话虽如此,您需要以正确的方式配置服务器 . 上传可以是基于会话,基于APC或上传进度模块 . 您可能会发现这些服务器上没有安装这些服务器以便最终部署 .
Better overall solution (possibly).
因此,无论如何,当你使用JS时,你也可以在现代浏览器中使用JS中的一个功能(IE9,Chrome,FF等,所以它可以保存在主要版本中) . 使用IE8它只是工作(没有进度指示器) . 我发现它是迄今为止更可靠的替代方案(如果您接受IE8用户获取上传功能而没有进度条) .
您可以在XHR对象的upload属性中添加事件侦听器 . 随着上传进度不断调用EventLister .
你可以将一个挂钩到每个文件(我在下面做的,你需要排队然后一次处理一个或两个,然后在完成时提交主表单)或者你可以挂钩到整个表单(一个进度条,一个处理程序服务器端=更简单,但不是很好的用户体验) .
为了提供帮助,这里是最近项目的一个片段,但网上有一些教程可以提供帮助 . 它与您拥有的不同,但并不多,部署和控制也更简单 . 它使用jQuery,但如果使用原始JS,理论(向xhr添加事件监听器)完全相同 .
我知道这是从你当前的方法转变 - 但我也使用你当前的方法,并发现这更好 .
jQuery代码段/示例
假设您安装了APC,您可以执行此tutorial显示的内容或使用Session upload progress选项我相信使用APC或会话上传进度是仅使用PHP获取真正上传仪表的唯一方法
另一个替代方案(实际上,我最喜欢的)是使用nginx上传模块nginx upload progress module .
因此,nginx负责上传|下载文件,PHP只负责插入/处理一些数据,JS只是轮询nginx处理程序来读取进度状态 .