完成文件上传管理

This commit is contained in:
augushong
2021-06-19 18:41:05 +08:00
parent ae9f2c6999
commit 1be98f85b6
30 changed files with 51988 additions and 163 deletions

View File

@@ -7,10 +7,18 @@ use League\Flysystem\Util\MimeType;
use think\facade\Filesystem; use think\facade\Filesystem;
use think\facade\Config; use think\facade\Config;
use think\File; use think\File;
use think\file\UploadedFile;
class UploadFiles class UploadFiles
{ {
protected static $disk = '';
public static function setDisks($disk)
{
self::$disk = $disk;
}
public static function add() public static function add()
{ {
return new AppUploadFiles(); return new AppUploadFiles();
@@ -46,7 +54,7 @@ class UploadFiles
$model_file->save(); $model_file->save();
return Filesystem::delete($model_file->getData('save_name')); return Filesystem::disk(self::$disk)->delete($model_file->getData('save_name'));
} }
public static function save(Request $request) public static function save(Request $request)
@@ -59,6 +67,9 @@ class UploadFiles
$file = request()->file('file'); $file = request()->file('file');
if (empty($file)) {
return json_message('上传失败');
}
$file_extension = $file->extension(); $file_extension = $file->extension();
if ($file_extension == 'php') { if ($file_extension == 'php') {
@@ -67,15 +78,13 @@ class UploadFiles
$file_path = $file->getRealPath(); $file_path = $file->getRealPath();
$file_content = file_get_contents($file_path); // $file_content = file_get_contents($file_path);
// if (strpos($file_content, '<?php') !== false) {
// return json_message('上传文件异常');
// }
if (strpos($file_content, '<?php') !== false) {
return json_message('上传文件异常');
}
if (empty($file)) {
return json_message('上传失败');
}
$dir_name = $request->param('dir', $type); $dir_name = $request->param('dir', $type);
try { try {
@@ -92,7 +101,13 @@ class UploadFiles
$dir_name = $type; $dir_name = $type;
} }
$model_file = UploadFiles::add(); $model_file = UploadFiles::add();
$model_file->file_name = $file->getFilename(); if ($file instanceof UploadedFile) {
$model_file->file_name = $file->getOriginalName();
} else {
$model_file->file_name = $file->getFilename();
}
$model_file->mime_type = $file->getMime(); $model_file->mime_type = $file->getMime();
$model_file->ext_name = $file->extension(); $model_file->ext_name = $file->extension();
$model_file->file_size = $file->getSize(); $model_file->file_size = $file->getSize();
@@ -101,7 +116,7 @@ class UploadFiles
$model_file->create_time = time(); $model_file->create_time = time();
$model_file->type = $type; $model_file->type = $type;
$model_file->save_name = Filesystem::putFile('upload/' . $dir_name, $file, 'uniqid'); $model_file->save_name = Filesystem::disk(self::$disk)->putFile('upload/' . $dir_name, $file, 'uniqid');
$model_file->save(); $model_file->save();
return $model_file; return $model_file;
} }

View File

@@ -11,59 +11,53 @@ use think\facade\Session;
class Login extends Common class Login extends Common
{ {
/** /**
* 显示资源列表 * 显示资源列表
* *
* @return \think\Response * @return \think\Response
*/ */
public function index() public function index()
{ {
// //
return View::fetch(); return View::fetch();
}
public function auth()
{
$post_data = $this->request->post();
$validate = Validate::rule('account', Rule::isRequire())
->rule('password', Rule::isRequire())
->rule('captcha', function ($value) {
return \captcha_check($value) ? true : '验证码错误';
});
if (!$validate->check($post_data)) {
return json_message($validate->getError());
} }
$model_admin = Admin::where('account', $post_data['account'])->find();
public function auth() if (empty($model_admin)) {
{ return json_message('帐号不存在');
$post_data = $this->request->post();
$validate = Validate::rule('account',Rule::isRequire())
->rule('password',Rule::isRequire())
->rule('captcha',function($value){
return \captcha_check($value)?true:'验证码错误';
});
if(!$validate->check($post_data)){
Session::set('admin_id',1);
return json_message();
return json_message($validate->getError());
}
$model_admin = Admin::where('account',$post_data['account'])->find();
if(empty($model_admin)){
Session::set('admin_id',1);
return json_message();
return json_message('帐号不存在');
}
if($model_admin->getData('password') !== md5($post_data['password'].$model_admin->getData('salt'))){
Session::set('admin_id',1);
return json_message();
return json_message('密码错误');
}
Session::set('admin_id',$model_admin->id);
return json_message();
} }
public function logout() if ($model_admin->getData('password') !== md5($post_data['password'] . $model_admin->getData('salt'))) {
{ return json_message('密码错误');
Session::clear();
$this->success('已经安全退出','Login/Index');
} }
Session::set('admin_id', $model_admin->id);
return json_message();
}
public function logout()
{
Session::clear();
$this->success('已经安全退出', 'Login/Index');
}
} }

View File

@@ -61,7 +61,7 @@ function get_system_config($name = '', $default = '')
return $list; return $list;
} }
if (isset($list[$name])) { if (!empty($list[$name])) {
return $list[$name]; return $list[$name];
} }

