修改php.ini以适应文件的要求:
//php.ini
file_uploads = On
post_max_size = 600M
upload_max_filesize = 600M
session.upload_progress.enabled = On
session.upload_progress.freq = "1%"
session.upload_progress.min_freq = "1"
以上我们限制了文件大小限制在不超过600MB。
关于文件上传进度的获取方式,zf2提供了三个类:
Zend\Progressbar\Upload\ApcProgress
Zend\ProgressBar\Upload\SessionProgress
Zend\Progressbar\Upload\UploadProgress
其中SessionProcess要求php5.4以上,本文以SessionProcess为例,其他2个使用方法一样。
首先创建文件上传的From:
//Test/Form/TestForm.php
<?php
namespace Test\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\Validator\File\Extension;
use Zend\Validator\File\Size;
use Zend\Validator\File\ImageSize;
use Zend\Validator\File\MimeType;
use Zend\Filter\File\RenameUpload;
class TestForm extends Form implements InputFilterProviderInterface{
protected $inputFilter;
public function __construct($name = null) {
parent::__construct($name?$name:'test');
//添加一个文本框
$this->add(array(
'name' => 'userName',
'type' => 'Text',
'options' => array(
'label' => 'name:',
),
'attributes' => array(
'size' => 60,
'maxlength' => 100,
),
));
//添加文件上传input
$this->add(array(
'name' => 'u',
'type' => 'File',
'options' => array(
'label' => 'file:',
),
'attributes' => array(
'id' => 'u_e',
),
));
//提交按钮
$this->add(array(
'name' => 'send',
'type' => 'Submit',
'attributes' => array(
'value' => 'ok',
),
));
}
public function getInputFilterSpecification() {
//文件上传后保存到临时目录的filter
$renameUpload = new RenameUpload(array(
"target" => __DIR__."/../../../../../uploads",
"randomize" => true,
"use_upload_name" => true,
));
//验证文件扩展名
$extention = new Extension('png,jpeg,jpg,bmp,gif,mp4');
//验证文件大小
$size = new Size(array('min'=>'1kB', 'max'=>'600MB'));
//验证文件MIME类型
$mime = new MimeType(array('image/png','image/jpeg','image/jpg','image/bmp','image/gif','video/mp4','enableHeaderCheck' => true));
return array(
'userName' => array(
'required' => true,
'filters' => array(
array('name' => 'StringTrim'),
array('name' => 'StringToLower'),
),
'validators' => array(
array('name' => 'NotEmpty'),
array('name' => 'Alnum'),
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 3,
'max' => 256,
),
),
array(
'name' => 'Db\NoRecordExists',
'options' => array(
'table' => 'user',
'field' => 'name',
'adapter' => $this->adapter,
),
),
),
),
'u' => array(
'required' => true,
'filters' => array(
$renameUpload,
),
'validators' => array(
$extention,
$size,
//$imgSize,
$mime,
),
),
);
}
}
然后创建view:
//Test/view/test/test/testform.phtml
<?php
$this->tform->setAttribute('action', $this->url('test', array('action' => 'testform')));
$this->tform->setAttribute('method', 'POST');
$this->tform->prepare();
echo "<div class='tf'>";
echo '<div>'.$this->form()->openTag($this->tform).'</div>';
//获取文件上传进度必须要使用该view helper,formFileSessionProgress会创建一个id为progress_key的hidden的input记录文件上传进度获取需要的id
//SessionProgress对应formFileSessionProgress,ApcProgress对应formFileApcProgress,UploadProgress对应formFileUploadProgress
echo $this->formFileSessionProgress();
//文本框
echo '<div>'.$this->formRow($this->tform->get('userName')).'</div>';
//文件input
//存在这样的情况:用户第一次提交后,文件上传了并检验通过了,但是文本框检验不过,此时,不需要用户重新再上传一次文件,所以,在此情况下将文件input隐藏起来
echo '<div '.($this->tempFile?'style="display:none"':'').'>'.$this->formRow($this->tform->get('u')).'</div>';
if ($this->tempFile){
echo '<div>'.$this->tempFile['name'].'</div>';
}else{
//上传进度显示
echo '<div><span style="display:block; float:left;">process:</span><span id="proc" style="display:block; margin-left:60px; width:60px; height: 30px; background-color:
#dddddd;"></span></div>';
}
echo '<div>'.$this->formRow($this->tform->get('send')).'</div>';
echo '<div>'.$this->form()->closeTag().'</div>';
echo "</div>";
echo "<hr />";
?>
<!-- 以下脚本用于定时获取并显示上传进度 -->
<script>
var progressInterval;
function getProgress() {
var ul = '/test/uploadprogress?id='+$("#progress_key").val();//上传进度通过控制器test/uploadprogress获取
$.ajax({
type : "GET",
url : ul,
dataType : "json",
success : function(data, textStatus) {
if (!data.done) {
var value = Math.floor((data.current / data.total) * 100);
showProgress(value);
} else {
showProgress(100);
clearInterval(progressInterval);
}
},
error : function(r, t, r){
}
});
}
function startProgress() {
showProgress(0);
progressInterval = setInterval(getProgress, 400);
}
function showProgress(amount) {
$('#proc').html(amount+"%");
}
$(document).ready(
function(){
$('#test').submit(startProgress);
}
);
</script>
最后是控制器:
//Test/Controller/TestController.php
<?php
namespace Test\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Session\Storage\SessionArrayStorage;
use Zend\ProgressBar\Upload\SessionProgress;
use Zend\View\Model\JsonModel;
use Test\Form\TestForm;
class TestController extends AbstractActionController {
public function testformAction() {
$form = new TestForm();
$inputFilter = $form->getInputFilter();
$sessionKey = 'uploadedTempFile';
$sessionStorage = new SessionArrayStorage();//session存储对象,文件上传成功并检验成功后,将信息保存进session,避免用户再次重新上传文件
$tempFile = null;
if ($sessionStorage->offsetExists($sessionKey)){
$tempFile = $sessionStorage->offsetGet($sessionKey);
}
if ($this->getRequest()->isPost()){//用户提交了表单,处理表单数据
$postData = array_merge_recursive(//文件信息合并到postdata数组
$this->getRequest()->getPost()->toArray(),
$this->getRequest()->getFiles()->toArray()
);
if (isset($tempFile)) {//本次提交不是首次提交,并且session中有用户上传的文件的缓存数据
if (!empty($postData['u']['name'])){//用户重新上传了文件,则原来缓存的文件要清除
$sessionStorage->offsetUnset($sessionKey);
$tempFile = null;
}else{//用户没有上传新文件,所以,检验表单时不检验file元素
$inputFilter->get('u')->setRequired(false);
}
}
$form->setData($postData);
if ($form->isValid()) {//表单检验成功
$data = $form->getData();
if (empty($data['u']) && $tempFile){//用户没有上传新文件,所以,使用session中缓存的文件
$data['u'] = $tempFile;
$fileName = $data['u']['name'];
$fileMIMEType = $data['u']['type'];
$tmpFilePathName = $data['u']['tmp_name'];
$fileSize = $data['u']['size'];
}
//保存表单
//.......
//$this->redirect()->toRoute(
} else {//表单元素检验失败
$data = $form->getData();
$fileErrors = $form->get('u')->getMessages();
if (empty($fileErrors) && isset($data['u']['error'])
&& $data['u']['error'] === UPLOAD_ERR_OK
) {
$tempFile = $data['u'];
$sessionStorage->offsetSet($sessionKey, $tempFile);
}
}
} else {//不是用户提交的表单,清空session中的缓存数据
if ($sessionStorage->offsetExists($sessionKey)){
$sessionStorage->offsetUnset($sessionKey);
}
$tempFile = null;
}
return array(
'tform' => $form,
'tempFile' => $tempFile,
);
}
//获取上传进度的action
public function uploadprogressAction()
{
$id = $this->params()->fromQuery('id', null);
$progress = new SessionProgress();
return new JsonModel($progress->getProgress($id));
}
}
由于上传过程使用了session,因此,需要添加session的启动代码,以保证刷新页面的时候不会出现session错误。
session的参数配置:
// /config/autoload/local.php
return array(
'session' => array(
'config' => array(
'class' => 'Zend\Session\Config\SessionConfig',
'options' => array(
'name' => 'zf2ttttt',
),
),
'storage' => 'Zend\Session\Storage\SessionArrayStorage',
'validators' => array(
'Zend\Session\Validator\RemoteAddr',
'Zend\Session\Validator\HttpUserAgent',
),
),
);
Application启动后调用session_start:
// /module/Application/Module.php
namespace Application;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
use Zend\Session\SessionManager;
use Zend\Session\Container;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
//为表单验证对象设置默认的语言翻译器,以便验证码验证不通过时用默认语言提示用户
\Zend\Validator\AbstractValidator::setDefaultTranslator($e->getApplication()->getServiceManager()->get('translator'));
//启动session
$this->bootstrapSession($e);
}
public function bootstrapSession($e){
$session = $e->getApplication()->getServiceManager()->get('Zend\Session\SessionManager');
$session->start();
}
//SessionManager工厂
public function getServiceConfig() {
return array(
'factories' => array(
'Zend\Session\SessionManager' => function ($sm) {
$config = $sm->get('config');
if (isset($config['session'])){
$session = $config['session'];
$sessionConfig = null;
if (isset($session['config'])){
$class = isset($session['config']['class']) ? $session['config']['class'] : 'Zend\Session\Config\SessionConfig';
$options = isset($session['config']['options']) ? $session['config']['options'] : array();
$sessionConfig = new $class;
$sessionConfig->setOptions($options);
}
$sessionStorage = null;
if (isset($session['storage'])){
$class = $session['storage'];
$sessionStorage = new $class;
}
$sessionSaveHandler = null;
if (isset($session['save_handler'])){
$sessionSaveHandler = $sm->get($session['save_handler']);
}
$sessionManager = new SessionManager($sessionConfig, $sessionStorage, $sessionSaveHandler);
} else {
$sessionManager = new SessionManager();
}
Container::setDefaultManager($sessionManager);
return $sessionManager;
},
),
);
}
}
另外,貌似zf2.3版本中文件上传validator的消息字符串的中文翻译在语言文件/vendor/ZF2/resources/languages/zh/Zend_Validate.php中不存在,需要自己添加下:
/vendor/ZF2/resources/languages/zh/Zend_Validate.php
return array(
......
"File has an incorrect extension" => "文件扩展名不正确",
"Maximum allowed size for file is '%max%' but '%size%' detected" => "值“%size%”超过最小值“%max%”",
"Minimum expected size for file is '%min%' but '%size%' detected" => "值“%size%”超过最小值“%min%”",
"File has an incorrect mimetype of '%type%'" => "文件mime类型不正确“%type%”",
"The mimetype could not be detected from the file" => "文件中检测不到mime类型",
"File is not readable or does not exist" => "文件不可读或者不存在",
'File was not uploaded' => "没有上传文件"
);