视频切片上传

视频上传属于大文件上传啊 直接上传很浪费时间 和占内存所以使用切片上传比较好

前断上传插件 我使用的是zui 自带切片上传

 

<div class="layui-form-item">
<label class="layui-form-label"><span class="red">•</span> 视频文件:</label>
<div class="layui-input-block">
<div id="uploaderVideo" class="uploader" style="margin-bottom:0;">
<div class="file-list" data-drag-placeholder="视频上传只支持mp4格式"></div>
<button type="button" class="btn btn-primary uploader-btn-browse"><i
class="icon icon-cloud-upload"></i> 选择文件
</button>
</div>
</div>
</div>
<input type="hidden" name="chunk_uuid" id="chunk_uuid" value=""/>
<input type="hidden" name="video_name_raw" id="video_name_raw" value=""/>
<input type="hidden" name="video_name" id="video_name" value=""/>
$('#uploaderVideo').uploader({
autoUpload: true,
multi_selection: false,
chunk_size: '2m',
url: '/upload/uploadVideoChunk',
onBeforeUpload: function (file) {
if ($.trim($('#chunk_uuid').val()).length > 0) {
redTip('只能上传一个文件');
this.removeFile(file);
this.stop();
}
},
onChunkUploaded: function (file, responseObject) {
var jn = $.parseJSON(responseObject.response);
console.log('上传进度:' + jn.d.chunk + '/' + jn.d.chunks);
},
onFileUploaded: function (file, responseObject) {
var jn = $.parseJSON(responseObject.response);
//记录uuid
$('#chunk_uuid').val(jn.d.chunk_uuid);
$('#video_name_raw').val(jn.d.name);
//自动填写视频名称
var video_name = $.trim($('#video_name').val());
if (video_name.length < 1) {
$('#video_name').val(jn.d.name);
}
},
responseHandler: function (responseObject, file) {
console.log('ok');
}
});

这个是js的使用代码,自带切片功能

上传的参数
chunk:表示现在 正在上传第几片切片
chunks:表示总共切片多少分
uuid:代表上传的别名,可以作为独一的存储地址
file:代表上传的文件
前端是比较简单的 主要是服务端代码
$now_chunk = $request->post('chunk');
$now_chunk = intval($now_chunk);
$chunk_total = $request->post('chunks');
$chunk_total = intval($chunk_total);
$name = $request->post('name');
$uuid = $request->post('uuid');

if ($now_chunk < 0) {
return rs(Code::VIDEO_UPLOAD_CHUNK_ERROR, m(Code::VIDEO_UPLOAD_CHUNK_ERROR));
}
if ($chunk_total < 1) {
return rs(Code::VIDEO_UPLOAD_CHUNK_ERROR, m(Code::VIDEO_UPLOAD_CHUNK_ERROR));
}
if ($uuid == '') {
return rs(Code::VIDEO_UPLOAD_UUID_ERROR, m(Code::VIDEO_UPLOAD_UUID_ERROR));
}

if (!$request->hasFile('file')) {
return rs(Code::VIDEO_UPLOAD_FILE_ERROR, m(Code::VIDEO_UPLOAD_FILE_ERROR));
}

if (!$request->file('file')->isValid()) {
return rs(Code::VIDEO_UPLOAD_FILE_ERROR, m(Code::VIDEO_UPLOAD_FILE_ERROR));
}

$year = date('Y');

//上传文件
$request->file('file')->storePubliclyAs($this->baseDirOfVideoChunk() . '/' . $year . '/' . $uuid, $now_chunk);

//保存到session中
$key = 'video_upload_' . $uuid;
if (session()->exists($key)) {
$one = session()->get($key);
$one['chunk_ok'][] = $now_chunk;
} else {
$one = [
'uuid' => $uuid,
'name' => $name,
'chunk_total' => $chunk_total,
'chunk_ok' => [$now_chunk]
];
}
session()->put($key, $one);
session()->save();

