You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

796 lines
29 KiB
PHP

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace app\admin\controller;
use app\common\builder\ZBuilder;
use app\admin\model\Attachment as AttachmentModel;
use think\Image;
use think\File;
use think\facade\Hook;
use think\Db;
use think\facade\Env;
/**
* 附件控制器
* @package app\admin\controller
*/
class Attachment extends Admin
{
/**
* 附件列表
* @author 蔡伟明 <314013107@qq.com>
*/
public function index()
{
// 查询
$map = $this->getMap();
// 数据列表
$data_list = AttachmentModel::where($map)->order('sort asc,id desc')->paginate();
foreach ($data_list as $key => &$value) {
if (in_array(strtolower($value['ext']), ['jpg', 'jpeg', 'png', 'gif', 'bmp'])) {
if ($value['driver'] == 'local') {
$thumb = $value['thumb'] != '' ? $value['thumb'] : $value['path'];
$value['type'] = '<div class="js-gallery"><img class="image" title="点击查看大图" data-original="'. PUBLIC_PATH . $value['path'].'" src="'. PUBLIC_PATH . $thumb.'"></div>';
} else {
$value['type'] = '<div class="js-gallery"><img class="image" title="点击查看大图" data-original="'. $value['path'].'" src="'. $value['path'].'"></div>';
}
} else {
if ($value['driver'] == 'local') {
$path = PUBLIC_PATH. $value['path'];
} else {
$path = $value['path'];
}
if (is_file('.'.config('public_static_path').'admin/img/files/'.$value['ext'].'.png')) {
$value['type'] = '<a href="'. $path.'"
data-toggle="tooltip" title="点击下载">
<img class="image" src="'.config('public_static_path').'admin/img/files/'.$value['ext'].'.png"></a>';
} else {
$value['type'] = '<a href="'. $path.'"
data-toggle="tooltip" title="点击下载">
<img class="image" src="'.config('public_static_path').'admin/img/files/file.png"></a>';
}
}
}
// 使用ZBuilder快速创建数据表格
return ZBuilder::make('table')
->setSearch(['name' => '名称']) // 设置搜索框
->addColumns([ // 批量添加数据列
['id', 'ID'],
['type', '类型'],
['name', '名称'],
['size', '大小', 'byte'],
['driver', '上传驱动', parse_attr(Db::name('admin_config')->where('name', 'upload_driver')->value('options'))],
['create_time', '上传时间', 'datetime'],
['status', '状态', 'switch'],
['right_button', '操作', 'btn']
])
->addTopButtons('enable,disable,delete') // 批量添加顶部按钮
->addRightButtons('delete') // 批量添加右侧按钮
->setRowList($data_list) // 设置表格数据
->fetch(); // 渲染模板
}
/**
* 上传附件
* @param string $dir 保存的目录:images,files,videos,voices
* @param string $from 来源wangeditorwangEditor编辑器, ueditor:ueditor编辑器, editormd:editormd编辑器等
* @param string $module 来自哪个模块
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function upload($dir = '', $from = '', $module = '')
{
// 临时取消执行时间限制
set_time_limit(0);
if ($dir == '') $this->error('没有指定上传目录');
if ($from == 'ueditor') return $this->ueditor();
if ($from == 'jcrop') return $this->jcrop();
return $this->saveFile($dir, $from, $module);
}
/**
* 保存附件
* @param string $dir 附件存放的目录
* @param string $from 来源
* @param string $module 来自哪个模块
* @author 蔡伟明 <314013107@qq.com>
* @return string|\think\response\Json
*/
private function saveFile($dir = '', $from = '', $module = '')
{
// 附件大小限制
$size_limit = $dir == 'images' ? config('upload_image_size') : config('upload_file_size');
$size_limit = $size_limit * 1024;
// 附件类型限制
$ext_limit = $dir == 'images' ? config('upload_image_ext') : config('upload_file_ext');
$ext_limit = $ext_limit != '' ? parse_attr($ext_limit) : '';
// 缩略图参数
$thumb = $this->request->post('thumb', '');
// 水印参数
$watermark = $this->request->post('watermark', '');
// 获取附件数据
$callback = '';
switch ($from) {
case 'editormd':
$file_input_name = 'editormd-image-file';
break;
case 'ckeditor':
$file_input_name = 'upload';
$callback = $this->request->get('CKEditorFuncNum');
break;
case 'ueditor_scrawl':
return $this->saveScrawl();
break;
default:
$file_input_name = 'file';
}
$file = $this->request->file($file_input_name);
// 判断附件是否已存在
if ($file_exists = AttachmentModel::get(['md5' => $file->hash('md5')])) {
if ($file_exists['driver'] == 'local') {
$file_path = PUBLIC_PATH. $file_exists['path'];
} else {
$file_path = $file_exists['path'];
}
// 附件已存在
return $this->uploadSuccess($from, $file_path, $file_exists['name'], $file_exists['id'], $callback);
}
// 判断附件大小是否超过限制
if ($size_limit > 0 && ($file->getInfo('size') > $size_limit)) {
return $this->uploadError($from, '附件过大', $callback);
}
// 判断附件格式是否符合
$file_name = $file->getInfo('name');
$file_ext = strtolower(substr($file_name, strrpos($file_name, '.')+1));
$error_msg = '';
if ($ext_limit == '') {
$error_msg = '获取文件信息失败!';
}
if ($file->getMime() == 'text/x-php' || $file->getMime() == 'text/html') {
$error_msg = '禁止上传非法文件!';
}
if (preg_grep("/php/i", $ext_limit)) {
$error_msg = '禁止上传非法文件!';
}
if (!preg_grep("/$file_ext/i", $ext_limit)) {
$error_msg = '附件类型不正确!';
}
if ($error_msg != '') {
// 上传错误
return $this->uploadError($from, $error_msg, $callback);
}
// 附件上传钩子,用于第三方文件上传扩展
if (config('upload_driver') != 'local') {
$hook_result = Hook::listen('upload_attachment', ['file' => $file, 'from' => $from, 'module' => $module], true);
if (false !== $hook_result) {
return $hook_result;
}
}
// 移动到框架应用根目录/uploads/ 目录下
$info = $file->move(config('upload_path') . DIRECTORY_SEPARATOR . $dir);
if($info){
// 缩略图路径
$thumb_path_name = '';
// 图片宽度
$img_width = '';
// 图片高度
$img_height = '';
if ($dir == 'images') {
$img = Image::open($info);
$img_width = $img->width();
$img_height = $img->height();
// 水印功能
if ($watermark == '') {
if (config('upload_thumb_water') == 1 && config('upload_thumb_water_pic') > 0) {
$this->create_water($info->getRealPath(), config('upload_thumb_water_pic'));
}
} else {
if (strtolower($watermark) != 'close') {
list($watermark_img, $watermark_pos, $watermark_alpha) = explode('|', $watermark);
$this->create_water($info->getRealPath(), $watermark_img, $watermark_pos, $watermark_alpha);
}
}
// 生成缩略图
if ($thumb == '') {
if (config('upload_image_thumb') != '') {
$thumb_path_name = $this->create_thumb($info, $info->getPathInfo()->getfileName(), $info->getFilename());
}
} else {
if (strtolower($thumb) != 'close') {
list($thumb_size, $thumb_type) = explode('|', $thumb);
$thumb_path_name = $this->create_thumb($info, $info->getPathInfo()->getfileName(), $info->getFilename(), $thumb_size, $thumb_type);
}
}
}
// 获取附件信息
$file_info = [
'uid' => session('user_auth.uid'),
'name' => $file->getInfo('name'),
'mime' => $file->getInfo('type'),
'path' => 'uploads/' . $dir . '/' . str_replace('\\', '/', $info->getSaveName()),
'ext' => $info->getExtension(),
'size' => $info->getSize(),
'md5' => $info->hash('md5'),
'sha1' => $info->hash('sha1'),
'thumb' => $thumb_path_name,
'module' => $module,
'width' => $img_width,
'height' => $img_height,
];
// 写入数据库
if ($file_add = AttachmentModel::create($file_info)) {
$file_path = PUBLIC_PATH. $file_info['path'];
return $this->uploadSuccess($from, $file_path, $file_info['name'], $file_add['id'], $callback);
} else {
return $this->uploadError($from, '上传失败', $callback);
}
}else{
return $this->uploadError($from, $file->getError(), $callback);
}
}
/**
* 处理ueditor上传
* @author 蔡伟明 <314013107@qq.com>
* @return string|\think\response\Json
*/
private function ueditor(){
$action = $this->request->get('action');
$config_file = './static/libs/ueditor/php/config.json';
$config = json_decode(preg_replace("/\/\*[\s\S]+?\*\//", "", file_get_contents($config_file)), true);
switch ($action) {
/* 获取配置信息 */
case 'config':
$result = $config;
break;
/* 上传图片 */
case 'uploadimage':
return $this->saveFile('images', 'ueditor');
break;
/* 上传涂鸦 */
case 'uploadscrawl':
return $this->saveFile('images', 'ueditor_scrawl');
break;
/* 上传视频 */
case 'uploadvideo':
return $this->saveFile('videos', 'ueditor');
break;
/* 上传附件 */
case 'uploadfile':
return $this->saveFile('files', 'ueditor');
break;
/* 列出图片 */
case 'listimage':
return $this->showFile('listimage', $config);
break;
/* 列出附件 */
case 'listfile':
return $this->showFile('listfile', $config);
break;
/* 抓取远程附件 */
// case 'catchimage':
// $result = include("action_crawler.php");
// break;
default:
$result = ['state' => '请求地址出错'];
break;
}
/* 输出结果 */
if (isset($_GET["callback"])) {
if (preg_match("/^[\w_]+$/", $_GET["callback"])) {
return htmlspecialchars($_GET["callback"]) . '(' . $result . ')';
} else {
return json(['state' => 'callback参数不合法']);
}
} else {
return json($result);
}
}
/**
* 保存涂鸦ueditor
* @author 蔡伟明 <314013107@qq.com>
* @return \think\response\Json
*/
private function saveScrawl()
{
$file = $this->request->post('file');
$file_content = base64_decode($file);
$file_name = md5($file) . '.jpg';
$dir = config('upload_path') . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . date('Ymd', $this->request->time());
$file_path = $dir . DIRECTORY_SEPARATOR . $file_name;
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
if (false === file_put_contents($file_path, $file_content)) {
return json(['state' => '涂鸦上传出错']);
}
$file = new File($file_path);
$img = Image::open($file);
$file_info = [
'uid' => session('user_auth.uid'),
'name' => $file_name,
'mime' => 'image/png',
'path' => 'uploads/images/' . date('Ymd', $this->request->time()) . '/' . $file_name,
'ext' => 'png',
'size' => $file->getSize(),
'md5' => $file->hash('md5'),
'sha1' => $file->hash('sha1'),
'module' => $this->request->module(),
'width' => $img->width(),
'height' => $img->height()
];
if ($file_add = AttachmentModel::create($file_info)) {
// 返回成功信息
return json([
"state" => "SUCCESS", // 上传状态,上传成功时必须返回"SUCCESS"
"url" => PUBLIC_PATH. $file_info['path'], // 返回的地址
"title" => $file_info['name'], // 附件名
]);
} else {
return json(['state' => '涂鸦上传出错']);
}
}
/**
* 显示附件列表ueditor
* @param string $type 类型
* @param $config
* @author 蔡伟明 <314013107@qq.com>
* @return \think\response\Json
*/
private function showFile($type, $config){
/* 判断类型 */
switch ($type) {
/* 列出附件 */
case 'listfile':
$allowFiles = $config['fileManagerAllowFiles'];
$listSize = $config['fileManagerListSize'];
$path = realpath(config('upload_path') .'/files/');
break;
/* 列出图片 */
case 'listimage':
default:
$allowFiles = $config['imageManagerAllowFiles'];
$listSize = $config['imageManagerListSize'];
$path = realpath(config('upload_path') .'/images/');
}
$allowFiles = substr(str_replace(".", "|", join("", $allowFiles)), 1);
/* 获取参数 */
$size = isset($_GET['size']) ? htmlspecialchars($_GET['size']) : $listSize;
$start = isset($_GET['start']) ? htmlspecialchars($_GET['start']) : 0;
$end = $start + $size;
/* 获取附件列表 */
$files = $this->getfiles($path, $allowFiles);
if (!count($files)) {
return json(array(
"state" => "no match file",
"list" => array(),
"start" => $start,
"total" => count($files)
));
}
/* 获取指定范围的列表 */
$len = count($files);
for ($i = min($end, $len) - 1, $list = array(); $i < $len && $i >= 0 && $i >= $start; $i--){
$list[] = $files[$i];
}
//倒序
//for ($i = $end, $list = array(); $i < $len && $i < $end; $i++){
// $list[] = $files[$i];
//}
/* 返回数据 */
$result = array(
"state" => "SUCCESS",
"list" => $list,
"start" => $start,
"total" => count($files)
);
return json($result);
}
/**
* 处理Jcrop图片裁剪
* @author 蔡伟明 <314013107@qq.com>
*/
private function jcrop()
{
$file_path = $this->request->post('path', '');
$cut_info = $this->request->post('cut', '');
$thumb = $this->request->post('thumb', '');
$watermark = $this->request->post('watermark', '');
$module = $this->request->param('module', '');
// 上传图片
if ($file_path == '') {
$file = $this->request->file('file');
// 附件类型限制
$ext_limit = config('upload_image_ext');
$ext_limit = $ext_limit != '' ? parse_attr($ext_limit) : '';
// 判断附件格式是否符合
$file_name = $file->getInfo('name');
$file_ext = strtolower(substr($file_name, strrpos($file_name, '.')+1));
if ($ext_limit == '') {
$this->error('获取文件信息失败!');
}
if (strtolower($file_ext) == 'php') {
$this->error('禁止上传非法文件!');
}
if ($file->getMime() == 'text/x-php' || $file->getMime() == 'text/html') {
$this->error('禁止上传非法文件!');
}
if (preg_grep("/php/i", $ext_limit)) {
$this->error('禁止上传非法文件!');
}
if (!preg_grep("/$file_ext/i", $ext_limit)) {
$this->error('附件类型不正确!');
}
if (!is_dir(config('upload_temp_path'))) {
mkdir(config('upload_temp_path'), 0766, true);
}
$info = $file->move(config('upload_temp_path'), $file->hash('md5'));
if ($info) {
return json(['code' => 1, 'src' => PUBLIC_PATH. 'uploads/temp/'. $info->getFilename()]);
} else {
$this->error('上传失败');
}
}
$file_path = config('upload_temp_path') . str_replace(PUBLIC_PATH. 'uploads/temp/', '', $file_path);
if (is_file($file_path)) {
// 获取裁剪信息
$cut_info = explode(',', $cut_info);
// 读取图片
$image = Image::open($file_path);
$dir_name = date('Ymd');
$file_dir = config('upload_path') . DIRECTORY_SEPARATOR . 'images/' . $dir_name . '/';
if (!is_dir($file_dir)) {
mkdir($file_dir, 0766, true);
}
$file_name = md5(microtime(true)) . '.' . $image->type();
$new_file_path = $file_dir . $file_name;
// 裁剪图片
$image->crop($cut_info[0], $cut_info[1], $cut_info[2], $cut_info[3], $cut_info[4], $cut_info[5])->save($new_file_path);
// 水印功能
if ($watermark == '') {
if (config('upload_thumb_water') == 1 && config('upload_thumb_water_pic') > 0) {
$this->create_water($new_file_path, config('upload_thumb_water_pic'));
}
} else {
if (strtolower($watermark) != 'close') {
list($watermark_img, $watermark_pos, $watermark_alpha) = explode('|', $watermark);
$this->create_water($new_file_path, $watermark_img, $watermark_pos, $watermark_alpha);
}
}
// 是否创建缩略图
$thumb_path_name = '';
if ($thumb == '') {
if (config('upload_image_thumb') != '') {
$thumb_path_name = $this->create_thumb($new_file_path, $dir_name, $file_name);
}
} else {
if (strtolower($thumb) != 'close') {
list($thumb_size, $thumb_type) = explode('|', $thumb);
$thumb_path_name = $this->create_thumb($new_file_path, $dir_name, $file_name, $thumb_size, $thumb_type);
}
}
// 保存图片
$file = new File($new_file_path);
$file_info = [
'uid' => session('user_auth.uid'),
'name' => $file_name,
'mime' => $image->mime(),
'path' => 'uploads/images/' . $dir_name . '/' . $file_name,
'ext' => $image->type(),
'size' => $file->getSize(),
'md5' => $file->hash('md5'),
'sha1' => $file->hash('sha1'),
'thumb' => $thumb_path_name,
'module' => $module,
'width' => $image->width(),
'height' => $image->height()
];
if ($file_add = AttachmentModel::create($file_info)) {
// 删除临时图片
unlink($file_path);
// 返回成功信息
return json([
'code' => 1,
'id' => $file_add['id'],
'src' => PUBLIC_PATH . $file_info['path'],
'thumb' => $thumb_path_name == '' ? '' : PUBLIC_PATH . $thumb_path_name,
]);
} else {
$this->error('上传失败');
}
}
$this->error('文件不存在');
}
/**
* 创建缩略图
* @param string $file 目标文件,可以是文件对象或文件路径
* @param string $dir 保存目录,即目标文件所在的目录名
* @param string $save_name 缩略图名
* @param string $thumb_size 尺寸
* @param string $thumb_type 裁剪类型
* @author 蔡伟明 <314013107@qq.com>
* @return string 缩略图路径
*/
private function create_thumb($file = '', $dir = '', $save_name = '', $thumb_size = '', $thumb_type = '')
{
// 获取要生成的缩略图最大宽度和高度
$thumb_size = $thumb_size == '' ? config('upload_image_thumb') : $thumb_size;
list($thumb_max_width, $thumb_max_height) = explode(',', $thumb_size);
// 读取图片
$image = Image::open($file);
// 生成缩略图
$thumb_type = $thumb_type == '' ? config('upload_image_thumb_type') : $thumb_type;
$image->thumb($thumb_max_width, $thumb_max_height, $thumb_type);
// 保存缩略图
$thumb_path = config('upload_path') . DIRECTORY_SEPARATOR . 'images/' . $dir . '/thumb/';
if (!is_dir($thumb_path)) {
mkdir($thumb_path, 0766, true);
}
$thumb_path_name = $thumb_path. $save_name;
$image->save($thumb_path_name);
$thumb_path_name = 'uploads/images/' . $dir . '/thumb/' . $save_name;
return $thumb_path_name;
}
/**
* 添加水印
* @param string $file 要添加水印的文件路径
* @param string $watermark_img 水印图片id
* @param string $watermark_pos 水印位置
* @param string $watermark_alpha 水印透明度
* @author 蔡伟明 <314013107@qq.com>
*/
private function create_water($file = '', $watermark_img = '', $watermark_pos = '', $watermark_alpha = '')
{
$path = model('admin/attachment')->getFilePath($watermark_img, 1);
$thumb_water_pic = realpath(Env::get('root_path') . 'public/' . $path);
if (is_file($thumb_water_pic)) {
// 读取图片
$image = Image::open($file);
// 添加水印
$watermark_pos = $watermark_pos == '' ? config('upload_thumb_water_position') : $watermark_pos;
$watermark_alpha = $watermark_alpha == '' ? config('upload_thumb_water_alpha') : $watermark_alpha;
$image->water($thumb_water_pic, $watermark_pos, $watermark_alpha);
// 保存水印图片,覆盖原图
$image->save($file);
}
}
/**
* 上传成功信息
* @param $from
* @param string $file_path
* @param string $file_name
* @param string $file_id
* @param string $callback
* @return string|\think\response\Json
* @author 蔡伟明 <314013107@qq.com>
*/
private function uploadSuccess($from, $file_path = '', $file_name = '', $file_id = '', $callback = '')
{
switch ($from) {
case 'wangeditor':
return $file_path;
break;
case 'ueditor':
return json([
"state" => "SUCCESS", // 上传状态,上传成功时必须返回"SUCCESS"
"url" => $file_path, // 返回的地址
"title" => $file_name, // 附件名
]);
break;
case 'editormd':
return json([
"success" => 1,
"message" => '上传成功',
"url" => $file_path,
]);
break;
case 'ckeditor':
return ck_js($callback, $file_path);
break;
default:
return json([
'code' => 1,
'info' => '上传成功',
'class' => 'success',
'id' => $file_id,
'path' => $file_path
]);
}
}
/**
* 上传错误信息
* @param $from
* @param string $msg
* @param string $callback
* @return string|\think\response\Json
* @author 蔡伟明 <314013107@qq.com>
*/
private function uploadError($from, $msg = '', $callback = '')
{
switch ($from) {
case 'wangeditor':
return "error|".$msg;
break;
case 'ueditor':
return json(['state' => $msg]);
break;
case 'editormd':
return json(["success" => 0, "message" => $msg]);
break;
case 'ckeditor':
return ck_js($callback, '', $msg);
break;
default:
return json([
'code' => 0,
'class' => 'danger',
'info' => $msg
]);
}
}
/**
* 遍历获取目录下的指定类型的附件
* @param string $path 路径
* @param string $allowFiles 允许查看的类型
* @param array $files 文件列表
* @author 蔡伟明 <314013107@qq.com>
* @return array|null
*/
public function getfiles($path = '', $allowFiles = '', &$files = array())
{
if (!is_dir($path)) return null;
if(substr($path, strlen($path) - 1) != '/') $path .= '/';
$handle = opendir($path);
while (false !== ($file = readdir($handle))) {
if ($file != '.' && $file != '..') {
$path2 = $path . $file;
if (is_dir($path2)) {
$this->getfiles($path2, $allowFiles, $files);
} else {
if (preg_match("/\.(".$allowFiles.")$/i", $file)) {
$files[] = array(
'url'=> str_replace("\\", "/", substr($path2, strlen($_SERVER['DOCUMENT_ROOT']))),
'mtime'=> filemtime($path2)
);
}
}
}
}
return $files;
}
/**
* 启用附件
* @param array $record 行为日志
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function enable($record = [])
{
return $this->setStatus('enable');
}
/**
* 禁用附件
* @param array $record 行为日志
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function disable($record = [])
{
return $this->setStatus('disable');
}
/**
* 设置附件状态:删除、禁用、启用
* @param string $type 类型delete/enable/disable
* @param array $record
* @author 蔡伟明 <314013107@qq.com>
* @throws \think\Exception
* @throws \think\exception\PDOException
*/
public function setStatus($type = '', $record = [])
{
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
$ids = is_array($ids) ? implode(',', $ids) : $ids;
return parent::setStatus($type, ['attachment_'.$type, 'admin_attachment', 0, UID, $ids]);
}
/**
* 删除附件
* @param string $ids 附件id
* @author 蔡伟明 <314013107@qq.com>
* @throws \think\Exception
* @throws \think\exception\PDOException
*/
public function delete($ids = '')
{
$ids = $this->request->isPost() ? input('post.ids/a') : input('param.ids');
if (empty($ids)) $this->error('缺少主键');
$files_path = AttachmentModel::where('id', 'in', $ids)->column('path,thumb', 'id');
foreach ($files_path as $value) {
$real_path = realpath(config('upload_path').'/../'.$value['path']);
$real_path_thumb = realpath(config('upload_path').'/../'.$value['thumb']);
if (is_file($real_path) && !unlink($real_path)) {
$this->error('删除失败');
}
if (is_file($real_path_thumb) && !unlink($real_path_thumb)) {
$this->error('删除缩略图失败');
}
}
if (AttachmentModel::where('id', 'in', $ids)->delete()) {
// 记录行为
$ids = is_array($ids) ? implode(',', $ids) : $ids;
action_log('attachment_delete', 'admin_attachment', 0, UID, $ids);
$this->success('删除成功');
} else {
$this->error('删除失败');
}
}
/**
* 快速编辑
* @param array $record 行为日志
* @author 蔡伟明 <314013107@qq.com>
* @return mixed
*/
public function quickEdit($record = [])
{
$id = input('post.pk', '');
return parent::quickEdit(['attachment_edit', 'admin_attachment', 0, UID, $id]);
}
}