Files

427 lines
14 KiB
PHP

<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
namespace ywxapp\services;
use think\facade\Filesystem;
use think\facade\Request;
/**
* UEditorService 类
*
* @author ywxapp <admin@ywxapp.cn>
*/
class UEditorService
{
public function __construct()
{
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
if (request()->method() == 'OPTIONS') {
exit();
}
}
/**
* UEditor 后端接口
*/
public function index()
{
$action = Request::param('action');
switch ($action) {
case 'config':
$result = $this->getConfig();
break;
case 'uploadimage':
$result = $this->uploadImage();
break;
case 'uploadscrawl':
$result = $this->uploadScrawl();
break;
case 'uploadvideo':
$result = $this->uploadVideo();
break;
case 'uploadfile':
$result = $this->uploadFile();
break;
case 'listimage':
$result = $this->listImage();
break;
case 'listfile':
$result = $this->listFile();
break;
case 'catchimage':
$result = $this->catchImage();
break;
default:
$result = [
'state' => '请求地址出错',
];
break;
}
return json($result);
}
/**
* 获取配置
*/
private function getConfig()
{
return [
/* 上传图片配置项 */
"imageActionName" => "uploadimage",
"imageFieldName" => "upfile",
"imageMaxSize" => 2048000,
"imageAllowFiles" => [".png", ".jpg", ".jpeg", ".gif", ".bmp"],
"imageCompressEnable" => true,
"imageCompressBorder" => 1600,
"imageInsertAlign" => "none",
"imageUrlPrefix" => "",
/* 涂鸦图片上传配置项 */
"scrawlActionName" => "uploadscrawl",
"scrawlFieldName" => "upfile",
"scrawlMaxSize" => 2048000,
"scrawlUrlPrefix" => "",
"scrawlInsertAlign" => "none",
/* 截图工具上传 */
"snapscreenActionName" => "uploadimage",
"snapscreenUrlPrefix" => "",
"snapscreenInsertAlign" => "none",
/* 抓取远程图片配置 */
"catcherLocalDomain" => ["127.0.0.1", "localhost"],
"catcherActionName" => "catchimage",
"catcherFieldName" => "source",
"catcherUrlPrefix" => "",
"catcherMaxSize" => 2048000,
"catcherAllowFiles" => [".png", ".jpg", ".jpeg", ".gif", ".bmp"],
/* 上传视频配置 */
"videoActionName" => "uploadvideo",
"videoFieldName" => "upfile",
"videoMaxSize" => 102400000,
"videoUrlPrefix" => "",
"videoAllowFiles" => [
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
],
/* 上传文件配置 */
"fileActionName" => "uploadfile",
"fileFieldName" => "upfile",
"fileMaxSize" => 51200000,
"fileUrlPrefix" => "",
"fileAllowFiles" => [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml",
],
/* 列出指定目录下的图片 */
"imageManagerActionName" => "listimage",
"imageManagerListPath" => "/upload/image/",
"imageManagerListSize" => 20,
"imageManagerUrlPrefix" => "",
"imageManagerInsertAlign" => "none",
"imageManagerAllowFiles" => [".png", ".jpg", ".jpeg", ".gif", ".bmp"],
/* 列出指定目录下的文件 */
"fileManagerActionName" => "listfile",
"fileManagerListPath" => "/upload/file/",
"fileManagerListSize" => 20,
"fileManagerUrlPrefix" => "",
"fileManagerAllowFiles" => [
".png", ".jpg", ".jpeg", ".gif", ".bmp",
".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg",
".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso",
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml",
],
/* 配置项 */
"imageUrlPrefix" => Request::domain() , /* 图片访问路径前缀 */
"scrawlUrlPrefix" => Request::domain() , /* 涂鸦访问路径前缀 */
"snapscreenUrlPrefix" => Request::domain() , /* 截图访问路径前缀 */
"catcherUrlPrefix" => Request::domain() , /* 图片访问路径前缀 */
"videoUrlPrefix" => Request::domain() , /* 视频访问路径前缀 */
"fileUrlPrefix" => Request::domain() , /* 文件访问路径前缀 */
"imagePathFormat" => "/uploads/images/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */
"scrawlPathFormat" => "/uploads/scrawl/{yyyy}{mm}{dd}/{time}{rand:6}", /* 涂鸦上传保存路径 */
"snapscreenPathFormat" => "/uploads/images/{yyyy}{mm}{dd}/{time}{rand:6}", /* 截图上传保存路径 */
"catcherPathFormat" => "/uploads/images/{yyyy}{mm}{dd}/{time}{rand:6}", /* 抓取图片上传保存路径 */
"videoPathFormat" => "/uploads/videos/{yyyy}{mm}{dd}/{time}{rand:6}", /* 视频上传保存路径 */
"filePathFormat" => "/uploads/files/{yyyy}{mm}{dd}/{time}{rand:6}", /* 文件上传保存路径 */
];
}
/**
* 上传图片
*/
private function uploadImage()
{
return $this->upload('image');
}
/**
* 上传涂鸦
*/
private function uploadScrawl()
{
$file = Request::post('upfile');
if (empty($file)) {
return ['state' => '上传文件为空'];
}
// 生成文件名
$filename = date('Ymd') . '/' . uniqid() . '.png';
// 保存路径
$savePath = 'uploads/scrawl/' . $filename;
$fullPath = public_path() . $savePath;
// 确保目录存在
$dir = dirname($fullPath);
if (! is_dir($dir)) {
mkdir($dir, 0755, true);
}
// 解码并保存base64图片
$img = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $file));
file_put_contents($fullPath, $img);
return [
"state" => "SUCCESS",
"url" => Request::domain() . '/' . $savePath,
"title" => $filename,
"original" => "scrawl.png",
"type" => ".png",
"size" => strlen($img),
];
}
/**
* 上传视频
*/
private function uploadVideo()
{
return $this->upload('video');
}
/**
* 上传文件
*/
private function uploadFile()
{
return $this->upload('file');
}
/**
* 通用上传方法
*/
private function upload($type)
{
$file = Request::file('upfile');
if (empty($file)) {
return ['state' => '上传文件为空'];
}
// 验证文件
$config = $this->getConfig();
$maxSize = $config[$type . 'MaxSize'];
$allowFiles = $config[$type . 'AllowFiles'];
try {
$validate = [
'size' => $maxSize,
'ext' => implode(',', array_map(function ($ext) {
return ltrim($ext, '.');
}, $allowFiles)),
];
$savename = Filesystem::disk('public')->putFile('uploads/' . $type, $file);
return [
"state" => "SUCCESS",
"url" => '/storage/' . $savename,
"title" => basename($savename),
"original" => $file->getOriginalName(),
"type" => '.' . $file->extension(),
"size" => $file->getSize(),
];
} catch (\Exception $e) {
return ['state' => $e->getMessage()];
}
}
/**
* 列出图片
*/
private function listImage()
{
return $this->listFiles('image');
}
/**
* 列出文件
*/
private function listFile()
{
return $this->listFiles('file');
}
/**
* 列出文件通用方法
*/
private function listFiles($type)
{
$config = $this->getConfig();
$allowFiles = $config[$type . 'ManagerAllowFiles'];
$listSize = $config[$type . 'ManagerListSize'];
$path = $config[$type . 'ManagerListPath'];
$start = Request::param('start', 0);
$size = Request::param('size', $listSize);
$files = $this->getFiles(public_path() . $path, $allowFiles);
if (! count($files)) {
return [
"state" => "no match file",
"list" => [],
"start" => $start,
"total" => 0,
];
}
// 排序
usort($files, function ($a, $b) {
return strcmp($a['mtime'], $b['mtime']);
});
// 分页
$files = array_slice($files, $start, $size);
return [
"state" => "SUCCESS",
"list" => $files,
"start" => $start,
"total" => count($files),
];
}
/**
* 获取目录下的文件
*/
private function getFiles($path, $allowFiles, &$files = [])
{
if (! is_dir($path)) {
return [];
}
$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("/\.(" . str_replace('.', '', implode('|', $allowFiles)) . ")$/i", $file)) {
$files[] = [
'url' => Request::domain() . str_replace(public_path(), '', $path2),
'mtime' => filemtime($path2),
];
}
}
}
}
return $files;
}
/**
* 抓取远程图片
*/
private function catchImage()
{
$sources = Request::param('source', []);
if (empty($sources)) {
return ['state' => '参数错误:没有指定抓取源'];
}
$list = [];
foreach ($sources as $imgUrl) {
// 验证域名
$config = $this->getConfig();
$domain = parse_url($imgUrl, PHP_URL_HOST);
if (! in_array($domain, $config['catcherLocalDomain'])) {
$list[] = [
'state' => '抓取远程图片失败,域名不允许',
'url' => $imgUrl,
'source' => $imgUrl,
];
continue;
}
// 获取图片
$imgContent = file_get_contents($imgUrl);
if ($imgContent === false) {
$list[] = [
'state' => '抓取远程图片失败',
'url' => $imgUrl,
'source' => $imgUrl,
];
continue;
}
// 验证图片
$imgInfo = getimagesizefromstring($imgContent);
if (! $imgInfo) {
$list[] = [
'state' => '不是有效的图片文件',
'url' => $imgUrl,
'source' => $imgUrl,
];
continue;
}
// 生成文件名
$ext = image_type_to_extension($imgInfo[2]);
$filename = date('Ymd') . '/' . uniqid() . $ext;
$savePath = 'uploads/images/' . $filename;
$fullPath = public_path() . $savePath;
// 确保目录存在
$dir = dirname($fullPath);
if (! is_dir($dir)) {
mkdir($dir, 0755, true);
}
// 保存文件
file_put_contents($fullPath, $imgContent);
$list[] = [
'state' => 'SUCCESS',
'url' => Request::domain() . '/' . $savePath,
'source' => $imgUrl,
];
}
return [
'state' => count($list) ? 'SUCCESS' : 'ERROR',
'list' => $list,
];
}
}