项目已弃坑

开源地址

innc11/RedirectableAttachmentLink

代码参考:

Typecho项目 | OssForTypecho插件 | SiteRunningTime插件 | Access插件 | Attachment插件

开始

Typecho相对WordPress来说代码相对简洁,不及WordPress知名度那么高,但是因此也缺少许多功能,不过好在有typecho-fans/plugins项目,里面有许多大佬前辈的好用插件,可以说涵盖了很多方面

最近因为网站空间即将用完(附件占了很大一部分),我琢磨着把所有文章的附件转移到一个空间更大的地方,附件和图片差不多占了总文件大小的70%左右。来回对比最后选择了用阿里云的OSS来存放,先前买过一个40GB的存储包,半年只要9块还是4块来着特别便宜

我选择了用OSSForTypecho插件来进行转换,但也很快发现了一个问题,原有的附件移动到了OSS里之后,所有的链接就无法访问了,因为文章里的链接都还是 https://innc11.cn/usr/uploads/2020/xx/xx/aa.png 这种格式,OSS访问应该是 https://res.innc11.cn/usr/uploads/2020/xx/xx/aa.png 除了域名以外后面都是相同的,我就在想能不能有一种插件可以同时访问这两种链接,只需要很少的配置的就可以来回切换,然后就开始了我的第一个插件的制作(保不齐哪天更换了新的服务器环境又会变回来)

插件功能

我希望能用一个固定的链接,以附件cid为索引进行重定向。刚开始很困难,Typecho开发文档太少,不得不拆解各大开源插件寻求方案,首先是要注册一个路由规则: https://your.domain/attach/acid 这样的链接进行访问,由插件负责进行显式重定向,还要能支持OSS的图片处理功能(加预设样式的参数),其中acid为附件的cid,这是恒定不变的一个参数

重定向处理流程

路由规则为/attach/[acid:digital]确保acid是一个数字类型的

这一部分是重定向的实现代码:

  1. 首先判断是否包含acid(实际测试中如果不包含或者不是数字是无法匹配路由规则的,也就是不会触发代码)

  2. 拿到acid后在数据库里搜索,如果找不到就显示错误信息

  3. 将数据库里的原始数据进行反序列化,同时根据附件的后缀名判断是否是一个图片文件,如果是则附加样式参数

  4. 根据配置信息进行重定向到OSS URL或者本地URL并将最终的URL进行显式重定向


public function access()
{
	$options = Typecho_Widget::widget('Widget_Options');
	$pluginOpt = $options->plugin('RedirectableAttachmentLink');
	$domain = $pluginOpt->domain;

	if (!isset($this->request->acid))
		throw new Typecho_Widget_Exception(_t('无效的参数'));

	$acid = intval($this->request->get("acid"));
	$db = Typecho_Db::get();
	$attach = $db->fetchRow($db->select()->from('table.contents')->where('type = \'attachment\' AND cid = ?', $acid));

	if (empty($attach))
		throw new Typecho_Widget_Exception(_t('文件不存在或无法读取,请与管理员联系。'));

	$attach_text = unserialize($attach['text']);
	$isImage = in_array(strrchr($attach_text['name'], '.'), explode(" ", $pluginOpt->imagesuffixs));
	$attach_url = Typecho_Common::url($attach_text['path'], ($domain ? $domain : $options->index)) . ($isImage? $pluginOpt->imgstyle : "");
	$this->response->redirect($attach_url);
}

对编辑器的附件功能进行劫持并换成自己的代码

通过在activate函数中注册回掉进行插入自己的js代码


public static function activate()
{
	Typecho_Plugin::factory('admin/write-post.php')->bottom = ['RedirectableAttachmentLink_Plugin','inject'];
	Typecho_Plugin::factory('admin/write-page.php')->bottom = ['RedirectableAttachmentLink_Plugin','inject'];
}

public static function inject($pageOrPost)
{
	$options 	= Typecho_Widget::widget('Widget_Options');
	$plugin	    = $options->plugin('RedirectableAttachmentLink');
	echo '< script src="' . $options->pluginUrl . '/RedirectableAttachmentLink/js/inject.js"></script>';
}

js代码的任务很简单,把点击附件名时插入的链接改成自己的链接

首先通过延时执行以确保其它所有插件加载完毕和持有事件处理的最高优先级(最后执行的可以覆盖先前执行的结果)

首先对列表中已有的附件按钮进行替换,然后在注册监听器对新上传的附件进行替换

另外这里用到一个变量用以控制Node改变触发的回调次数,控制在一次以内(因为实测每一个子元素都会触发一次)

实际替换时用自己的链接进行插入即可(https::xx.xx/ attach/ acid)

最后就可以通过 https://innc11.cn/attach/90 这种链接来访问原始文件了


setTimeout(function(){
	var readyToReplace = false // 防止多次触发

	function replaceListener() // 劫持原有的监听器
	{
		$('#file-list li .insert').unbind("click") // 解除原有的监听器
		$('#file-list li .insert').click(function (e) { // 换上自己的
			e.preventDefault() // 防止打开附件链接
			var t = $(this)
			var p = t.parents('li');
			var domain = location.origin

			Typecho.insertFileToEditor(t.text(), domain + "/attach/" + p.data('cid'), p.data('image'));
		});
	}

	replaceListener() // 对列表中已有的项目进行劫持

	$("#file-list").bind("DOMNodeInserted", function(){ // 对新加入的项目进行劫持
		if(!readyToReplace)
		{
			readyToReplace = true
			setTimeout(function(){
				replaceListener()
				readyToReplace = false
			}, 100)
		}
	});

}, 1000)