視頻上傳屬於大文件上傳啊 直接上傳很浪費時間 和占內存所以使用切片上傳比較好
前斷上傳插件 我使用的是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));
合並代碼是比較重要的 上面就是合並的代碼
