我们知道淘宝,京东商品都是有视频的,针对BBC产品改造实现视频上传功能,七牛适用。
一、前台视频上传 /data/www/product/app/topshop/view/item/edit.html
html部分:
<div class="form-group">
<label for="" class="col-sm-2 control-label">上传视频:</label>
<div class="col-sm-10">
<div class="video-input">
<{if $item.video_default_id}>
<a class="select-image" data-toggle="modal" href="javascript:void;" data-target="#gallery_modal"><input type="hidden" name="video_default_id" value="<{$item.video_default_id}>"><div class="img-put"><video src="<{$item.video_default_id}>" width="100" height="100" controls="controls"><source src="<{$item.video_default_id}>" type=""></video></div></a>
<{/if}>
</div>
<div class="multiple-upload pro-thumb video-upload" >
<input type="file" class="video-file-input" name="video" data-size="10540000" data-ismodal="true" multiple="multiple" data-remote="<{url action=toputil_ctl_image@uploadVideos from=shop}>" accept="" data-type="item">
<div class="btn btn-default text-blue act-gallery action-upload"><i class="glyphicon glyphicon-open"></i>上传视频</div>
</div>
</div>
</div>
js部分:
$('.video-upload').on('click', '.action-upload', function(e) {
var file_input = $('.video-file-input')[0];
var container = $('.video-file-input').parent();
// var handle = container.find(options.handle);
var type = file_input.getAttribute('data-type') ? file_input.getAttribute('data-type') : 'video';
var imgCatId = $('.folder-info-item[data-open-state="1"]').attr('data-img-catid');
var fileName = file_input.getAttribute('data-filename') ? file_input.getAttribute('data-filename') : 'upload_files';
var size = file_input.getAttribute('data-size') ? file_input.getAttribute('data-size') : 10540000;
size = Number(size) || options.size;
var insertWhere = container.find(file_input.getAttribute('data-insertwhere') || '.image-box');
var multiple = file_input.multiple;
var isModal = file_input.getAttribute('data-ismodal');
var url = file_input.getAttribute('data-remote')+'&type='+type+'&image_cat_id='+imgCatId;
var inputName = file_input.getAttribute('name');
var callback = file_input.getAttribute('data-callback');
var data = new FormData();
var files = file_input.files;
if (!files || !Array.prototype.slice.call(files).every(function(file, i) {
if (file.size > size) {
$('#messagebox').message('上传视频 "' + file.name + '" 不能大于' + size / 1024 / 1024 + 'M!');
return false;
}
if (multiple) {
data.append(fileName + '[]', file);
} else {
data.append(fileName, file);
}
return true;
})) return false;
$.ajax({
url: url,
type: 'POST',
data: data,
cache: false,
contentType: false,
processData: false,
success: function(rs) {
try {
rs = JSON.parse(rs);
} catch (e) {}
if (rs.success && rs.data) {
if (callback) {
return callback(rs);
}
var html = '';
$.each(rs.data, function(k, data) {
if (data.url) {
html += '<a class="select-image" data-toggle="modal" href="javascript:void;" data-target="#gallery_modal"><input type="hidden" name="video_default_id" value="' + data.url + '"><div class="img-put"><video src="' + data.url + '" width="100" height="100" controls="controls"><source src="' + data.url + '" type="video/mov"></video></div></a>';
}
});
$('.video-input').html(html);
} else if (rs.message) {
$('#messagebox').message(rs.message);
return false;
}
},
error: function() {
$('#messagebox').message('上传出错,请重试');
}
});
});
文件上传需ajax请求,form表单提交file值后台接受不到。。。
效果:
二、图片上传: /data/www/product/app/toputil/controller/image.php
添加uploadVideos方法,内容同uploadImages,需将
$objLibImage->rebuild($imageData['ident'], $imageType);
注释(打开也不报错,只会生成几个无用的文件)
三、视频配置项: /data/www/product/config/image.php
/**
|--------------------------------------------------------------------------
| 视频支持的格式
| 暂时只支持 'avi', 'mov', 'rmvb', 'flv','mp4','flash','3gp'
|--------------------------------------------------------------------------
*/
'video_support_filetype' => ['avi', 'mov', 'rmvb', 'flv','mp4','flash','3gp'],
//图片上传文件最大限制,如果上传大小超出PHP.ini配置,请修改PHP.ini
'uploadedVideoMaxSize' => 1024*1024*10,// 10M
四、实现图片上传:新建 /data/www/product/app/image/lib/data/video.php
<?php
use Symfony\Component\HttpFoundation\File\UploadedFile;
class image_data_video {
/**
* 上传视频商家ID
*/
public $sellerId = null;
/**
* 上传视频的店铺ID
*/
public $shopId = null;
/**
* 上传视频的分类ID
*/
public $imageCatId = null;
public function __construct()
{
$this->objMdlImage = app::get('image')->model('images');
}
//上传视频支持的后缀
public function getImageSupportFiletype()
{
return config::get('image.video_support_filetype');
}
private function __getImageParams( $file )
{
$imageParams = getimagesize($file);
$data['width'] = $imageParams[0];
$data['height'] = $imageParams[1];
$data['type'] = $imageParams[2];
return $data;
}
private function __checkImage( $fileObject )
{
$extension = $fileObject->getClientOriginalExtension();
if( !in_array(strtolower($extension), $this->getImageSupportFiletype()) )
{
throw new \LogicException(app::get('image')->_('不支持该视频格式'));
}
//检查上传视频大小限制
$this->__checkUploadedFileMaxSize($fileObject);
$imageName = $fileObject->getClientOriginalName();
if( strlen($imageName) > 200 )
{
throw new \LogicException(app::get('image')->_('视频文件名称过长,名称不能超过200个字符'));
}
return true;
}
private function __checkUploadedFileMaxSize($fileObject)
{
$phpIniMax = $fileObject->getMaxFilesize();
$maxFilesize = config::get('image.uploadedVideoMaxSize');
$maxFilesize = $maxFilesize > $phpIniMax ? $phpIniMax : $maxFilesize;
if( $fileObject->getError() == UPLOAD_ERR_INI_SIZE ||//上传文件大小限制
$fileObject->getError() == UPLOAD_ERR_FORM_SIZE ||//POST上传大小表单限制
$maxFilesize < $fileObject->getClientSize()
)
{
throw new \LogicException(app::get('image')->_('超出限制,上传视频不能超过'.format_filesize($maxFilesize)));
}
}
private function __preFileObject( $fileObject )
{
if(substr($fileObject,0,4) == 'http' )
{
$fileObject = $this->__getNetworkImage($fileObject);
}
elseif( !is_object($fileObject) )
{
if( file_exists($fileObject) )
{
$imageParams = pathinfo($fileObject);
$file = tempnam(TMP_DIR,'tmpImage');
kernel::single('base_filesystem')->copy($fileObject, $file);
$size = filesize($file);
$imageName = substr(strrchr($fileObject,'/'),1);
$fileObject = new UploadedFile($file, $imageName, $imageParams['extension'], $size, 0, true);
}
else
{
$fileObject = new UploadedFile($fileObject['tmp_name'], $fileObject['name'], $fileObject['type'], $fileObject['size'], $fileObject['error']);
}
}
return $fileObject;
}
/**
* 商家:检查上传视频指定的视频类型和分类是否正确
*/
public function setImageCatId($imageCatId, $imageType)
{
if( !$this->shopId )
{
throw new \LogicException(app::get('image')->_('上传失败,店铺不存在'));
}
$filter = [
'image_cat_id'=>$imageCatId,
'img_type' => $imageType,
'shop_id'=>$this->shopId,
];
$objMdlImageCat = app::get('image')->model('image_cat');
if( !$objMdlImageCat->count($filter) )
{
throw new \LogicException(app::get('image')->_('上传指定的视频类型子分类不存在'));
}
$this->imageCatId = $imageCatId;
return $this;
}
/**
* 存储视频接口
*
* @param object $fileObject 继承SplFileInfo封装的类
* @param string $from 上传视频用户类型
*/
public function store( $fileObject, $from, $imageType, $test=false)
{
$fileObject = $this->__preFileObject( $fileObject );
$this->__checkImage($fileObject);
$file = $fileObject->getRealPath();
$imageParams = $this->__getImageParams($file);
$params['width'] = $imageParams['width'];
$params['height'] = $imageParams['height'];
$params['size'] = $fileObject->getClientSize();
$params['image_name'] = htmlspecialchars(specialutils::filterInput($fileObject->getClientOriginalName()),ENT_QUOTES);
if( empty($imageType) )
{
throw new \LogicException(app::get('image')->_('上传视频失败,上传视频类型不能为空'));
}
$params['image_cat_id'] = $this->imageCatId ? $this->imageCatId : 0;
$params['img_type'] = $imageType;
$params['last_modified'] = time();
$storager = kernel::single('base_storager');
$result = $storager->upload($fileObject);
list($url,$ident,$storage) = explode('|', $result);
$params['url'] = kernel::get_host_mirror_img().$url;
$params['ident'] = $ident;
$params['storage'] = $storage;
$accountData = $this->__imageAttach($test);
$params['target_id'] = $accountData['target_id'];
$params['target_type'] = $accountData['target_type'];
$params['disabled'] = 0;
$params['file_type'] = $fileObject->getClientOriginalExtension();
if( !in_array($imageType, config::get('image.image_type.'.$params['target_type'])) )
{
throw new \LogicException(app::get('image')->_('上传视频失败,上传视频类型错误'));
}
if( $row = $this->objMdlImage->getRow('id',['url'=>$params['url'],'target_id'=>$params['target_id'],['target_type'=>$params['target_type']]]) )
{
$this->objMdlImage->update($params, ['id'=>$row['id']]);
}
else
{
$this->objMdlImage->insert($params);
}
unlink($file);
return $params;
}
/**
* 设置上传视频的账号ID和类型
*
* @param string $from 上传视频用户类型
* @param string $from 上传视频用户类型
*/
public function setUploadImageAccount($from, $id)
{
$this->targetId = $id;
$this->targetType = $from;
if( !$this->targetId )
{
throw new \LogicException(app::get('image')->_('无上传视频权限'));
}
return $this;
}
public function setUploadShopId($shopId = null)
{
$this->shopId = $shopId;
}
/**
* 视频ID,关联上用户类型ID
*
* @param string $from 上传视频用户类型
*/
private function __imageAttach($test=false)
{
if( $this->targetId && $this->targetType )
{
if( $this->shopId )
{
$data['target_id'] = $this->shopId;
}
else
{
$data['target_id'] = $this->targetId;
}
$data['target_type'] = $this->targetType;
}
else
{
pamAccount::setAuthType('desktop');
$data['target_id'] = pamAccount::getAccountId();
$data['target_type'] = 'admin';
}
if( !$data['target_id'] && !$test )
{
throw new \LogicException(app::get('image')->_('无上传视频权限'));
}
return $data;
}
public function getImageTypeSize( $imageType )
{
if( empty($imageType) ) return false;
$imageSetting = config::get('image.image_setting');
$size = $imageSetting['normal']['size'];
foreach( $imageSetting as $k=>$row )
{
if( $row['image_type'] && in_array($imageType, $row['image_type']) )
{
$size = $row['size'];
}
}
if( !$size ) return false;
$setImageSize = config::get('image.image_setting_set');
if( in_array( $imageType, $setImageSize) )
{
$setSize = $this->getImageSetting($imageType);
}
if( !$setSize )
{
$setSize = config::get('image.image_default_set');
}
foreach( $size as $s )
{
$res[$s]['width'] = $setSize[$s]['width'];
$res[$s]['height'] = $setSize[$s]['height'];
$res[$s]['title'] = $setSize[$s]['title'];
}
return $res;
}
public function setImageSetting($imageType, $value)
{
return app::get('image')->setConf('image.set.'.$imageType, $value);
}
public function getImageSetting($imageType)
{
return app::get('image')->getConf('image.set.'.$imageType);
}
public function setImageDefault($url)
{
return app::get('image')->setConf('image.set.defaultPic', $url);
}
public function getImageDefault()
{
return app::get('image')->getConf('image.set.defaultPic');
}
/**
* 商品视频相册视频生成
*
* @param $ident 需要生成相册视频唯一值
* @param $sizes 生成视频大小
*
* @return bool
*/
public function rebuild($ident, $imageType, $sizes = 0 )
{
if( !$sizes )
{
$sizes = $this->getImageTypeSize($imageType);
}
$storager = kernel::single('base_storager');
$orgFile = $storager->getFile($ident);
if( !file_exists($orgFile) || !$sizes ) return true;
foreach($sizes as $s=>$value)
{
$tmpTarget = tempnam(TMP_DIR,'img');
$w = $value['width'];
$h = $value['height'];
$orgFileSize = getimagesize($orgFile);
if( $orgFileSize['0'] < $w )
{
$w = $orgFileSize['0'];
}
if( $orgFileSize['1'] < $h )
{
$h = $orgFileSize['1'];
}
image_clip::image_resize($orgFile, $tmpTarget, $w, $h);
$imageParams = getimagesize($tmpTarget);
$size = filesize($tmpTarget);
$fileObject = new UploadedFile($tmpTarget, $images['image_name'], $imageParams['mime'], $size, 0, true);
$storager->rebuild($fileObject, strtolower($s), $ident);
unlink($tmpTarget);
}
return true;
}
/**
* 存储网络视频
*
* @param string $imageUrl 视频URL地址
*/
public function storeNetworkImage( $imageUrl, $from, $imageType, $test=false )
{
$fileObject = $this->__getNetworkImage($imageUrl);
$imageId = $this->store($fileObject, $from, $imageType, $test);
$file = $fileObject->getRealPath();
unlink($file);
return $imageId;
}
private function __getNetworkImage($imageUrl)
{
$imageContent = client::get($imageUrl)->getBody();
$tmpTarget = tempnam(TMP_DIR, 'imageurl');
file_put_contents($tmpTarget, $imageContent);
$imageParams = getimagesize($tmpTarget);
$size = filesize($tmpTarget);
$imageName = substr(strrchr($imageUrl,'/'),1);
if( $num = strpos($imageName,'?') )
{
$imageName = substr($imageName,0,$num);
}
$fileObject = new UploadedFile($tmpTarget, $imageName, $imageParams['mime'], $size, 0, true);
return $fileObject;
}
/**
* 存储服务器中移除视频源文件
* @param array $ids 多个视频ID
*/
public function removeImageFile($ids)
{
try{
$data = $this->objMdlImage->getList('id,storage,ident,img_type',['id'=>$ids]);
foreach( $data as $row )
{
$storager = kernel::single('base_storager',$row['storage']);
$num = $this->objMdlImage->count(['ident'=>$row['ident']]);
//七牛存储服务 同一个视频只生成一个唯一标示
//如果不同店铺上传同一个视频,或导致一个唯一标示对应多条记录
if( $num == 1 )
{
$storager->remove($row['ident']);
$allsize = $this->getImageTypeSize($row['img_type']);
foreach($allsize as $s=>$value)
{
$storager->removeSizeFile($row['ident'],$s);
}
}
$delImageIds[] = $row['id'];
}
}
catch( Exception $e)
{
if( $delImageIds )
{
$this->objMdlImage->delete(['id'=>$delImageIds]);
throw new Exception('部分删除失败');
}
throw new Exception('删除失败');
}
$this->objMdlImage->delete(['id'=>$delImageIds]);
return true;
}
}