View File

@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace app\index\controller;
use app\UploadFiles;
use think\Request;
class File extends Common
{
/**
* 显示资源列表
*
* @return \think\Response
*/
public function index()
{
//
}
/**
* 显示创建资源表单页.
*
* @return \think\Response
*/
public function create()
{
//
}
/**
* 保存新建的资源
*
* @param \think\Request $request
* @return \think\Response
*/
public function save(Request $request)
{
//
}
public function webSave(Request $request)
{
# code...
return UploadFiles::save($request);
}
/**
* 显示指定的资源
*
* @param int $id
* @return \think\Response
*/
public function read($id)
{
//
}
/**
* 显示编辑资源表单页.
*
* @param int $id
* @return \think\Response
*/
public function edit($id)
{
//
}
/**
* 保存更新的资源
*
* @param \think\Request $request
* @param int $id
* @return \think\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* 删除指定资源
*
* @param int $id
* @return \think\Response
*/
public function delete($id)
{
//
}
}

View File

@@ -2,6 +2,7 @@
namespace app\middleware; namespace app\middleware;
use app\UploadFiles;
use think\facade\Db; use think\facade\Db;
use think\facade\Config; use think\facade\Config;
use think\helper\Arr; use think\helper\Arr;
@@ -15,6 +16,8 @@ class ConfigInit
$filesystem_config = Config::get('filesystem'); $filesystem_config = Config::get('filesystem');
Arr::set($filesystem_config,'default','public'); Arr::set($filesystem_config,'default','public');
UploadFiles::setDisks('public');
Config::set($filesystem_config, 'filesystem'); Config::set($filesystem_config, 'filesystem');
// 社微信开放平台 // 社微信开放平台

View File

@@ -25,7 +25,8 @@
"topthink/think-migration": "^3.0", "topthink/think-migration": "^3.0",
"topthink/think-helper": "^3.1", "topthink/think-helper": "^3.1",
"topthink/think-captcha": "^3.0", "topthink/think-captcha": "^3.0",
"topthink/think-multi-app": "^1.0" "topthink/think-multi-app": "^1.0",
"ulthon/user_hub_client": "^1.0"
}, },
"require-dev": { "require-dev": {
"symfony/var-dumper": "^4.2" "symfony/var-dumper": "^4.2"

View File

@@ -21,6 +21,16 @@ return [
// 可见性 // 可见性
'visibility' => 'public', 'visibility' => 'public',
], ],
'safe' => [
// 磁盘类型
'type' => 'local',
// 磁盘路径
'root' => app()->getRootPath() . 'safe/',
// 磁盘路径对应的外部URL路径
'url' => '/',
// 可见性
'visibility' => 'public',
],
// 更多的磁盘配置信息 // 更多的磁盘配置信息
], ],
]; ];

View File

@@ -0,0 +1,29 @@
body {
background-color: #e2e2e2;
}
.file-list {
padding: 15px;
margin : 15px;
border : 3px dashed #bbb;
min-height: 200px;
}
.file-item {
display : flex;
align-items : center;
justify-content: space-between;
background-color: #ddd;
padding: 5px;
cursor: pointer;
}
.file-item:hover{
background-color: #ccc;
}
.file-size{
margin-left: 15px;
color: #999;
}

View File

