因为考虑到服务器费用的问题,展开来说就是很多服务器商为了吸引用户,会给予新用户一个极低的价格,而在续费时却基本没有什么优惠,导致续费价格甚至需要在首年价格之后再添上一个0
,也因此导致了一些不产生利润的站点如同候鸟一样在各大服务器商之间来回迁徙。迁移站点之前我们需要做很多工作,比如站点设置项目的数据备份。
备份后台设置项
typecho后台设置项的数据会被存储在数据库的typecho_options
这张表里,字段名一般为theme:主题名
,根据主题名称的不同,该字段名也会产生变化,以避免不同的主题产生字段重叠。
设置数据的组织结构如下图,看起来是不是有点象一组json数据?主题设置项目较多的时候,手动重设会耗费一定的时间。当需要给站点搬家的时候,我们还是希望能够拿到一份备份的文件,以便在需要的时候直接导入。
代码实现
在主题设置页添加了一个表单,以实现数据备份与下载的功能,使用input元素提交的时候没有什么问题,但修改为button元素后,表单数据就无法正确被提交了,最后只能利用js给按钮分别做了绑定,当按钮被点击后,先去修改隐藏的input的value,再执行表单提交操作。
<?php
$name = "august";
$db = Typecho_Db::get();
$backupDir = AUG_THEME_DIR . '/backup/';
if (isset($_POST['type'])) {
$value = $db->fetchRow($db->select()->from('table.options')->where('name = ?', 'theme:' . $name))['value'];
if ($_POST["type"] == "backup") {
$backupData = [
'backup_time' => date('Y-m-d H:i:s'),
'theme' => $name,
'settings' => unserialize($value)
];
$jsonData = json_encode($backupData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
$filename = $backupDir . 'theme_' . $name . '_backup.json';
if (file_put_contents($filename, $jsonData)) {
?>
<script>alert("设置已成功备份!");</script>
<?php
} else {
?>
<script>alert("备份失败,请检查backup目录权限!");</script>
<?php
}
}
if ($_POST["type"] == "recovery") {
$backupFile = $backupDir . 'theme_' . $name . '_backup.json';
if (file_exists($backupFile)) {
$backupContent = file_get_contents($backupFile);
$backupData = json_decode($backupContent, true);
if ($backupData && isset($backupData['settings'])) {
$serializedSettings = serialize($backupData['settings']);
$db->query($db->update('table.options')->rows(array('value' => $serializedSettings))->where('name = ?', 'theme:' . $name));
?>
<script>
alert("已从备份文件恢复设置!");
window.location.href="<?php Helper::options()->adminUrl('options-theme.php');?>";
</script>
<?php
} else {
?>
<script>
alert("备份文件解析失败,可能已损坏!");
</script>
<?php
}
} else {
?>
<script>
alert("未找到备份文件,无法恢复!");
</script>
<?php
}
}
if ($_POST["type"] == "download") {
$backupFile = $backupDir . 'theme_' . $name . '_backup.json';
ob_clean();
ob_start();
if (file_exists($backupFile)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="theme_' . $name . '_backup.json"');
header('Content-Length: ' . filesize($backupFile));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
ob_end_flush();
readfile($backupFile);
exit;
} else {
?>
<script>
alert("没有找到可下载的备份文件!");
</script>
exit;
<?php
}
}
}
?>
<form id="backup" class="backup" action="" method="post">
<input type="hidden" name="type" value="">
<button type="button" value="backup" class="icon-btn">
<i class="fa fa-save"></i>
</button>
<button type="button" value="recovery" class="icon-btn">
<i class="fa fa-undo"></i>
</button>
<button type="button" value="download" class="icon-btn">
<i class="fa fa-download"></i>
</button>
</form>
按钮动作绑定的js代码:
document.querySelectorAll('#backup button[type="button"]').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
const form = this.closest('form');
const hiddenInput = form.querySelector('input[name="type"]');
hiddenInput.value = this.value;
form.submit();
});
});