檔案上傳是網站開發必不可少的,常見的有圖檔上傳。但是大檔案和視訊上傳不常見。這裡我将自己寫的視訊上傳demo貼出來供大家參考:
利用是最新的WebUploader插件請 下載下傳使用最新版即可
js代碼
_extensions ='3gp,mp4,rmvb,mov,avi,m4v';
_mimeTypes ='video/*,audio/*,application/*';
$(function(){
var chunkSize = 500 * 1024; //分塊大小
var uniqueFileName = null; //檔案唯一辨別符
var md5Mark = null;
// var _backEndUrl = '';
WebUploader.Uploader.register({
"before-send-file": "beforeSendFile"
, "before-send": "beforeSend"
, "after-send-file": "afterSendFile"
}, {
beforeSendFile: function(file){
console.log(file);
//秒傳驗證
var task = new $.Deferred();
var start = new Date().getTime();
(new WebUploader.Uploader()).md5File(file, 0, 10*1024*1024).progress(function(percentage){
}).then(function(val){
md5Mark = val;
_userInfo.md5 = val;
$.ajax({
type: "POST",
url: _backEndUrl,
data: {
status: "md5Check",
md5: val
},
cache: false,
timeout: 1000, //todo 逾時的話,隻能認為該檔案不曾上傳過
dataType: "json"
}).then(function(data, textStatus, jqXHR){
if(data.ifExist){ //若存在,這傳回失敗給WebUploader,表明該檔案不需要上傳
task.reject();
uploader.skipFile(file);
file.path = data.path;
UploadComlate(file);
}else{
task.resolve();
//拿到上傳檔案的唯一名稱,用于斷點續傳
uniqueFileName = md5(_userInfo.openid+_userInfo.time);
}
}, function(jqXHR, textStatus, errorThrown){ //任何形式的驗證失敗,都觸發重新上傳
task.resolve();
//拿到上傳檔案的唯一名稱,用于斷點續傳
uniqueFileName = md5(_userInfo.openid+_userInfo.time);
});
});
return $.when(task);
}
, beforeSend: function(block){
//分片驗證是否已傳過,用于斷點續傳
var task = new $.Deferred();
$.ajax({
type: "POST"
, url: _backEndUrl
, data: {
status: "chunkCheck"
, name: uniqueFileName
, chunkIndex: block.chunk
, size: block.end - block.start
}
, cache: false
, timeout: 1000 //todo 逾時的話,隻能認為該分片未上傳過
, dataType: "json"
}).then(function(data, textStatus, jqXHR){
if(data.ifExist){ //若存在,傳回失敗給WebUploader,表明該分塊不需要上傳
task.reject();
}else{
task.resolve();
}
}, function(jqXHR, textStatus, errorThrown){ //任何形式的驗證失敗,都觸發重新上傳
task.resolve();
});
return $.when(task);
}
, afterSendFile: function(file){
var chunksTotal = 0;
if((chunksTotal = Math.ceil(file.size/chunkSize)) > 1){
//合并請求
var task = new $.Deferred();
$.ajax({
type: "POST"
, url: _backEndUrl
, data: {
status: "chunksMerge"
, name: uniqueFileName
, chunks: chunksTotal
, ext: file.ext
, md5: md5Mark
}
, cache: false
, dataType: "json"
}).then(function(data, textStatus, jqXHR){
//todo 檢查響應是否正常
task.resolve();
file.path = data.path;
UploadComlate(file);
}, function(jqXHR, textStatus, errorThrown){
task.reject();
});
return $.when(task);
}else{
UploadComlate(file);
}
}
});
var uploader = WebUploader.create({
swf: "./Uploader.swf",
server: _backEndUrl, //伺服器處理檔案的路徑
pick: "#picker", //指定選擇檔案的按鈕,此處放的是id
resize: false,
dnd: "#theList", //上傳檔案的拖拽容器(即,如果選擇用拖拽的方式選擇檔案進行上傳,應該要把檔案拖拽到的區域容器)
paste: document.body, //[可選] [預設值:undefined]指定監聽paste事件的容器,如果不指定,不啟用此功能。此功能為通過粘貼來添加截屏的圖檔。建議設定為document.body
disableGlobalDnd: true, //[可選] [預設值:false]是否禁掉整個頁面的拖拽功能,如果不禁用,圖檔拖進來的時候會預設被浏覽器打開。
compress: false,
prepareNextFile: true,
chunked: true,
chunkSize: chunkSize,
chunkRetry: 2, //[可選] [預設值:2]如果某個分片由于網絡問題出錯,允許自動重傳多少次?
threads: true, //[可選] [預設值:3] 上傳并發數。允許同時最大上傳程序數。
formData: function(){return $.extend(true, {}, _userInfo);},
fileNumLimit: 1,
fileSingleSizeLimit: 50 * 1024 * 1024,// 限制在50M
duplicate: true,
accept: {
title: '大檔案上傳', //文字描述
extensions: _extensions, //允許的檔案字尾,不帶點,多個用逗号分割。,jpg,png,
mimeTypes: _mimeTypes, //多個用逗号分割。image/*,
},
});
/**
* 驗證檔案格式以及檔案大小
*/
uploader.on("error",function (type,handler){
if (type=="Q_TYPE_DENIED"){
swal({
title:'',
text: '請上傳MP4格式的視訊~',
type: "warning",
confirmButtonColor: "#DD6B55",
confirmButtonText: "我知道了",
});
}else if(type=="F_EXCEED_SIZE"){
swal({
title:'',
text: '視訊大小不能超過50M哦~',
type: "warning",
confirmButtonColor: "#DD6B55",
confirmButtonText: "我知道了",
});
}
});
uploader.on("fileQueued", function(file){
$('#theList').show();
$("#theList").append('<li id="'+file.id+'" class="upload_li">' +
' <img /> <span class="file_name upload_li">'+file.name+'</span></li><li class="upload_li"><span class="itemUpload weui-btn weui-btn_mini weui-btn_primary">上傳</span><span class="itemStop weui-btn weui-btn_mini weui-btn_default">暫停</span><span class="itemDel weui-btn weui-btn_mini weui-btn_warn">删除</span></li><li class="upload_li">' +
'<div id="percentage'+file.id+'" class="percentage"><div class="weui-progress__bar"><div class="weui-progress__inner-bar js_progress" style="width: 0%;"></div> <b id="pers"></b> </div></div>' +
'</li>');
var $img = $("#" + file.id).find("img");
uploader.makeThumb(file, function(error, src){
if(error){
$img.replaceWith("<span class='no_view'>視訊暫不能預覽</span>");
}
$img.attr("src", src);
});
});
$("#theList").on("click", ".itemUpload", function(){
uploader.upload();
//"上傳"-->"暫停"
$(this).hide();
$(".itemStop").css('display','inline-block');
$(".itemStop").show();
});
$("#theList").on("click", ".itemStop", function(){
uploader.stop(true);
//"暫停"-->"上傳"
$(this).hide();
$(".itemUpload").show();
});
//todo 如果要删除的檔案正在上傳(包括暫停),則需要發送給後端一個請求用來清除伺服器端的緩存檔案
$("#theList").on("click", ".itemDel", function(){
uploader.removeFile($('.upload_li').attr("id")); //從上傳檔案清單中删除
$('.upload_li').remove(); //從上傳清單dom中删除
});
uploader.on("uploadProgress", function(file, percentage){
$(".percentage").find('.js_progress').css("width",percentage * 100 + "%");
$(".percentage").find('#pers').text(parseInt(percentage * 100) + "%");
});
function UploadComlate(file){
console.log(file);
if(file && file.name){
$('#vedio').val(file.name);
$(".percentage").find('#pers').html("<span style='color:green;'>上傳完畢</span>");
$(".itemStop").hide();
$(".itemUpload").hide();
$(".itemDel").hide();
}else{
$(".percentage").find('#pers').html("<span style='color:red;'>上傳失敗,請您檢查網絡狀況~</span>");
$(".itemStop").hide();
$(".itemUpload").hide();
}
}
})
PHP控制器
public function vupload(){
set_time_limit (0);
//關閉緩存
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
$ip_path = './uploads/'.$_SESSION['userinfo']['id'];
$save_path = 'uploads/'.$_SESSION['userinfo']['id'];
$uploader = new \Org\Util\Vupload;
$uploader->set('path',$ip_path);
//用于斷點續傳,驗證指定分塊是否已經存在,避免重複上傳
if(isset($_POST['status'])){
if($_POST['status'] == 'chunkCheck'){
$target = $ip_path.'/'.$_POST['name'].'/'.$_POST['chunkIndex'];
if(file_exists($target) && filesize($target) == $_POST['size']){
die('{"ifExist":1}');
}
die('{"ifExist":0}');
}elseif($_POST['status'] == 'md5Check'){
//todo 模拟持久層查詢
$dataArr = array(
'b0201e4d41b2eeefc7d3d355a44c6f5a' => 'kazaff2.jpg'
);
if(isset($dataArr[$_POST['md5']])){
die('{"ifExist":1, "path":"'.$dataArr[$_POST['md5']].'"}');
}
die('{"ifExist":0}');
}elseif($_POST['status'] == 'chunksMerge'){
if($path = $uploader->chunksMerge($_POST['name'], $_POST['chunks'], $_POST['ext'])){
//todo 把md5簽名存入持久層,供未來的秒傳驗證
session('video_path', $save_path.'/'.$path);
die('{"status":1, "path": "'.$save_path.'/'.$path.'"}');
}
die('{"status":0}');
}
}
if(($path = $uploader->upload('file', $_POST)) !== false){
if(!session('video_path')){
session('video_path', $save_path.'/'.$path);
}
die('{"status":1, "path": "'.$save_path.'/'.$path.'"}');
}
die('{"status":0}');
}
封裝的上傳類庫
<?php
/**
*
* 版權所有:重慶市環境保護資訊中心
* 作 者:Sqc
* 日 期:2016-12-06
* 版 本:1.0.0
* 功能說明:用于視訊等上傳。
*
**/
namespace Org\Util;
Class Vupload
{
//要配置的内容
private $path = "./uploads";
private $allowtype = array('jpg', 'gif', 'png', 'mp4', 'mp3','3gp','rmvb','mov','avi','m4v');
private $maxsize = 104857600;//
private $israndname = true;
private $originName;
private $tmpFileName;
private $fileType;
private $fileSize;
private $newFileName;
private $errorNum = 0;
private $errorMess = "";
private $isChunk = false;
private $indexOfChunk = 0;
public function _initialize(){
parent::_initialize();
}
/**
* 用于設定成員屬性($path, $allowtype, $maxsize, $israndname)
* 可以通過連貫操作一次設定多個屬性值
* @param $key 成員屬性(不區分大小寫)
* @param $val 為成員屬性設定的值
* @return object 傳回自己對象$this, 可以用于連貫操作
*/
function set($key, $val){
$key = strtolower($key);
if (array_key_exists($key, get_class_vars(get_class($this)))){
$this->setOption($key, $val);
}
return $this;
}
/**
* 調用該方法上傳檔案
* Enter description here ...
* @param $fileField 上傳檔案的表單名稱
*
*/
function upload($fileField, $info){
//判斷是否為分塊上傳
$this->checkChunk($info);
if (!$this->checkFilePath($this->path)){
$this->errorMess = $this->getError();
return false;
}
//将檔案上傳的資訊取出賦給變量
$name = $_FILES[$fileField]['name'];
$tmp_name = $_FILES[$fileField]['tmp_name'];
$size = $_FILES[$fileField]['size'];
$error = $_FILES[$fileField]['error'];
//設定檔案資訊
if ($this->setFiles($name, $tmp_name, $size, $error)){
//如果是分塊,則建立一個唯一名稱的檔案夾用來儲存該檔案的所有分塊
if($this->isChunk){
$uploadDir = $this->path;
if($info){
$tmpName = $this->setDirNameForChunks();
if(!$this->checkFilePath($uploadDir . '/' . $tmpName)){
$this->errorMess = $this->getError();
return false;
}
}
// $tmpName = $this->setDirNameForChunks($info);
// if(!$this->checkFilePath($uploadDir . '/' . $tmpName)){
// $this->errorMess = $this->getError();
// return false;
// }
//建立一個對應的檔案,用來記錄上傳分塊檔案的修改時間,用于清理長期未完成的垃圾分塊
touch($uploadDir.'/'.$tmpName.'.tmp');
}
if($this->checkFileSize() && $this->checkFileType()){
$this->setNewFileName();
if ($this->copyFile()){
return $this->newFileName;
}
}
}
$this->errorMess = $this->getError();
return false;
}
public function chunksMerge($uniqueFileName, $chunksTotal, $fileExt){
$targetDir = $this->path.'/'.$uniqueFileName;
//檢查對應檔案夾中的分塊檔案數量是否和總數保持一緻
if($chunksTotal > 1 && (count(scandir($targetDir)) - 2) == $chunksTotal){
//同步鎖機制
$lockFd = fopen($this->path.'/'.$uniqueFileName.'.lock', "w");
if(!flock($lockFd, LOCK_EX | LOCK_NB)){
fclose($lockFd);
return false;
}
//進行合并
$this->fileType = $fileExt;
$finalName = $this->path.'/'.($this->setOption('newFileName', $this->proRandName()));
$file = fopen($finalName, 'wb');
for($index = 0; $index < $chunksTotal; $index++){
$tmpFile = $targetDir.'/'.$index;
$chunkFile = fopen($tmpFile, 'rb');
$content = fread($chunkFile, filesize($tmpFile));
fclose($chunkFile);
fwrite($file, $content);
//删除chunk檔案
unlink($tmpFile);
}
fclose($file);
//删除chunk檔案夾
rmdir($targetDir);
unlink($this->path.'/'.$uniqueFileName.'.tmp');
//解鎖
flock($lockFd, LOCK_UN);
fclose($lockFd);
unlink($this->path.'/'.$uniqueFileName.'.lock');
return $this->newFileName;
}
return false;
}
//擷取上傳後的檔案名稱
public function getFileName(){
return $this->newFileName;
}
//上傳失敗後,調用該方法則傳回,上傳出錯資訊
public function getErrorMsg(){
return $this->errorMess;
}
//設定上傳出錯資訊
public function getError(){
$str = "上傳檔案<font color='red'>{$this->originName}</font>時出錯:";
switch ($this->errorNum) {
case 4:
$str.= "沒有檔案被上傳";
break;
case 3:
$str.= "檔案隻有部分被上傳";
break;
case 2:
$str.= "上傳檔案的大小超過了HTML表單中MAX_FILE_SIZE選項指定的值";
break;
case 1:
$str.= "上傳的檔案超過了php.ini中upload_max_filesize選項限制的值";
break;
case -1:
$str.= "未允許的類型";
break;
case -2:
$str.= "檔案過大, 上傳的檔案夾不能超過{$this->maxsize}個位元組";
break;
case -3:
$str.= "上傳失敗";
break;
case -4:
$str.= "建立存放上傳檔案目錄失敗,請重新指定上傳目錄";
break;
case -5:
$str.= "必須指定上傳檔案的路徑";
break;
default:
$str .= "未知錯誤";
}
return $str."<br>";
}
//根據檔案的相關資訊為分塊資料建立檔案夾
//md5(目前登入使用者的資料庫id + 檔案原始名稱 + 檔案類型 + 檔案最後修改時間 + 檔案總大小)
private function setDirNameForChunks(){
$str = $_SESSION['userinfo']['openid'].$_SESSION['userinfo']['report_time'];
return md5($str);
return $str;
}
//設定和$_FILES有關的内容
private function setFiles($name="", $tmp_name="", $size=0, $error=0){
$this->setOption('errorNum', $error);
if ($error) {
return false;
}
$this->setOption('originName', $name);
$this->setOption('tmpFileName', $tmp_name);
$aryStr = explode(".", $name);
$this->setOption("fileType", strtolower($aryStr[count($aryStr)-1]));
$this->setOption("fileSize", $size);
return true;
}
private function checkChunk($info){
if(isset($info['chunks']) && $info['chunks'] > 0){
$this->setOption("isChunk", true);
if(isset($info['chunk']) && $info['chunk'] >= 0){
$this->setOption("indexOfChunk", $info['chunk']);
return true;
}
throw new Exception('分塊索引不合法');
}
return false;
}
//為單個成員屬性設定值
private function setOption($key, $val){
$this->$key = $val;
return $val;
}
//設定上傳後的檔案名稱
private function setNewFileName(){
if($this->isChunk){ //如果是分塊,則以分塊的索引作為檔案名稱儲存
$this->setOption('newFileName', $this->indexOfChunk);
}elseif($this->israndname) {
$this->setOption('newFileName', $this->proRandName());
}else{
$this->setOption('newFileName', $this->originName);
}
}
//檢查上傳的檔案是否是合法的類型
private function checkFileType(){
if (in_array(strtolower($this->fileType), $this->allowtype)) {
return true;
}else{
$this->setOption('errorNum', -1);
return false;
}
}
//檢查上傳的檔案是否是允許的大小
private function checkFileSize(){
if ($this->fileSize > $this->maxsize) {
$this->setOption('errorNum', -5);
return false;
}else{
return true;
}
}
//檢查是否有存放上傳檔案的目錄
private function checkFilePath($target){
if (empty($target)) {
$this->setOption('errorNum', -5);
return false;
}
if (!file_exists($target) || !is_writable($target)) {
if ([email protected]($target, 0755)) {
$this->setOption('errorNum', -4);
return false;
}
}
$this->path = $target;
return true;
}
//設定随機檔案名
private function proRandName(){
$fileName = date('YmdHis')."_".rand(100,999);
return $fileName.'.'.$this->fileType;
}
//複制上傳檔案到指定的位置
private function copyFile(){
if (!$this->errorNum) {
$path = rtrim($this->path, '/').'/';
$path.= $this->newFileName;
if (@move_uploaded_file($this->tmpFileName, $path)) {
return true;
}else{
$this->setOption('errorNum', -3);
return false;
}
}else{
return false;
}
}
}