文件优化
This commit is contained in:
parent
3d7ea601f6
commit
72f0eb51bd
@ -1,13 +0,0 @@
|
|||||||
### 该问题是怎么引起的?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 重现步骤
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 报错信息
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
### 相关的Issue
|
|
||||||
|
|
||||||
|
|
||||||
### 原因(目的、解决的问题等)
|
|
||||||
|
|
||||||
|
|
||||||
### 描述(做了什么,变更了什么)
|
|
||||||
|
|
||||||
|
|
||||||
### 测试用例(新增、改动、可能影响的功能)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,11 +1,11 @@
|
|||||||
/.idea
|
/.idea
|
||||||
/.vscode
|
/.vscode
|
||||||
/vendor
|
/.gitee
|
||||||
|
/vendor/*
|
||||||
*.log
|
*.log
|
||||||
.env
|
|
||||||
/.env
|
/.env
|
||||||
/public/storage
|
/public/storage/*
|
||||||
/runtime
|
/runtime
|
||||||
/public/.user.ini
|
/public/.user.ini
|
||||||
/public/backup
|
/public/backup/*
|
||||||
/config/install.lock
|
/config/install.lock
|
||||||
|
425
extend/avatars/MDAvatars.php
Normal file
425
extend/avatars/MDAvatars.php
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace avatars;
|
||||||
|
|
||||||
|
class MDAvatars
|
||||||
|
{
|
||||||
|
public $Char;
|
||||||
|
public $AvatarSize;
|
||||||
|
public $Shape;
|
||||||
|
public $Padding;
|
||||||
|
public $Avatar;
|
||||||
|
public $FontFile;
|
||||||
|
public $IsNotLetter;
|
||||||
|
public $LetterFont;
|
||||||
|
public $AsianFont;
|
||||||
|
public $EnableAsianChar;
|
||||||
|
|
||||||
|
|
||||||
|
function __construct($Char, $AvatarSize = 256 ,$Shape = 1)
|
||||||
|
{
|
||||||
|
$this->Char = mb_strtoupper(mb_substr($Char, 0, 1, "UTF-8"));
|
||||||
|
$this->AvatarSize = $AvatarSize;
|
||||||
|
$this->Shape = $Shape;
|
||||||
|
$this->Padding = 30 * ($this->AvatarSize / 256);
|
||||||
|
$this->LetterFont = dirname(__FILE__) . '/fonts/SourceCodePro-Light.ttf';
|
||||||
|
$this->AsianFont = dirname(__FILE__) . '/fonts/SourceHanSansCN-Normal.ttf';
|
||||||
|
$this->EnableAsianChar = is_file($this->AsianFont);
|
||||||
|
|
||||||
|
$CNChar = ord($this->Char);
|
||||||
|
if (!$this->EnableAsianChar &&
|
||||||
|
preg_match("/^[\x7f-\xff]/", $this->Char) &&
|
||||||
|
!($CNChar >= ord("A") && $CNChar <= ord("z"))
|
||||||
|
) {
|
||||||
|
//如果是中文,并且没有中文字体包,则按拼音首字母对其进行转换
|
||||||
|
$CNByte = iconv("UTF-8", "gb2312", $this->Char);
|
||||||
|
$Code = ord($CNByte{0}) * 256 + ord($CNByte{1}) - 65536;//求其偏移量
|
||||||
|
if ($Code >= -20319 and $Code <= -20284) $this->Char = "A";
|
||||||
|
if ($Code >= -20283 and $Code <= -19776) $this->Char = "B";
|
||||||
|
if ($Code >= -19775 and $Code <= -19219) $this->Char = "C";
|
||||||
|
if ($Code >= -19218 and $Code <= -18711) $this->Char = "D";
|
||||||
|
if ($Code >= -18710 and $Code <= -18527) $this->Char = "E";
|
||||||
|
if ($Code >= -18526 and $Code <= -18240) $this->Char = "F";
|
||||||
|
if ($Code >= -18239 and $Code <= -17923) $this->Char = "G";
|
||||||
|
if ($Code >= -17922 and $Code <= -17418) $this->Char = "H";
|
||||||
|
if ($Code >= -17417 and $Code <= -16475) $this->Char = "J";
|
||||||
|
if ($Code >= -16474 and $Code <= -16213) $this->Char = "K";
|
||||||
|
if ($Code >= -16212 and $Code <= -15641) $this->Char = "L";
|
||||||
|
if ($Code >= -15640 and $Code <= -15166) $this->Char = "M";
|
||||||
|
if ($Code >= -15165 and $Code <= -14923) $this->Char = "N";
|
||||||
|
if ($Code >= -14922 and $Code <= -14915) $this->Char = "O";
|
||||||
|
if ($Code >= -14914 and $Code <= -14631) $this->Char = "P";
|
||||||
|
if ($Code >= -14630 and $Code <= -14150) $this->Char = "Q";
|
||||||
|
if ($Code >= -14149 and $Code <= -14091) $this->Char = "R";
|
||||||
|
if ($Code >= -14090 and $Code <= -13319) $this->Char = "S";
|
||||||
|
if ($Code >= -13318 and $Code <= -12839) $this->Char = "T";
|
||||||
|
if ($Code >= -12838 and $Code <= -12557) $this->Char = "W";
|
||||||
|
if ($Code >= -12556 and $Code <= -11848) $this->Char = "X";
|
||||||
|
if ($Code >= -11847 and $Code <= -11056) $this->Char = "Y";
|
||||||
|
if ($Code >= -11055 and $Code <= -10247) $this->Char = "Z";
|
||||||
|
}
|
||||||
|
if (in_array($this->Char, str_split('QWERTYUIOPASDFGHJKLZXCVBNM0123456789', 1))) {
|
||||||
|
$this->IsNotLetter = false;
|
||||||
|
$this->FontFile = $this->LetterFont;
|
||||||
|
} else {
|
||||||
|
$this->IsNotLetter = true;
|
||||||
|
$this->FontFile = $this->AsianFont;
|
||||||
|
}
|
||||||
|
$this->Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function Initialize()
|
||||||
|
{
|
||||||
|
//extension_loaded('gd')
|
||||||
|
$Width = $this->AvatarSize;//Width of avatar
|
||||||
|
$Height = $this->AvatarSize;//Height of avatar
|
||||||
|
$Padding = $this->Padding;
|
||||||
|
$this->Avatar = imagecreatetruecolor($Width, $Height);
|
||||||
|
//全透明背景
|
||||||
|
imageSaveAlpha($this->Avatar, true);
|
||||||
|
$BackgroundAlpha = imagecolorallocatealpha($this->Avatar, 255, 255, 255, 127);
|
||||||
|
imagefill($this->Avatar, 0, 0, $BackgroundAlpha);
|
||||||
|
//抗锯齿
|
||||||
|
if (function_exists('imageantialias')) {
|
||||||
|
imageantialias($this->Avatar, true);
|
||||||
|
}
|
||||||
|
//Material Design参考颜色
|
||||||
|
//http://www.google.com/design/spec/style/color.html#color-color-palette
|
||||||
|
$MaterialDesignColor = array(
|
||||||
|
array(239, 154, 154),
|
||||||
|
array(229, 115, 115),
|
||||||
|
array(239, 83, 80),
|
||||||
|
array(244, 67, 54),
|
||||||
|
array(229, 57, 53),
|
||||||
|
array(211, 47, 47),
|
||||||
|
array(198, 40, 40),
|
||||||
|
array(183, 28, 28),
|
||||||
|
array(255, 138, 128),
|
||||||
|
array(255, 82, 82),
|
||||||
|
array(255, 23, 68),
|
||||||
|
array(213, 0, 0),
|
||||||
|
array(248, 187, 208),
|
||||||
|
array(244, 143, 177),
|
||||||
|
array(240, 98, 146),
|
||||||
|
array(236, 64, 122),
|
||||||
|
array(233, 30, 99),
|
||||||
|
array(216, 27, 96),
|
||||||
|
array(194, 24, 91),
|
||||||
|
array(173, 20, 87),
|
||||||
|
array(136, 14, 79),
|
||||||
|
array(255, 128, 171),
|
||||||
|
array(255, 64, 129),
|
||||||
|
array(245, 0, 87),
|
||||||
|
array(197, 17, 98),
|
||||||
|
array(206, 147, 216),
|
||||||
|
array(186, 104, 200),
|
||||||
|
array(171, 71, 188),
|
||||||
|
array(156, 39, 176),
|
||||||
|
array(142, 36, 170),
|
||||||
|
array(123, 31, 162),
|
||||||
|
array(106, 27, 154),
|
||||||
|
array(74, 20, 140),
|
||||||
|
array(234, 128, 252),
|
||||||
|
array(224, 64, 251),
|
||||||
|
array(213, 0, 249),
|
||||||
|
array(170, 0, 255),
|
||||||
|
array(179, 157, 219),
|
||||||
|
array(149, 117, 205),
|
||||||
|
array(126, 87, 194),
|
||||||
|
array(103, 58, 183),
|
||||||
|
array(94, 53, 177),
|
||||||
|
array(81, 45, 168),
|
||||||
|
array(69, 39, 160),
|
||||||
|
array(49, 27, 146),
|
||||||
|
array(179, 136, 255),
|
||||||
|
array(124, 77, 255),
|
||||||
|
array(101, 31, 255),
|
||||||
|
array(98, 0, 234),
|
||||||
|
array(197, 202, 233),
|
||||||
|
array(159, 168, 218),
|
||||||
|
array(121, 134, 203),
|
||||||
|
array(92, 107, 192),
|
||||||
|
array(63, 81, 181),
|
||||||
|
array(57, 73, 171),
|
||||||
|
array(48, 63, 159),
|
||||||
|
array(40, 53, 147),
|
||||||
|
array(26, 35, 126),
|
||||||
|
array(140, 158, 255),
|
||||||
|
array(83, 109, 254),
|
||||||
|
array(61, 90, 254),
|
||||||
|
array(48, 79, 254),
|
||||||
|
array(227, 242, 253),
|
||||||
|
array(187, 222, 251),
|
||||||
|
array(144, 202, 249),
|
||||||
|
array(100, 181, 246),
|
||||||
|
array(66, 165, 245),
|
||||||
|
array(33, 150, 243),
|
||||||
|
array(30, 136, 229),
|
||||||
|
array(25, 118, 210),
|
||||||
|
array(21, 101, 192),
|
||||||
|
array(13, 71, 161),
|
||||||
|
array(130, 177, 255),
|
||||||
|
array(68, 138, 255),
|
||||||
|
array(41, 121, 255),
|
||||||
|
array(41, 98, 255),
|
||||||
|
array(179, 229, 252),
|
||||||
|
array(129, 212, 250),
|
||||||
|
array(79, 195, 247),
|
||||||
|
array(41, 182, 252),
|
||||||
|
array(3, 169, 244),
|
||||||
|
array(3, 155, 229),
|
||||||
|
array(2, 136, 209),
|
||||||
|
array(2, 119, 189),
|
||||||
|
array(1, 87, 155),
|
||||||
|
array(128, 216, 255),
|
||||||
|
array(64, 196, 255),
|
||||||
|
array(0, 176, 255),
|
||||||
|
array(0, 145, 234),
|
||||||
|
array(178, 235, 242),
|
||||||
|
array(128, 222, 234),
|
||||||
|
array(77, 208, 225),
|
||||||
|
array(38, 198, 218),
|
||||||
|
array(0, 188, 212),
|
||||||
|
array(0, 172, 193),
|
||||||
|
array(0, 151, 167),
|
||||||
|
array(0, 131, 143),
|
||||||
|
array(0, 96, 100),
|
||||||
|
array(132, 255, 255),
|
||||||
|
array(24, 255, 255),
|
||||||
|
array(0, 229, 255),
|
||||||
|
array(0, 184, 212),
|
||||||
|
array(178, 223, 219),
|
||||||
|
array(128, 203, 196),
|
||||||
|
array(77, 182, 172),
|
||||||
|
array(38, 166, 154),
|
||||||
|
array(0, 150, 136),
|
||||||
|
array(0, 137, 123),
|
||||||
|
array(0, 121, 107),
|
||||||
|
array(0, 105, 92),
|
||||||
|
array(0, 77, 64),
|
||||||
|
array(167, 255, 235),
|
||||||
|
array(100, 255, 218),
|
||||||
|
array(29, 233, 182),
|
||||||
|
array(0, 191, 165),
|
||||||
|
array(165, 214, 167),
|
||||||
|
array(129, 199, 132),
|
||||||
|
array(102, 187, 106),
|
||||||
|
array(76, 175, 80),
|
||||||
|
array(67, 160, 71),
|
||||||
|
array(56, 142, 60),
|
||||||
|
array(46, 125, 50),
|
||||||
|
array(27, 94, 32),
|
||||||
|
array(185, 246, 202),
|
||||||
|
array(105, 240, 174),
|
||||||
|
array(0, 230, 118),
|
||||||
|
array(0, 200, 83),
|
||||||
|
array(197, 225, 165),
|
||||||
|
array(174, 213, 129),
|
||||||
|
array(156, 204, 101),
|
||||||
|
array(139, 195, 74),
|
||||||
|
array(124, 179, 66),
|
||||||
|
array(104, 159, 56),
|
||||||
|
array(85, 139, 47),
|
||||||
|
array(51, 105, 30),
|
||||||
|
array(178, 255, 89),
|
||||||
|
array(118, 255, 3),
|
||||||
|
array(100, 221, 23),
|
||||||
|
array(249, 251, 231),
|
||||||
|
array(240, 244, 195),
|
||||||
|
array(230, 238, 156),
|
||||||
|
array(220, 231, 117),
|
||||||
|
array(212, 225, 87),
|
||||||
|
array(205, 220, 57),
|
||||||
|
array(192, 202, 51),
|
||||||
|
array(164, 180, 43),
|
||||||
|
array(158, 157, 36),
|
||||||
|
array(130, 119, 23),
|
||||||
|
array(198, 255, 0),
|
||||||
|
array(174, 234, 0),
|
||||||
|
array(251, 192, 45),
|
||||||
|
array(249, 168, 37),
|
||||||
|
array(245, 127, 23),
|
||||||
|
array(255, 179, 0),
|
||||||
|
array(255, 160, 0),
|
||||||
|
array(255, 143, 0),
|
||||||
|
array(255, 111, 0),
|
||||||
|
array(255, 171, 0),
|
||||||
|
array(255, 183, 77),
|
||||||
|
array(255, 167, 38),
|
||||||
|
array(255, 152, 0),
|
||||||
|
array(251, 140, 0),
|
||||||
|
array(245, 124, 0),
|
||||||
|
array(239, 108, 0),
|
||||||
|
array(230, 81, 0),
|
||||||
|
array(255, 171, 64),
|
||||||
|
array(255, 145, 0),
|
||||||
|
array(255, 109, 0),
|
||||||
|
array(255, 171, 145),
|
||||||
|
array(255, 138, 101),
|
||||||
|
array(255, 112, 67),
|
||||||
|
array(255, 87, 34),
|
||||||
|
array(244, 81, 30),
|
||||||
|
array(230, 74, 25),
|
||||||
|
array(216, 67, 21),
|
||||||
|
array(191, 54, 12),
|
||||||
|
array(255, 158, 128),
|
||||||
|
array(255, 110, 64),
|
||||||
|
array(255, 61, 0),
|
||||||
|
array(221, 38, 0),
|
||||||
|
array(188, 170, 164),
|
||||||
|
array(161, 136, 127),
|
||||||
|
array(141, 110, 99),
|
||||||
|
array(121, 85, 72),
|
||||||
|
array(109, 76, 65),
|
||||||
|
array(93, 64, 55),
|
||||||
|
array(78, 52, 46),
|
||||||
|
array(62, 39, 35),
|
||||||
|
array(189, 189, 189),
|
||||||
|
array(158, 158, 158),
|
||||||
|
array(117, 117, 117),
|
||||||
|
array(97, 97, 97),
|
||||||
|
array(66, 66, 66),
|
||||||
|
array(33, 33, 33),
|
||||||
|
array(236, 239, 241),
|
||||||
|
array(176, 187, 197),
|
||||||
|
array(144, 164, 174),
|
||||||
|
array(120, 144, 156),
|
||||||
|
array(96, 125, 139),
|
||||||
|
array(84, 110, 122),
|
||||||
|
array(69, 90, 100),
|
||||||
|
array(55, 71, 79),
|
||||||
|
array(38, 50, 56)
|
||||||
|
);
|
||||||
|
$BackgroundColorIndex = mt_rand(0, count($MaterialDesignColor) - 1);
|
||||||
|
$BackgroundColor = imagecolorallocate($this->Avatar,
|
||||||
|
$MaterialDesignColor[$BackgroundColorIndex][0],
|
||||||
|
$MaterialDesignColor[$BackgroundColorIndex][1],
|
||||||
|
$MaterialDesignColor[$BackgroundColorIndex][2]
|
||||||
|
);
|
||||||
|
if($this->Shape == 1){
|
||||||
|
//画一个矩形
|
||||||
|
imagerectangle($this->Avatar,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
$Width,
|
||||||
|
$Height,
|
||||||
|
$BackgroundColor
|
||||||
|
);
|
||||||
|
imagefilledrectangle($this->Avatar,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
$Width,
|
||||||
|
$Height,
|
||||||
|
$BackgroundColor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
//画一个居中圆形
|
||||||
|
imagefilledellipse($this->Avatar,
|
||||||
|
$Width / 2,
|
||||||
|
$Height / 2,
|
||||||
|
$Width,
|
||||||
|
$Height,
|
||||||
|
$BackgroundColor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//字体
|
||||||
|
$FontColor = imagecolorallocate($this->Avatar, 255, 255, 255);
|
||||||
|
if ($this->IsNotLetter) {
|
||||||
|
//中文字符偏移
|
||||||
|
$FontSize = $Width - $Padding * 3.5;
|
||||||
|
$X = $Padding + (-2 / 166) * $FontSize;
|
||||||
|
$Y = $Height - $Padding - (23.5 / 166) * $FontSize;
|
||||||
|
} else {
|
||||||
|
$FontSize = $Width - $Padding * 2;
|
||||||
|
$X = $Padding + (20 / 196) * $FontSize;
|
||||||
|
$Y = $Height - $Padding - (13 / 196) * $FontSize;
|
||||||
|
}
|
||||||
|
// 在圆正中央填入字符
|
||||||
|
imagettftext($this->Avatar,
|
||||||
|
$FontSize,
|
||||||
|
0,
|
||||||
|
$X,
|
||||||
|
$Y,
|
||||||
|
$FontColor,
|
||||||
|
$this->FontFile,
|
||||||
|
$this->Char
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function Resize($TargetSize)
|
||||||
|
{
|
||||||
|
if (isset($this->Avatar)) {
|
||||||
|
if ($this->AvatarSize > $TargetSize) {
|
||||||
|
$Percent = $TargetSize / $this->AvatarSize;
|
||||||
|
$TargetWidth = round($this->AvatarSize * $Percent);
|
||||||
|
$TargetHeight = round($this->AvatarSize * $Percent);
|
||||||
|
$TargetImageData = imagecreatetruecolor($TargetWidth, $TargetHeight);
|
||||||
|
//全透明背景
|
||||||
|
imageSaveAlpha($TargetImageData, true);
|
||||||
|
$BackgroundAlpha = imagecolorallocatealpha($TargetImageData, 255, 255, 255, 127);
|
||||||
|
imagefill($TargetImageData, 0, 0, $BackgroundAlpha);
|
||||||
|
imagecopyresampled($TargetImageData, $this->Avatar, 0, 0, 0, 0, $TargetWidth, $TargetHeight, $this->AvatarSize, $this->AvatarSize);
|
||||||
|
return $TargetImageData;
|
||||||
|
} else {
|
||||||
|
return $this->Avatar;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Free()
|
||||||
|
{
|
||||||
|
imagedestroy($this->Avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Output2Browser($AvatarSize = 0)
|
||||||
|
{
|
||||||
|
if (!$AvatarSize) {
|
||||||
|
$AvatarSize = $this->AvatarSize;
|
||||||
|
}
|
||||||
|
header('Content-Type: image/png');
|
||||||
|
return imagepng($this->Resize($AvatarSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get image resource identifier
|
||||||
|
* @param int $AvatarSize
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
public function Output2ImageResource($AvatarSize = 0)
|
||||||
|
{
|
||||||
|
if (!$AvatarSize) {
|
||||||
|
$AvatarSize = $this->AvatarSize;
|
||||||
|
}
|
||||||
|
return $this->Resize($AvatarSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output Base64 encoded image data
|
||||||
|
* @param int $AvatarSize
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function Output2Base64($AvatarSize = 0)
|
||||||
|
{
|
||||||
|
if (!$AvatarSize) {
|
||||||
|
$AvatarSize = $this->AvatarSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
imagepng($this->Resize($AvatarSize));
|
||||||
|
$content = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
|
||||||
|
return 'data:image/png;base64,' . base64_encode($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Save($Path, $AvatarSize = 0)
|
||||||
|
{
|
||||||
|
if (!$AvatarSize) {
|
||||||
|
$AvatarSize = $this->AvatarSize;
|
||||||
|
}
|
||||||
|
return imagepng($this->Resize($AvatarSize), $Path);
|
||||||
|
}
|
||||||
|
}
|
BIN
extend/avatars/fonts/SourceCodePro-Light.ttf
Normal file
BIN
extend/avatars/fonts/SourceCodePro-Light.ttf
Normal file
Binary file not shown.
BIN
extend/avatars/fonts/SourceHanSansCN-Normal.ttf
Normal file
BIN
extend/avatars/fonts/SourceHanSansCN-Normal.ttf
Normal file
Binary file not shown.
433
extend/backup/Backup.php
Normal file
433
extend/backup/Backup.php
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
<?php
|
||||||
|
declare (strict_types = 1);
|
||||||
|
namespace backup;
|
||||||
|
use think\facade\Db;
|
||||||
|
use think\facade\Config;
|
||||||
|
class Backup
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 文件指针
|
||||||
|
* @var resource
|
||||||
|
*/
|
||||||
|
private $fp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份文件信息 part - 卷号,name - 文件名
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前打开文件大小
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
private $size = 0;
|
||||||
|
/**
|
||||||
|
* 数据库配置
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
private $dbconfig=array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份配置
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
private $config=array(
|
||||||
|
'path' => './backup/',//数据库备份路径
|
||||||
|
'part' => 20971520,//数据库备份卷大小
|
||||||
|
'compress' => 0,//数据库备份文件是否启用压缩 0不压缩 1 压缩
|
||||||
|
'level' => 9 //数据库备份文件压缩级别 1普通 4 一般 9最高
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* 数据库备份构造方法
|
||||||
|
* @param array $file 备份或还原的文件信息
|
||||||
|
* @param array $config 备份配置信息
|
||||||
|
*/
|
||||||
|
public function __construct($config=[]){
|
||||||
|
$this->config = array_merge($this->config, $config);
|
||||||
|
//初始化文件名
|
||||||
|
$this->setFile();
|
||||||
|
//初始化数据库连接参数
|
||||||
|
$this->setDbConn();
|
||||||
|
//检查文件是否可写
|
||||||
|
if(!$this->checkPath($this->config['path'])){
|
||||||
|
throw new \Exception("The current directory is not writable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置数据库连接必备参数
|
||||||
|
* @param array $dbconfig 数据库连接配置信息
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function setDbConn($dbconfig=[])
|
||||||
|
{
|
||||||
|
if (empty($dbconfig)) {
|
||||||
|
$this->dbconfig = Config::get('database');
|
||||||
|
}else{
|
||||||
|
$this->dbconfig=$dbconfig;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 设置备份文件名
|
||||||
|
* @param String $file 文件名字
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function setFile($file=null)
|
||||||
|
{
|
||||||
|
if(is_null($file)){
|
||||||
|
$this->file=['name'=>date('Ymd-His'),'part'=>1];
|
||||||
|
}else{
|
||||||
|
if(!array_key_exists("name",$file) && !array_key_exists("part",$file)){
|
||||||
|
$this->file=$file['1'];
|
||||||
|
}else{
|
||||||
|
$this->file=$file;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
//数据类连接
|
||||||
|
public static function connect()
|
||||||
|
{
|
||||||
|
return Db::connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
//数据库备份文件列表
|
||||||
|
public function fileList()
|
||||||
|
{
|
||||||
|
if(!is_dir($this->config['path'])){
|
||||||
|
mkdir($this->config['path'], 0755, true);
|
||||||
|
}
|
||||||
|
$path = realpath($this->config['path']);
|
||||||
|
$flag = \FilesystemIterator::KEY_AS_FILENAME;
|
||||||
|
$glob = new \FilesystemIterator($path, $flag);
|
||||||
|
$list = array();
|
||||||
|
foreach ($glob as $name => $file) {
|
||||||
|
if(preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql(?:\.gz)?$/', $name)){
|
||||||
|
$name = sscanf($name, '%4s%2s%2s-%2s%2s%2s-%d');
|
||||||
|
|
||||||
|
$date = "{$name[0]}-{$name[1]}-{$name[2]}";
|
||||||
|
$time = "{$name[3]}:{$name[4]}:{$name[5]}";
|
||||||
|
$part = $name[6];
|
||||||
|
|
||||||
|
if(isset($list["{$date} {$time}"])){
|
||||||
|
$info = $list["{$date} {$time}"];
|
||||||
|
$info['part'] = max($info['part'], $part);
|
||||||
|
$info['size'] = $info['size'] + $file->getSize();
|
||||||
|
} else {
|
||||||
|
$info['part'] = $part;
|
||||||
|
$info['size'] = $file->getSize();
|
||||||
|
}
|
||||||
|
$extension = strtoupper(pathinfo($file->getFilename(), PATHINFO_EXTENSION));
|
||||||
|
$info['compress'] = ($extension === 'SQL') ? '-' : $extension;
|
||||||
|
$info['time'] = strtotime("{$date} {$time}");
|
||||||
|
|
||||||
|
$list["{$date} {$time}"] = $info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
public function getFile($type='',$time=0)
|
||||||
|
{ //
|
||||||
|
if(!is_numeric($time) ){
|
||||||
|
throw new \Exception("{$time} Illegal data type");
|
||||||
|
}
|
||||||
|
switch ($type)
|
||||||
|
{
|
||||||
|
case 'time':
|
||||||
|
$name = date('Ymd-His', $time) . '-*.sql*';
|
||||||
|
$path = realpath($this->config['path']) . DIRECTORY_SEPARATOR . $name;
|
||||||
|
return glob($path);
|
||||||
|
break;
|
||||||
|
case 'timeverif':
|
||||||
|
$name = date('Ymd-His', $time) . '-*.sql*';
|
||||||
|
$path = realpath($this->config['path']) . DIRECTORY_SEPARATOR . $name;
|
||||||
|
$files = glob($path);
|
||||||
|
$list = array();
|
||||||
|
foreach($files as $name){
|
||||||
|
$basename = basename($name);
|
||||||
|
$match = sscanf($basename, '%4s%2s%2s-%2s%2s%2s-%d');
|
||||||
|
$gz = preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql.gz$/', $basename);
|
||||||
|
$list[$match[6]] = array($match[6], $name, $gz);
|
||||||
|
}
|
||||||
|
$last = end($list);
|
||||||
|
if(count($list) === $last[0]){
|
||||||
|
return $list;
|
||||||
|
} else {
|
||||||
|
throw new \Exception("File {$files['0']} may be damaged, please check again");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'pathname':
|
||||||
|
return "{$this->config['path']}{$this->file['name']}-{$this->file['part']}.sql";
|
||||||
|
break;
|
||||||
|
case 'filename':
|
||||||
|
return "{$this->file['name']}-{$this->file['part']}.sql";
|
||||||
|
break;
|
||||||
|
case 'filepath':
|
||||||
|
return $this->config['path'];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$arr=array(
|
||||||
|
'pathname'=>"{$this->config['path']}{$this->file['name']}-{$this->file['part']}.sql",
|
||||||
|
'filename'=>"{$this->file['name']}-{$this->file['part']}.sql",
|
||||||
|
'filepath'=>$this->config['path'],
|
||||||
|
'file'=>$this->file
|
||||||
|
);
|
||||||
|
return $arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除备份文件
|
||||||
|
public function delFile($time)
|
||||||
|
{
|
||||||
|
if($time){
|
||||||
|
$file=$this->getFile('time',$time);
|
||||||
|
array_map("unlink", $this->getFile('time',$time));
|
||||||
|
if(count( $this->getFile('time',$time) )){
|
||||||
|
throw new \Exception("File {$path} deleted failed");
|
||||||
|
} else {
|
||||||
|
return $time;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new \Exception("{$time} Time parameter is incorrect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function import($start){
|
||||||
|
//还原数据
|
||||||
|
$db = self::connect();
|
||||||
|
|
||||||
|
if($this->config['compress']){
|
||||||
|
$gz = gzopen($this->file[1], 'r');
|
||||||
|
$size = 0;
|
||||||
|
} else {
|
||||||
|
$size = filesize($this->file[1]);
|
||||||
|
$gz = fopen($this->file[1], 'r');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = '';
|
||||||
|
if($start){
|
||||||
|
$this->config['compress'] ? gzseek($gz, $start) : fseek($gz, $start);
|
||||||
|
}
|
||||||
|
for($i = 0; $i < 1000; $i++){
|
||||||
|
$sql .= $this->config['compress'] ? gzgets($gz) : fgets($gz);
|
||||||
|
if(preg_match('/.*;$/', trim($sql))){
|
||||||
|
if(false !== $db->execute($sql)){
|
||||||
|
$start += strlen($sql);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$sql = '';
|
||||||
|
} elseif ($this->config['compress'] ? gzeof($gz) : feof($gz)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($start, $size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//数据库表列表
|
||||||
|
public function dataList($table=null)
|
||||||
|
{
|
||||||
|
$db = self::connect();
|
||||||
|
|
||||||
|
if(is_null($table)){
|
||||||
|
$list = $db->query("SHOW TABLE STATUS");
|
||||||
|
}else{
|
||||||
|
$list = $db->query("show columns from {$table}");
|
||||||
|
}
|
||||||
|
return array_map('array_change_key_case', $list);//$list;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 写入初始数据
|
||||||
|
* @return boolean true - 写入成功,false - 写入失败
|
||||||
|
*/
|
||||||
|
public function Backup_Init(){
|
||||||
|
$sql = "-- -----------------------------\n";
|
||||||
|
$sql .= "-- Think MySQL Data Transfer \n";
|
||||||
|
$sql .= "-- \n";
|
||||||
|
$sql .= "-- Host : " .$this->dbconfig['hostname']. "\n";
|
||||||
|
$sql .= "-- Port : " .$this->dbconfig['hostport']. "\n";
|
||||||
|
$sql .= "-- Database : " .$this->dbconfig['database']. "\n";
|
||||||
|
$sql .= "-- \n";
|
||||||
|
$sql .= "-- Part : #{$this->file['part']}\n";
|
||||||
|
$sql .= "-- Date : " . date("Y-m-d H:i:s") . "\n";
|
||||||
|
$sql .= "-- -----------------------------\n\n";
|
||||||
|
$sql .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
|
||||||
|
return $this->write($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份表结构
|
||||||
|
* @param string $table 表名
|
||||||
|
* @param integer $start 起始行数
|
||||||
|
* @return boolean false - 备份失败
|
||||||
|
*/
|
||||||
|
public function backup($table, $start){
|
||||||
|
|
||||||
|
$db = self::connect();
|
||||||
|
// 备份表结构
|
||||||
|
if(0 == $start){
|
||||||
|
$result = $db->query("SHOW CREATE TABLE `{$table}`");
|
||||||
|
$sql = "\n";
|
||||||
|
$sql .= "-- -----------------------------\n";
|
||||||
|
$sql .= "-- Table structure for `{$table}`\n";
|
||||||
|
$sql .= "-- -----------------------------\n";
|
||||||
|
$sql .= "DROP TABLE IF EXISTS `{$table}`;\n";
|
||||||
|
|
||||||
|
$sql .= trim($result[0]['Create Table']) . ";\n\n";
|
||||||
|
|
||||||
|
if(false === $this->write($sql)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//数据总数
|
||||||
|
$result = $db->query("SELECT COUNT(*) AS count FROM `{$table}`");
|
||||||
|
|
||||||
|
$count = $result['0']['count'];
|
||||||
|
|
||||||
|
//备份表数据
|
||||||
|
if($count){
|
||||||
|
//写入数据注释
|
||||||
|
if(0 == $start){
|
||||||
|
$sql = "-- -----------------------------\n";
|
||||||
|
$sql .= "-- Records of `{$table}`\n";
|
||||||
|
$sql .= "-- -----------------------------\n";
|
||||||
|
$this->write($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
//备份数据记录
|
||||||
|
$result = $db->query("SELECT * FROM `{$table}` LIMIT {$start}, 1000");
|
||||||
|
foreach ($result as $row) {
|
||||||
|
$row = array_map('addslashes', $row);
|
||||||
|
$sql = "INSERT INTO `{$table}` VALUES ('" . str_replace(array("\r","\n"),array('\r','\n'),implode("', '", $row)) . "');\n";
|
||||||
|
if(false === $this->write($sql)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//还有更多数据
|
||||||
|
if($count > $start + 1000){
|
||||||
|
return array($start + 1000, $count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//备份下一表
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 优化表
|
||||||
|
* @param String $tables 表名
|
||||||
|
* @return String $tables
|
||||||
|
*/
|
||||||
|
public function optimize($tables = null){
|
||||||
|
if($tables) {
|
||||||
|
$db = self::connect();
|
||||||
|
if(is_array($tables)){
|
||||||
|
$tables = implode('`,`', $tables);
|
||||||
|
$list = $db->query("OPTIMIZE TABLE `{$tables}`");
|
||||||
|
} else {
|
||||||
|
$list = $db->query("OPTIMIZE TABLE `{$tables}`");
|
||||||
|
}
|
||||||
|
if($list){
|
||||||
|
return $tables;
|
||||||
|
} else {
|
||||||
|
throw new \Exception("data sheet'{$tables}'Repair mistakes please try again!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new \Exception("Please specify the table to be repaired!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 修复表
|
||||||
|
* @param String $tables 表名
|
||||||
|
* @return String $tables
|
||||||
|
*/
|
||||||
|
public function repair($tables = null){
|
||||||
|
if($tables) {
|
||||||
|
$db = self::connect();
|
||||||
|
if(is_array($tables)){
|
||||||
|
$tables = implode('`,`', $tables);
|
||||||
|
$list = $db->query("REPAIR TABLE `{$tables}`");
|
||||||
|
} else {
|
||||||
|
$list = $db->query("REPAIR TABLE `{$tables}`");
|
||||||
|
}
|
||||||
|
if($list){
|
||||||
|
return $list;
|
||||||
|
} else {
|
||||||
|
throw new \Exception("data sheet'{$tables}'Repair mistakes please try again!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new \Exception("Please specify the table to be repaired!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入SQL语句
|
||||||
|
* @param string $sql 要写入的SQL语句
|
||||||
|
* @return boolean true - 写入成功,false - 写入失败!
|
||||||
|
*/
|
||||||
|
private function write($sql){
|
||||||
|
$size = strlen($sql);
|
||||||
|
//由于压缩原因,无法计算出压缩后的长度,这里假设压缩率为50%,
|
||||||
|
//一般情况压缩率都会高于50%;
|
||||||
|
$size = $this->config['compress'] ? $size / 2 : $size;
|
||||||
|
$this->open($size);
|
||||||
|
return $this->config['compress'] ? @gzwrite($this->fp, $sql) : @fwrite($this->fp, $sql);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 打开一个卷,用于写入数据
|
||||||
|
* @param integer $size 写入数据的大小
|
||||||
|
*/
|
||||||
|
private function open($size){
|
||||||
|
if($this->fp){
|
||||||
|
$this->size += $size;
|
||||||
|
if($this->size > $this->config['part']){
|
||||||
|
$this->config['compress'] ? @gzclose($this->fp) : @fclose($this->fp);
|
||||||
|
$this->fp = null;
|
||||||
|
$this->file['part']++;
|
||||||
|
session('backup_file', $this->file);
|
||||||
|
$this->create();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$backuppath = $this->config['path'];
|
||||||
|
$filename = "{$backuppath}{$this->file['name']}-{$this->file['part']}.sql";
|
||||||
|
if($this->config['compress']){
|
||||||
|
$filename = "{$filename}.gz";
|
||||||
|
$this->fp = @gzopen($filename, "a{$this->config['level']}");
|
||||||
|
} else {
|
||||||
|
$this->fp = @fopen($filename, 'a');
|
||||||
|
}
|
||||||
|
$this->size = filesize($filename) + $size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查目录是否可写
|
||||||
|
* @param string $path 目录
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected function checkPath($path)
|
||||||
|
{
|
||||||
|
if (is_dir($path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (mkdir($path, 0755, true)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 析构方法,用于关闭文件资源
|
||||||
|
*/
|
||||||
|
public function __destruct(){
|
||||||
|
if($this->fp){
|
||||||
|
$this->config['compress'] ? @gzclose($this->fp) : @fclose($this->fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2
public/storage/.gitignore
vendored
Normal file
2
public/storage/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
Loading…
x
Reference in New Issue
Block a user