@@ -26,7 +26,7 @@ function renderUpload(target, params) {
var type = params.type var type = params.type
} }
if(typeof window.uploadAddressPrefix == 'undefined'){ if (typeof window.uploadAddressPrefix == 'undefined') {
window.uploadAddressPrefix = '/api/' window.uploadAddressPrefix = '/api/'
} }
@@ -109,4 +109,18 @@ function renderUpload(target, params) {
layer.close(window.uploading) layer.close(window.uploading)
} }
}) })
}
function formatSize(filesize) {
var value = filesize
if (null == value || value == '') {
return "0 Bytes";
}
var unitArr = new Array("Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB");
var index = 0;
var srcsize = parseFloat(value);
index = Math.floor(Math.log(srcsize) / Math.log(1024));
var size = srcsize / Math.pow(1024, index);
size = size.toFixed(2);//保留的小数位数
return size + unitArr[index];
} }

View File

@@ -0,0 +1,25 @@
目录说明
========================
```bash
├── Uploader.swf # SWF文件当使用Flash运行时需要引入。
├── webuploader.js # 完全版本。
├── webuploader.min.js # min版本
├── webuploader.flashonly.js # 只有Flash实现的版本。
├── webuploader.flashonly.min.js # min版本
├── webuploader.html5only.js # 只有Html5实现的版本。
├── webuploader.html5only.min.js # min版本
├── webuploader.noimage.js # 去除图片处理的版本包括HTML5和FLASH.
├── webuploader.noimage.min.js # min版本
├── webuploader.custom.js # 自定义打包方案,请查看 Gruntfile.js满足移动端使用。
└── webuploader.custom.min.js # min版本
```
## 示例
请把整个 Git 包下载下来放在 php 服务器下,因为默认提供的文件接受是用 php 编写的,打开 examples 页面便能查看示例效果。

Binary file not shown.

View File

