我在文件上传进度方面遇到了一些问题。我在xampp,windows 7上使用了zend framework 2.2。
表单(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',
),
),
));
}
}
控制器(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));
}
}
型号(注册):
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;
}
}
查看(索引):
<?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 /".
打开当我点击提交按钮时,没有任何反应。
更新:
如果我使用以下代码,请告诉我如何使用“Zend \ ProgressBar \ Upload \ UploadProgress”将文件上传进度条添加到我的表单。我应该如何更改我的控制器或模型或视图?
控制器:
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']);
}
}
过滤和验证模型(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)
),
));
查看:
<?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>
更新2:
查看:
<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>
<br />
<!--image preview-->
<img id="upimg" name="upimg" src="" style="">
<br />
</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>
表单验证模型(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;
}
}
它在表单有效时有效,但如果表单有错误(假设'username'字段为空)则它不会显示错误消息(应该来自模型'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>
答案 0 :(得分:1)
哪里有更新状态的JavaScript?除非你没有发布(或者我已经错过),否则你实际上错过了重要的一块。
您在参考的教程中提到了这一点:
有一些不同的获取进度信息的方法 浏览器(长期与短期轮询)。在这里,我们将使用短轮询 因为它更简单,对服务器资源的负担更少,但保留 记住它不像长时间的民意调查那样敏感。
当我们的表单通过AJAX提交时,浏览器将持续进行 轮询服务器以获取上传进度。
您有三种选择:
1 - 继续刷新要求服务器进度的页面 - 基本上它会返回一个页面说“&#34;上传10%完成....&#34;等等,直到提交整个表格,然后处理和处理。
2 - 更容易在iFrame中进行轮询/更新(因此请不断刷新iFrame)
3 - 使用javascript调用(通过JSON上传 - 您的视图返回JSON反对HTML并且您不需要标题)然后通过JavaScript更新屏幕。
所以你需要的是将其中一个方法挂钩到你的表单中。
<强>警告:强>
话虽如此,您需要以正确的方式配置服务器。上传可以是基于会话,基于APC或上传进度模块。您可能会发现这些服务器上没有安装这些服务器以便最终部署。
更好的整体解决方案(可能)。
所以,当你正在使用JS时,你也可以在现代浏览器中使用JS中的一个功能(IE9 +,Chrome,FF等,所以它可以保存在主体中)。使用IE8它只是工作(没有进度指示器)。我发现它是迄今为止更可靠的替代方案(如果您接受IE8用户获取上传功能而没有进度条)。
您可以在XHR对象的upload属性中添加事件侦听器。随着上传进度不断调用EventLister。
你可以将一个挂钩到每个文件(我在下面做了什么,你需要排队然后一次处理一个或两个,然后在完成后提交主表单)或者你可以挂钩到整个表单(一个进度条,一个处理程序服务器端=更简单,但不是很好的用户体验)。
为了提供帮助,这是最近项目的一个片段,但网上有一些教程可以提供帮助。它与您所拥有的不同,但并不多,而且部署和控制也更加简单。它使用jQuery,但如果使用原始JS,理论(将事件监听器添加到xhr)完全相同。
我知道它与您当前的方法有所不同 - 但我也使用了您当前的方法并发现它更好。
jQuery代码段/示例
$.ajax({
xhr: function() {
var xhrobj = $.ajaxSettings.xhr();
if (xhrobj.upload) {
xhrobj.upload.addEventListener('progress', function(event) {
var percent = 0;
var position = event.loaded || event.position;
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
//Set progress
$('#upload_' + vr_admin.currentlyUploadIndex + ' .imageProgress .imageProgressBar').css({width: percent * 2});
}, false);
}
return xhrobj;
},
url: upload.php',
type: "POST",
contentType:false,
processData: false,
cache: false,
data: {data fields god here},
success: function(response){
alert ('UPloaded');
}
}
答案 1 :(得分:0)
确保您安装了APC,您可以执行tutorial显示的内容或使用Session upload progress选项我认为使用APC或会话上传进度是仅使用真正的上传计量表的方法PHP
答案 2 :(得分:0)
另一个替代方案(实际上,我最喜欢的)是使用nginx上传模块+ nginx upload progress module。
因此,nginx负责上传|下载文件,PHP只负责插入/处理一些数据,JS只是轮询nginx处理程序来读取进度状态。