<?php
namespace app\controller;
use app\BaseController;
class Index extends BaseController
{
public function upload()
{
$filename = 'file';
$path = 'common';
try {
$file = request()->file($filename);
if (empty($file)) {
throw new \Exception('没有文件上传');
}
if ($this->request->request('chunkid'))
{
$chunkid = $this->request->request('chunkid');
$chunkindex = $this->request->request('chunkindex');
$chunkcount = $this->request->request('chunkcount');
$fileName = $chunkid . "-" . $chunkindex . '.tmp';
$path = $path . DIRECTORY_SEPARATOR . date('Ymd');
$filepath = \think\facade\Filesystem::disk('public')->putFileAs($path, $file, $fileName);
if ($chunkindex + 1 == $chunkcount) {
$filePath = config('filesystem.disks.public.root') . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR;
$completed = true;
for ($i = 0; $i < $chunkcount; $i++) {
if (!file_exists("{$filePath}{$chunkid}-{$i}.tmp")) {
$completed = false;
break;
}
}
if(!$completed) {
for ($i = 0; $i < $chunkcount; $i++) {
if (file_exists("{$filePath}{$chunkid}-{$i}.tmp")) {
@unlink("{$filePath}{$chunkid}-{$i}.tmp");
}
}
throw new \Exception("分片上传文件错误");
}
$ext = $file->getOriginalExtension() ? $file->getOriginalExtension() : substr(strrchr($this->request->post('filename'), '.'), 1);
if (!$destFile = @fopen($filePath . $chunkid . '.' . $ext, "wb")) {
throw new \Exception("分片上传文件错误.");
}
if (flock($destFile, LOCK_EX)) {
for ($i = 0; $i < $chunkcount; $i++) {
$partFile = "{$filePath}{$chunkid}-{$i}.tmp";
if (!$handle = @fopen($partFile, "rb")) {
break;
}
while ($buff = fread($handle, filesize($partFile))) {
fwrite($destFile, $buff);
}
@fclose($handle);
@unlink($partFile);
}
flock($destFile, LOCK_UN);
}
@fclose($destFile);
$filepath = request()->domain() . '/storage/' . str_replace(DIRECTORY_SEPARATOR, '/', $path) . '/' . $chunkid . '.' . $ext;
}
} else {
$upload = config('filesystem.config');
validate(['file'=> [
'fileSize' => $upload['fileSize'],
'fileExt' => $upload['fileExt'],
'fileMime' => $upload['fileMime'],
]], [
'file.fileSize' => '上传文件不能大于'. $upload['fileSize'] / 1024 / 1024 .'M',
'file.fileExt' => '上传文件类型不允许',
'file.fileMime' => '上传文件类型不允许.',
])->check(['file' => $file]);
if (is_array($file)) {
$url = [];
$filepath = [];
foreach ($file as $value) {
$upload_path = \think\facade\Filesystem::disk('public')->putFile($path, $value);
$url[] = request()->domain() . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path;
$filepath[] = $upload_path;
$source[] = app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path;
event('upload_success', ['source' => app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path, 'file' => $file]);
}
} else {
$upload_path = \think\facade\Filesystem::disk('public')->putFile($path, $file);
$url = request()->domain() . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path;
$filepath = $upload_path;
$source = app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path;
event('upload_success', ['source' => app()->getRootPath() . 'public' . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $upload_path, 'file' => $file]);
}
}
} catch (\think\exception\ValidateException $e) {
throw new \Exception($e->getMessage());
} catch(\Exception $e) {
throw new \Exception($e->getMessage());
}
return json(['filepath' => $filepath, 'url' => $url ?? '', 'source' => $source ?? '']);
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<style lang="less" scoped>
.fileupload {
display: flex;
flex-direction: column;
}
.fileupload-title {
display: flex;
justify-content: flex-end;
padding: 5px 0px;
}
.fileupload-content {
flex: 1;
}
}
</style>
</head>
<body>
<div id="app">
<div class="fileupload">
<div class="fileupload-title">
<el-button type="primary" for="file" @click="$refs.file.click()">文件上传(分片上传)<i class="el-icon-upload el-icon--right"></i></el-button>
<input style="display:none" type="file" id="file" name="file" ref="file" multiple="multiple" @change="fileChange($event)" />
</div>
<div class="fileupload-content">
<el-table :data="fileUploadList" border stripe>
<el-table-column label="文件名称" prop="name"></el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button type="primary" plain v-if="scope.row.status==1" @click="stop(scope.$index)">停止上传</el-button>
<el-button type="danger" plain v-if="scope.row.status==-1" @click="continueUp(scope.$index)">断点续传</el-button>
<el-link :underline="false" v-if="scope.row.filepath" v-text="scope.row.filepath"></el-link>
</template>
</el-table-column>
<el-table-column label="上传进度">
<template slot-scope="scope">
<div>
<el-progress :status="scope.row.isSuccess==1?'success':scope.row.isSuccess==2?'exception':null" :percentage="scope.row.percent"></el-progress>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
let config = {
timeout: 120000,
onUploadProgress: function(progress) {
this.fileUploadList[i].percent = Math.round(
(progress.loaded * 100) / progress.total
);
}.bind(this)
};
function AddFiles(filelist, conf) {
return axios.post('/index/upload', filelist, conf)
}
new Vue({
el: '#app',
data() {
return {
fileUploadList: [],
bytesPerPiece: 1024 * 1024 * 2,
};
},
created: function() {
},
methods: {
async fileChange(event) {
this.fileUploadList = [];
let filelist = event.target.files;
if (filelist.length > 8) {
this.$message("上传文件个数不能超过8个");
return;
}
if (filelist.length > 0) {
for (let i = 0; i < filelist.length; i++) {
let filedata = {
file: filelist[i],
name: filelist[i].name.substring(0, filelist[i].name.length - 4),
size: filelist[i].size,
date: filelist[i].lastModified,
isSuccess: 0,
percent: 0,
speed_time: '',
lastTime: 0,
lastSize: 0,
status: 1,
lastChunk: {},
filepath: '',
};
this.fileUploadList.push(filedata);
}
for (let i = 0; i < filelist.length; i++) {
let config = {
timeout: 120000,
onUploadProgress: function(progress) {
let percent = Math.round(
(progress.loaded * index * 100) / filesize
);
if (this.fileUploadList[i].percent < percent) {
this.fileUploadList[i].percent = percent > 100 ? 100 : percent;
}
}.bind(this)
};
let chunkid = Number(Math.random().toString().substr(5,10) + Date.now()).toString(36);
let bytesPerPiece = this.bytesPerPiece;
let totalPieces = 0;
let blob = filelist[i];
let start = 0;
let end;
let index = 0;
let filesize = blob.size;
let filename = blob.name;
console.log(blob)
console.log(typeof blob)
totalPieces = Math.ceil(filesize / bytesPerPiece);
while(start < filesize) {
if (this.fileUploadList[i].status == -1) {
this.fileUploadList[i].lastChunk = {
chunkid: chunkid,
index: index,
start: start,
end: end,
};
break;
}
end = start + bytesPerPiece;
if(end > filesize) {
end = filesize;
}
let chunk = blob.slice(start,end);
let sliceIndex= blob.name + index;
let formData = new FormData();
formData.append("file", chunk, filename);
formData.append("chunkid", chunkid);
formData.append("chunkindex", index);
formData.append("chunkcount", totalPieces);
await AddFiles(formData, config).then(res => {
if (res.status == 200) {
if (index + 1 == totalPieces) {
this.fileUploadList[i].isSuccess = 1;
this.fileUploadList[i].percent = 100;
this.fileUploadList[i].status = 0;
this.fileUploadList[i].filepath = res.data.filepath;
this.$message("上传成功");
}
} else {
this.fileUploadList[i].isSuccess = 2;
this.fileUploadList[i].status = 0;
this.$message("上传失败");
}
});
start = end;
index++;
}
}
}
},
stop(i) {
this.fileUploadList[i].status = -1;
},
async continueUp(i) {
let config = {
timeout: 120000,
onUploadProgress: function(progress) {
let percent = Math.round(
(progress.loaded * index * 100) / filesize
);
if (this.fileUploadList[i].percent < percent) {
this.fileUploadList[i].percent = percent > 100 ? 100 : percent;
}
}.bind(this)
};
this.fileUploadList[i].status = 1;
let row = this.fileUploadList[i];
let blob = row.file;
let chunkid = row.lastChunk.chunkid;
let bytesPerPiece = this.bytesPerPiece;
let totalPieces = 0;
let start = row.lastChunk.start;
let end = row.lastChunk.end;
let index = row.lastChunk.index;
let filesize = blob.size;
let filename = blob.name;
totalPieces = Math.ceil(filesize / bytesPerPiece);
while(start < filesize) {
if (row.status == -1) {
row.lastChunk = {
chunkid: chunkid,
index: index,
start: start,
end: end,
};
break;
}
end = start + bytesPerPiece;
if(end > filesize) {
end = filesize;
}
let chunk = blob.slice(start,end);
let sliceIndex= blob.name + index;
let formData = new FormData();
formData.append("file", chunk, filename);
formData.append("chunkid", chunkid);
formData.append("chunkindex", index);
formData.append("chunkcount", totalPieces);
await AddFiles(formData, config).then(res => {
if (res.status == 200) {
if (index + 1 == totalPieces) {
row.isSuccess = 1;
row.percent = 100;
row.status = 0;
row.filepath = res.data.filepath;
this.$message("上传成功");
}
} else {
row.isSuccess = 2;
row.status = 0;
this.$message("上传失败");
}
});
start = end;
index++;
}
}
}
})
</script>
</html>