@@ -0,0 +1,28 @@
.webuploader-container {
position: relative;
}
.webuploader-element-invisible {
position: absolute !important;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px,1px,1px,1px);
}
.webuploader-pick {
position: relative;
display: inline-block;
cursor: pointer;
background: #00b7ee;
padding: 10px 15px;
color: #fff;
text-align: center;
border-radius: 3px;
overflow: hidden;
}
.webuploader-pick-hover {
background: #00a2d4;
}
.webuploader-pick-disable {
opacity: 0.6;
pointer-events:none;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -2,105 +2,102 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>登陆</title> <title>登陆</title>
{include file="common/_require"} {include file="common/_require"}
<style> <style>
.login{ .login {
margin-top: 20vh; margin-top: 20vh;
} }
.title{ .title {
text-align: center; text-align: center;
font-size: 22px; font-size: 22px;
line-height: 2; line-height: 2;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="ul-container">
<div class="layui-container">
<div class="layui-row">
<div class="layui-col-md4 layui-col-md-offset4">
<div class="login">
<div class="title">
{:get_system_config('site_name')}
</div>
<div>
<form class="layui-form layui-form-pane" action="">
<div class="layui-form-item">
<label class="layui-form-label">用户名</label>
<div class="layui-input-block">
<input type="text" name="account" required lay-verify="required"
placeholder="请输入用户名" value="test(直接登陆即可)" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密码</label>
<div class="layui-input-block">
<input type="password" name="password" required lay-verify="required"
placeholder="请输入密码" value="test(直接登陆即可)" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">验证码</label>
<div class="layui-input-block">
<input type="text" name="captcha" required lay-verify="required"
placeholder="请输入验证码" autocomplete="off" value="test(直接登陆即可)" class="layui-input">
<img class="captcha" src="/api/Captcha/build" onclick="this.src = '/api/Captcha/build?v='+Math.random()" style="cursor: pointer;width:
100%;" alt="">
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn-fluid" lay-submit lay-filter="login">登陆</button>
</div>
</form>
</div> <div class="ul-container">
</div> <div class="layui-container">
</div> <div class="layui-row">
<div class="layui-col-md4 layui-col-md-offset4">
<div class="login">
<div class="title">
{:get_system_config('site_name')}
</div> </div>
<div>
<form class="layui-form layui-form-pane" action="">
<div class="layui-form-item">
<label class="layui-form-label">用户名</label>
<div class="layui-input-block">
<input type="text" name="account" required lay-verify="required" placeholder="请输入用户名" value="" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密码</label>
<div class="layui-input-block">
<input type="password" name="password" required lay-verify="required" placeholder="请输入密码" value="" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">验证码</label>
<div class="layui-input-block">
<input type="text" name="captcha" required lay-verify="required" placeholder="请输入验证码" autocomplete="off" value="" class="layui-input">
<img class="captcha" src="/api/Captcha/build" onclick="this.src = '/api/Captcha/build?v='+Math.random()" style="cursor: pointer;width:
100%;" alt="">
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn-fluid" lay-submit lay-filter="login">登陆</button>
</div>
</form>
</div>
</div>
</div> </div>
</div>
</div> </div>
</div>
<script>
layui.use(['form','layer'],function(){
var form = layui.form;
var loadLayer = null; <script>
layui.use(['form', 'layer'], function () {
var form = layui.form;
form.on('submit(login)',function(data){ var loadLayer = null;
loadLayer = layer.load(2);
$.post('{:url("admin/Login/auth")}',data.field,function(result){
layer.close(loadLayer);
if(result.code == 0){
layer.msg('登陆成功');
setTimeout(() => {
location.href = '{:url("admin/Index/index")}'
}, 1200);
}else{
$('.captcha').click()
layer.msg(result.msg);
}
})
return false; form.on('submit(login)', function (data) {
}) loadLayer = layer.load(2);
$.post('{:url("admin/Login/auth")}', data.field, function (result) {
layer.close(loadLayer);
if (result.code == 0) {
layer.msg('登陆成功');
setTimeout(() => {
location.href = '{:url("admin/Index/index")}'
}, 1200);
} else {
$('.captcha').click()
layer.msg(result.msg);
}
}) })
</script>
return false;
})
})
</script>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,15 @@
<link rel="stylesheet" href="//layui.ulthon.com/cdn/layui-ul-change.css">
<link rel="stylesheet" href="//layui.ulthon.com/cdn/layui-ul.css">
<link rel="shortcut icon" href="{:get_source_link(get_system_config('site_favicon','/favicon.ico'))}" type="image/x-icon">
<link rel="stylesheet" href="/static/css/reset.css">
<link rel="stylesheet" href="/static/css/pagination.css">
<link rel="stylesheet" href="/static/lib/layui/css/layui.css">
<link rel="stylesheet" href="/static/css/{$Request.cookie.skin_name|default='skin-1'}.css">
<link rel="stylesheet" href="/static/css/common.css">
<link rel="stylesheet" href="/static/css/index.css">
<script src="/static/lib/jquery/jquery-3.4.1.min.js"></script>
<script src="/static/lib/jquery/jquery.cookie.js"></script>
<script src="/static/lib/layui/layui.js"></script>
<script src="/static/js/common.js"></script>
{:get_system_config('site_tongji')}

View File

@@ -2,32 +2,240 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{:get_system_config('site_name')}</title> <title>{:get_system_config('site_name')}</title>
{:get_system_config('site_tongji')} {include file='common/_require'/}
<!-- <link rel="stylesheet" href="/static/lib/webuploader/webuploader.css"> -->
<style>
.webuploader-element-invisible {
display: none;
}
</style>
<script src="/static/lib/webuploader/webuploader.min.js"></script>
</head> </head>
<body> <body>
<textarea id="README" style="display: none"> <div class="ul-header ">
{:file_get_contents(app()->getRootPath().'README.md')} <div class="ul-header-main" style="max-width: 1000px;padding: 0 15px">
</textarea>
<div id="README-PRE" style="max-width: 800px;margin: 0 auto"> <div class="ul-header-left">
<div class="">
<img class="ul-header-logo" src="{:get_source_link(get_system_config('site_logo'))}" alt="">
</div>
</div>
<div class="ul-header-right">
<div class="ul-header-user">
<div class="ul-header-user-avatar">
</div>
<div class="ul-header-user-info">
<div class="ul-header-user-name">
奥古斯宏
</div>
<div class="ul-header-user-options">
<i class="ul-icon-exit"></i>
<span>退出</span>
</div>
</div>
</div>
</div>
</div> </div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/markdown-it/9.1.0/markdown-it.min.js"></script>
<script>
var md = window.markdownit();
var result = md.render($('#README').val());
$('#README-PRE').html(result)
</script>
</div>
<div class="layui-container" style="margin-top: 15px;;">
<div class="layui-row">
<div class="layui-card">
<div class="layui-card-header">发送文件</div>
<div class="layui-card-body">
<div class="file-list">
</div>
<div class="file-options">
<form class="layui-form layui-form-pane" action="">
<div class="layui-form-item">
<div class="layui-inline">
<div class="layui-btn-container">
<div class="layui-btn " id="picker">选择文件</div>
<button class="layui-btn submit" type="submit" lay-filter="submit" lay-submit>提交分享</button>
</div>
</div>
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">密码</label>
<div class="layui-input-inline">
<input type="password" name="password" autocomplete="off" placeholder="可以为空" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">下载次数</label>
<div class="layui-input-inline">
<input type="number" name="times" autocomplete="off" placeholder="不限制下载次数" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">保存时长</label>
<div class="layui-input-inline">
<select name="expire" lay-verify="">
<option value="3600">1小时</option>
<option value="10800">3小时</option>
<option value="28800">8小时</option>
<option value="86400">24小时</option>
<option value="259200">3天</option>
<option value="604800">7天</option>
<option value="0" disabled>永久</option>
</select>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div style="display: none;" class="tpl">
<div class="file-item">
<div class="file-info">
<span class="file-name">3600.zip</span>
<span class="file-size">16k</span>
</div>
<div class="file-options" style="display: flex;">
<span class="file-progress">等待上传</span>
<div class="layui-btn-container">
<i class="option-item layui-btn layui-btn-xs delete">删除</i>
<!-- <i class="option-item layui-btn layui-btn-xs pause" style="display: none;">暂停</i> -->
<!-- <i class="option-item layui-btn layui-btn-xs start" style="display: none;">继续</i> -->
<i class="option-item layui-btn layui-btn-xs retry" style="display: none;">重新上传</i>
</div>
</div>
</div>
</div>
<script>
var BASE_URL = '/static/lib/webuploader'
var uploader = WebUploader.create({
auto: true,
// swf文件路径
swf: BASE_URL + '/Uploader.swf',
paste: document.body,
dnd: '.file-list',
fileVal: 'file',
disableGlobalDnd: true,
// 文件接收服务端。
server: '{:url("File/webSave")}',
// 选择文件的按钮。可选。
// 内部根据当前运行是创建可能是input元素也可能是flash.
pick: '#picker',
// 不压缩image, 默认如果是jpeg文件上传前会压缩一把再上传
resize: false
});
uploader.on('fileQueued', function (file) {
var fileDom = $('.tpl .file-item').clone()
fileDom.addClass('file-item-' + file.id)
fileDom.find('.file-name').text(file.name)
fileDom.find('.file-size').text(formatSize(file.size))
fileDom.appendTo('.file-list')
fileDom.find('.option-item').hide()
fileDom.find('.delete').click(function () {
uploader.removeFile(file, true);
fileDom.remove()
}).show();
fileDom.find('.retry').click(function () {
uploader.retry(file)
})
fileDom.find('.start').click(function () {
uploader.upload(file)
})
fileDom.find('.pause').click(function () {
fileDom.find('.option-item').hide()
fileDom.find('.start').show()
uploader.stop(file)
})
});
uploader.on('uploadProgress', function (file, percentage) {
var percentTitle = parseInt(percentage * 100) + '%';
$('.file-item-' + file.id).find('.file-progress').text(percentTitle)
})
uploader.on('uploadStart', function (file) {
$('.file-item-' + file.id).find('.option-item').hide()
$('.file-item-' + file.id).find('.pause').show()
$('.file-item-' + file.id).find('.delete').show()
})
uploader.on('uploadError', function (file, reason) {
$('.file-item-' + file.id).find('.file-progress').text('上传错误:' + reason)
$('.file-item-' + file.id).find('.option-item').hide()
$('.file-item-' + file.id).find('.retry').show()
$('.file-item-' + file.id).find('.delete').show()
})
uploader.on('uploadSuccess', function (file, reason) {
$('.file-item-' + file.id).find('.file-progress').text('上传成功');
$('.file-item-' + file.id).data('success', reason.data);
$('.file-item-' + file.id).addClass('success')
$('.file-item-' + file.id).find('.option-item').hide()
$('.file-item-' + file.id).find('.delete').show()
})
layui.form.on('submit(submit)', function (data) {
if (uploader.isInProgress()) {
layer.msg("正在上传中,请等待上传完成后操作");
return false;
}
if ($('.file-list .file-item').length == 0) {
layer.msg('请选择上传文件')
return false;
}
if ($('.file-list .file-item').length != $('.file-list .file-item.success').length) {
layer.msg('部分文件上传失败,请重新上传或从队列中删除')
return false;
}
var fileList = []
$('.file-list .file-item').each(function(index,elem){
var fileData = $(elem).data('success')
console.log(fileData);
fileList.push(fileData)
})
data.field.file_list = fileList
console.log(data.field);
return false;
})
</script>
</body> </body>
</html> </html>