实现一个功能:提交表单的时候,需要上传视频,把视频上传到oss上,然后把url作为表单值传到后端保存到数据库。需要ajax异步实现。
遇到了一个这样报错:Bad Request: 您提交的数据无法被验证。 这是因为form表单 里没有添加 ID属性,添加一个即可。
- 页面表单
<form class="auto-form" method="post" id="art_form" action="<?= Yii::$app->urlManager->createAbsoluteUrl(['mch/course/coursedir/add']);?>">
<div class="form-group row"> <div class="form-group-label col-sm-2 text-right"> <label class="col-form-label required">上传完整视频</label> </div> <div class="col-sm-6"> <input type="file" name="video" value=""/> <input type="text" name="video_url" value=""/> <img id="video" src="" alt="325×325" style="325px;height:325px;"> </div>
</div> //这是表单的一部分,要注意的是需要在<form id="art_form">code...</form>添加一个ID属性值,配合formData() - ajax 发送数据(使用jquery)
//上传视频, 这个url在yii里的写法是 index.php?r=moduleID/controllerID/actionID (写完整路径报错) url = 'index.php?r=mch/course/coursedir/upload-video'; var video = $('input[name="video"]');var csrf = $('input[name="_csrf"]').val(); //csrf 不传也没有报错,好像是不传也可以(不知道会不会有安全隐患,表单里是传了csrf的)这里没用 video.click(function () { video.change(function () { var obj = this; var formData = new FormData($('#art_form')[0]); //ajax 上传文件的时候,需要使用formData(),不要丢掉下标0 $.ajax({ type: 'post', url: url+'&fileName=video', //fileName 参数是后端处理文件需要使用的 data: formData, // 数据 //dataType: 'json', processData: false, contentType: false, //设置为false success: function (data) { if(data.status == 0){ //success $('input[name="video_url"]').val(data.url); $('#video').attr('src', data.url); $(obj).off('change'); }else{ //fail } }, error: function (err) { } }) }) })
- 服务器端处理文件并上传到oss里
/** * 上传视频到oss,ajax返回视频url给页面
* ajax传的fileName
* 注意: 上传视频和上传图片不同, 需要修改php.ini文件里的upload_max_filesize 和 post_max_filesize 参数,默认好像只有几M ,肯定是不够的
* 修改PHP上传文件大小限制的一个是nginx的client_max_body_size,还有一个是php.ini的限制。(包括upload_max_filesize, post_max_size, memory_limit)
* $oldFile 参数是用来修改表单的时候,删除原来文件的一个标示, 如果传此参数, 则表示修改了上传文件, 需要删除原文件,在修改的表单的视图中先获取url然后通过ajax传一个oldFile参数
* 上传视频MP4对应的mime类型是video/MP4 上传音频MP3 对应的类型是audio/MP3 上网查的是audio/mpeg 但是用这个就会报错上传文件类型不对,使用audio/MP3是对的
* 上传的时候, 如果在network中看到报错 413 Request Entity Too Large , 因为 nginx 默认的上传限制较小,不支持大文件上传, 所以需要修改nginx的配置文件, 在nginx.conf或者对应项目的虚拟主机配置文件里加上 client_max_body_size = 40m;
* 如果还小,还可以在=再设置大一点 */ public function actionUploadVideo($fileName, $oldFile='', $arr = ['video/mp4', 'audio/mp3'], $fileSize=10000000) { //判断文件是否合法 $file = $_FILES[$fileName]; if($file['error']>0){ switch($file['error']){ case 1: case 2: return '文件过大'; case 3: return '上传文件不完整,请重新上传'; case 4: return '请选择文件'; case 6: return 'tmp不存在'; case 7: return '没有权限'; case 8: return '........'; } } if(!in_array($file['type'], $arr)){ return '上传文件类型不合法'; } if($file['size']>$fileSize){ return '上传文件过大'; } $suffix = strrchr($file['name'], '.'); $localdir = $file['tmp_name']; // 直接上传到oss 就不需要把文件从临时目录移动到服务器的目录里,直接从临时目录上传到oss 即可 //引入ossSDK include Yii::$app->basePath.'/extensions/aliyun-oss-php-sdk-2.2.4/autoload.php'; //把视频上传到oss上 try{ $access_key = 'LTAI9c666666'; $secret_key = 'f3zkCC666666'; $domain = 'http://oss-cn-shenzhen.aliyuncs.com'; $bucket = 'qa-xingzuo'; $object = md5(time().uniqid()).$suffix; //文件名,要先获取上传的文件后缀 $ossClient = new OssClient($access_key, $secret_key, $domain); $exist = $ossClient->doesObjectExist($bucket, $object); //判断oss上是否已经存在要上传的文件
if($oldFile != ''){
$oldObject = trim(strrchr($oldFile, '/'), '/');
$ossClient->deleteObject($bucket, $oldObject); //删除原来的文件
} //$ossClient->deleteObject($bucket, $object); exit;//删除文件 if(!$exist){ $ossClient->uploadFile($bucket, $object, $localdir); } //unlink($localdir); //删除本地文件, 后期可以优化一下 $domain = explode('//', $domain); $url = 'http://'.$bucket.'.'.$domain[1].'/'.$object; //拼接访问路径,oss固定的访问格式 $data = [ 'status' => 0, 'msg' => 'success', 'url' => $url, ]; return $data; } catch(OssException $e) { printf(__FUNCTION__ . ": FAILED "); printf($e->getMessage() . " "); return; } } - 特别说明: 今天把项目迁移到新服务器上之后, 突然发现ajax上传视频不能成功, 打印上传之后的$_FILE 数组为null , 找了半天原因, 主要排查以下几点:
- 临时目录是不是没有读写权限, 特意改了一下upload_tmp_dir 并给了777 权限, 还是不行, 证明不是这个原因, 而且在旧服务器上, 根本没有设置upload_tmp_dir. 是注释掉的.
- 是不是upload_max_filesize和post_max_size设置的大小太小, 改成40m 还是不行(这个php.ini是通过php -ini命令显示的路径找的, /etc/php/7.0/cli/php.ini) 修改40m 还是不行 ,包括memory_limit 都看了没问题, nginx的client_max_body_size 设置的是100m 没问题
- 甚至看了网上说nginx转发的URL必须设置proxy_set_header才能使用ajax上传数据, 于是改
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 增加了这三行, 但----还是不行 (又被我删除了) 实际上只有proxy_pass 转发就OK了(ps: 迁移服务器是吧原来的域名nginx转发了, 目的是避免前端修改接口)
- 还有网上看到在linux环境下,php5.3.3以前php-fpm还没有被php收录,配置php基本都在php.ini里面,php5.3.3及以后,除了在php.ini配置以外,还可以在php-fpm.conf里面配置,而php-fpm.conf优先级比php.ini高。但是php-fpm.conf里并没有发现配置文件上传的相关参数, 不过 突然想到fpm目录下的php.ini文件, 尝试修改了这个文件, 发现 成功了. 旧服务器上没有fpm目录, 新服务器上的php-fpm是单独安装的, 所以可能是这个原因导致多了一个fpm目录, 然后 cli/php.ini的配置是针对命令行访问PHP项目的,fpm/php.ini是针对浏览器访问PHP项目的. /etc/php/7.0/目录下什么情况下会有fpm目录, 什么情况下没有fpm目录, 有待研究啊. (盲猜是因为单独安装了php-fpm的原因,待我有时间研究一下), 所以php -ini 显示的php.ini 不一定就是程序使用的啊!!! (也是借助了nginx的错误日志, 发现原因就是因为文件上传大小没有修改成功)