大文件上传代码片段记录
记录一下自己写的PHP大文件分段上传代码,方面以后要用的时候直接复制粘贴。使用了Layui、JQuery和ThinkPHP,还有一些优化空间,等下次用到的时候再完善~
样式
<style>
.input-toolbar {
display: inline-block;
position: absolute;
top: 1px;
right: 1px;
height: 36px;
line-height: 36px;
border-radius: 0 2px 2px 0;
border-left: 1px solid #e6e6e6;
background-color: #eee;
font-size: 0;
}
.input-toolbar .layui-icon {
width: 36px;
height: 36px;
display: inline-block;
text-align: center;
border: 0;
border-right: 1px solid #e6e6e6;
cursor: pointer;
}
.input-toolbar .layui-icon:hover {
background-color: #e0e0e0;
}
.input-toolbar .layui-icon:last-child {
border-right: 0;
}
.video-upload {
position: absolute;
opacity: 0;
top: 0;
display: block;
width: 36px;
height: 36px;
}
</style>
HTML
<div class="layui-form-item" id="video-container">
<label class="layui-form-label">视频文件</label>
<div class="layui-input-block">
<input type="text" name="video" class="layui-input" placeholder="请在右侧上传视频">
<div class="input-toolbar">
<button type="button" class="layui-icon layui-icon-upload" title="点击上传">
<input type="file" class="video-upload" data-bind="[name='video']" data-progress="videoProgress">
</button>
<button type="button" class="layui-icon video-preview" data-bind="[name='video']" title="点击查看预览"></button>
</div>
<br/>
<div class="layui-progress layui-progress-big" lay-filter="videoProgress" lay-showPercent="true">
<div class="layui-progress-bar" lay-percent="0%"></div>
</div>
</div>
</div>
Javascript
layui.use(['jquery', 'layer', 'element'], function(){
let $ = layui.jquery
,layer = layui.layer
,element = layui.element;
// 图片预览
$('.preview').click(function () {
var bind = $(this).data('bind');
if (!bind) {
layer.msg('缺少属性: data-bind');
return ;
}
var src = $(bind).val();
if (!src) {
layer.msg('图片地址为空');
return ;
}
layer.photos({
photos: {
"title": "预览",
"data": [{"src": src}]
}
});
});
$('.video-upload').change(async function () {
let that = this;
$(this).prop('disabled', true);
element.progress(that.dataset.progress, '0%');
console.log(that)
try {
if (!that.files.length) {
throw new Error('请选择文件');
}
let chunkSize = 1 * 1024 * 1024
,blob = that.files[0]
,chunkTotal = Math.ceil(blob.size / chunkSize)
,start = 0
,progress = 0
,end, formData;
if (!/.mp4$/i.test(blob.name)) {
throw new Error('仅允许上传mp4格式的视频');
}
for (var i = 1; i <= chunkTotal; i++) {
if (i == chunkTotal) {
end = blob.size;
} else {
end = start + chunkSize;
}
formData = new FormData();
formData.append("file", blob.slice(start, end), blob.name);
formData.append("batch", i);
await fetch("{:url('videoUpload')}", {
body: formData
,method: 'POST'
,credentials: 'same-origin'
})
.then(response => response.json())
.then(function (result) {
progress = i / (chunkTotal + 1);
element.progress(that.dataset.progress, progress * 100 + '%');
})
.catch(function (error) {
throw new Error(error);
});
start = end;
}
formData = new FormData();
formData.append("filename", blob.name);
formData.append("success", 1);
await fetch("{:url('videoUpload')}", {
body: formData
,method: 'POST'
,credentials: 'same-origin'
})
.then(response => response.json())
.then(function (result) {
$(that.dataset.bind).val(result.data.src);
})
.catch(function (error) {
throw new Error(error);
});
progress = 1;
element.progress(that.dataset.progress, progress * 100 + '%');
} catch (e) {
layer.alert(e.message);
}
$(this).prop('disabled', false);
});
// 视频预览
$('.video-preview').click(function () {
var bind = $(this).data('bind');
if (!bind) {
layer.msg('缺少属性: data-bind');
return ;
}
var src = $(bind).val();
if (!src) {
layer.msg('视频地址为空');
return ;
}
layer.open({
type: 1,
title: '视频预览',
area: ['450px', '400px'],
content: '<video src="' + src + '" controls="controls" style="width: 100%;"></video>'
});
});
});
PHP ThinkPHP控制器方法代码
public function videoUpload()
{
if (Request::has('success', 'param', true)) {
$tmpDir = 'storage/article_tmp/';
$videoDir = 'storage/article_video/';
$filenameMd5 = md5(Request::param('filename'));
$tmpFilePath = $tmpDir . $filenameMd5 . '.' . pathinfo(Request::param('filename'), PATHINFO_EXTENSION);
$fileResource = fopen($tmpFilePath, 'w+');
foreach(scandir($tmpDir) as $item) {
if (0 !== strpos($item, $filenameMd5 . '.mp4_')) continue;
fwrite($fileResource, file_get_contents($tmpDir . $item));
unlink($tmpDir . $item);
}
fclose($fileResource);
$newFileName = md5_file($tmpFilePath) . '.mp4';
$newFilePath = $videoDir . $newFileName;
if (!file_exists($newFilePath)) {
rename($tmpFilePath, $newFilePath);
} else {
unlink($tmpFilePath);
}
return json([
'code' => 0,
'msg' => '上传成功',
'data' => [
'src' => '/storage/article_video/' . $newFileName,
'size' => filesize($newFilePath),
]
]);
} else {
try {
$file = Request::file('file');
if (null === $file) {
throw new \Exception('请上传文件', UPLOAD_ERR_NO_FILE);
}
validate(['file' => [
'fileSize' => Config::get('filesystem.maxFileSize'),
'fileExt' => 'mp4',
]])->check(['file' => $file]);
$name = md5($file->getOriginalName()) . '.' . $file->extension() . '_' . Request::param('batch', 1);
Filesystem::disk('public')->putFileAs('article_tmp', $file, $name);
} catch (\Exception $e) {
return json([
'code' => 1,
'msg' => $e->getMessage(),
]);
}
return json([
'code' => 0,
'msg' => '上传成功',
]);
}
}