$one = session()->get($key);
if ($one['chunk_total'] === count($one['chunk_ok'])) {
//上传完毕
session()->forget($key);
return rs(Code::OK, m(Code::OK), ['finish' => 'yes', 'name' => $name, 'chunks' => $chunk_total, 'chunk' => ($now_chunk + 1), 'chunk_uuid' => $year . '/' . $uuid]);
} else {
//上传未完成
return rs(Code::OK, m(Code::OK), ['finish' => 'no', 'name' => $name, 'chunks' => $chunk_total, 'chunk' => ($now_chunk + 1), 'chunk_uuid' => $year . '/' . $uuid]);
}

这个只是简单的上传代码接受切片文件(因使用laravel框架,所以内置方法都是laravel的内置方法)
上传结束后 就是合并上传文件

$video = $this->getVideoData(['cook_st' => Vars::VIDEO_COOK_ST_MERGE_WAIT, 'merge_times_lt' => Vars::MAX_MERGE_AND_CUT_TIMES], '', 1, 1);
if (empty($video)) {
return rs(Code::OK, m(Code::OK));
}
$video = $video[0];
$video_id = $video['video_id'];

VideoModel::getInstance()->incField(array('video_id' => $video_id), 'merge_times', 1);


//检查chunk文件夹是否存在
$base_dir = $this->storageAppPublicDir() . '/' . $this->baseDirOfVideoChunk();
$video_chunk_dir = $base_dir . '/' . $video['chunk_uuid'];
if (!is_dir($video_chunk_dir)) {
Storage::disk('video_log')->append(date('Ymd') . '.log', $this->logContent('视频 video_id=' . $video_id . ' 的 chunk 文件夹不存在'));
return rs(Code::VIDEO_MERGE_ERROR, m(Code::VIDEO_MERGE_ERROR) . '_c');
}

//检查chunk文件夹是否有文件
$files = scandir($video_chunk_dir);
if ($files < 3) {
Storage::disk('video_log')->append(date('Ymd') . '.log', $this->logContent('视频 video_id=' . $video_id . ' 的 chunk 文件夹为空'));
return rs(Code::VIDEO_MERGE_ERROR, m(Code::VIDEO_MERGE_ERROR) . '_d');
}
sort($files);

//保存文件路径 + 文件名
$video_raw_path = $this->storageAppPublicDir() . '/' . $this->getVideoRawPath($video_id, $video['video_suffix']);

//创建文件夹
if (!is_dir(dirname($video_raw_path))) {
File::makeDirectory(dirname($video_raw_path));
}

//删除原来的视频
if (is_file($video_raw_path)) {
@unlink($video_raw_path);
}

//合并chunk切块
$errors = [];
foreach ($files as $chunk) {
if (in_array($chunk, ['.', '..'])) {
continue;
}
$chunk_fp = fopen($video_chunk_dir . '/' . $chunk, 'rb');
$data = fread($chunk_fp, filesize($video_chunk_dir . '/' . $chunk));
$fp = fopen($video_raw_path, 'ab');
fwrite($fp, $data);
fclose($fp);
fclose($chunk_fp);
}
if (!empty($errors)) {
Storage::disk('video_log')->append(date('Ymd') . '.log', $this->logContent('源视频 video_id=' . $video_id . ' 的 ' . implode(' - ', $errors) . ' 文件不存在'));
return rs(Code::VIDEO_MERGE_ERROR, m(Code::VIDEO_MERGE_ERROR), ['errors' => $errors]);
}

//原始文件大小 + 时长
$file_size_m = filesize($video_raw_path) / (1024 * 1024);
$video_duration = $this->getVideoDuration($video_raw_path);
$video_duration = video_time_to_second($video_duration);
VideoModel::getInstance()->edit(['video_id' => $video_id], ['video_size_m' => round($file_size_m, 2), 'video_duration' => $video_duration]);


//删除chunk文件
File::deleteDirectory($video_chunk_dir);

//更新数据库状态
$this->setVideoCookSt($video_id, Vars::VIDEO_COOK_ST_SPLIT_WAIT);

//修改视频ID的课程时长
$where = array();
$where['video_id'] = $video_id;
$up = array();
$up['video_tot_duration'] = $video_duration;
LessonModel::getInstance()->edit($where, $up);

return rs(Code::OK, m(Code::OK));

合并代码是比较重要的 上面就是合并的代码



posted @ 2020-03-11 13:37  蒋华  阅读(1739)  评论(0编辑  收藏  举报