奇怪的需求(2)——兼容旧设备的Blog

今年年初我并没有像往年一样,总结去年一年中收集到的各种古董设备。一方面是因为24年确实也没收集到什么新东西,另一方面也是因为孩子尚小没太多时间去仔细折腾。当然,这并不意味着在“垃圾处理”领域任何动作都没有。经过几年的折腾,我手头的大多数老设备都可以通过各种方式连接至互联网,可以好好回味一把儿时网上“冲浪”的乐趣。

然而很快我就发现了问题,在如今 HTTPS 已经普遍性的应用于各大站点的前提下,不支持哪怕是 SSL 1.1 的旧设备的联网能力事实上等同于不存在。加之旧版本浏览器并不支持诸如 JS 和 HTML5 等现代前端技术,又将即便未使用 HTTPS 得现代站点排除在外。这样下来,能够被老设备正常访问的站点实际上屈指可数,那我花费时间精力实现的旧设备联网能力就显得毫无意义了。

当然,RetroComputing圈子对这种问题早已有了自己的解决方案。HTTPS的问题最容易解决,很久前就有爱好者使用低功耗计算机如树莓派等进行“HTTPS脱壳”,将 HTTPS 流量转换为普通 HTTP。当然,这样的脱壳方案适应的设备数依然有限,现代JS和HTML5仍然是挡在古董设备面前的鸿沟。因此,也有爱好者利用Web Archive(互联网档案馆)或类似服务,把网站的历史版本保存下来,形成所谓的“时光机”页面。甚至搭配硬件实现“互联网时光机”。(见:【熟肉】我组装了一台互联网时光机【The Science Elf】_哔哩哔哩_bilibili)。

当然,除了这些相对复杂的方式外,最简单的兼容旧设备的方法莫过于直接以1990s的技术构建一个古董站点,极致的静态页面,极致的享受(误。知乎“古董电脑室”专栏的主持人Dr.Quest曾经专门写过《Y2K网页制作全攻略》系列文章,来帮助大家实现“原生”古董页面的创建需求。当然,这种方式也存在着固有缺陷,一是几乎不可能实现现代前端技术一样的美观度,二是意味着现有博客的内容完全无法被利用,每个页面都需要新建、排版。

本站基于WordPress构建,基于PHP。因此理论上就存在一种可能,通过一个兼容旧浏览器的静态页面模板+WordPress内容导出脚本,构建一个降级版的站点。并且只需在为这个降级站点赋予一个独立的二级域名,并且关闭HTTPS,即可实现对旧设备的兼容。

而兼容目标很容易就确定为了IE3,原因很简单,从IE3开始IE的市占率超过了Netscape。在之后的10余年里,IE的HTML实现事实上就是互联网的标准。因此,如果页面元素兼容IE3,那么基本可以覆盖所有旧设备。

与现代HTML页面相比,IE3不支持以下特性:

  • CSS2/3
  • JavaScript 的 DOM 操作
  • HTTPS(除非使用 SSLv2/3)
  • PNG 格式图片
  • UTF-8 编码(默认是 ANSI/Latin-1)
  • HTTP/1.1(只有部分支持)

因此,页面模板也不存在上述内容,仅通过<table>构建页面静态页面主体。主要代码如下

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
……
<body bgcolor="#EFF3FF" text="#000000" link="#0000FF" vlink="#800080">
<center>
<table width="600">
……
<tr><td>
<font face="宋体" size="3"><b>最新文章</b></font><br><br>
<!-- Begin Articles -->
<font face="宋体" size="2">
<!-- 此处将被替换为生成的文章内容 -->
</font>
<!-- End Articles -->
</td></tr></table>

<br>
<table width="600">
<tr><td align="center">
<font face="宋体" size="2">
<!-- Begin Pagination -->
<a href="index2.html">下一页</a>
<!-- End Pagination -->
</font>
</td></tr></table>

<br><table width="600">
<tr><td>
……
</table>
</center>
</body>
</html>

与之配套的,我们需要一个适配WordPress的导出脚本,从现代页面中导出文档的相关内容,同时处理图片等标签元素。导出内容部分直接依赖wp-load.php,该文件直接提供了加载文章内容所需的相关函数。

图片处理相关代码如下:

function extract_and_convert_images($content, $post_id, &$image_counter) {
preg_match_all('/<img[^>]+src=[\"\']([^\\"\']+)[\"\']/i', $content, $matches);
$converted_images = [];

foreach ($matches[1] as $img_src) {
$img_ext = pathinfo(parse_url($img_src, PHP_URL_PATH), PATHINFO_EXTENSION);
$gif_name = "post{$post_id}_img{$image_counter}.gif";
$output_path = "legacy/images/$gif_name";

$image_data = @file_get_contents($img_src);
if ($image_data !== false) {
file_put_contents("temp.$img_ext", $image_data);
exec("convert temp.$img_ext -resize 500 -colors 64 $output_path");
unlink("temp.$img_ext");
$converted_images[$img_src] = $gif_name;
$image_counter++;
}
}

return $converted_images;
}

获取和处理相关文档内容主要代码如下:

<?php
require('wp-load.php');

……
for ($page = 0; $page < $total_pages; $page++) {
……

foreach ($slice as $post) {
……

// remove all <iframe> <video> <audio> <script> <style> tags
$content = preg_replace([
'/<iframe[^>]*>.*?<\/iframe>/is',
'/<video[^>]*>.*?<\/video>/is',
'/<audio[^>]*>.*?<\/audio>/is',
'/<script[^>]*>.*?<\/script>/is',
'/<style[^>]*>.*?<\/style>/is'
], '', $content);

// process <figcaption> tags
$content = preg_replace_callback(
'/<figcaption[^>]*>(.*?)<\/figcaption>/is',
function ($matches) {
$text = strip_tags($matches[1]);
return "\n<font face=\"黑体\" color=\"#808080\" size=\"2\"><i>$text</i></font>\n";
},
$content
);

// remove <span> tags
$content = preg_replace('/<span[^>]*>.*?<\/span>/is', '', $content);

$image_counter = 0;
$converted_images = extract_and_convert_images($content, $post->ID, $image_counter);

$patterns = [];
$replacements = [];
// replace all <img> tags src
foreach ($converted_images as $original => $converted) {
$escaped = preg_quote($original, '/');
$patterns[] = '/<img([^>]+?)src\s*=\s*(["\'])' . $escaped . '\2/i';
$replacements[] = '<img$1src="images/' . $converted . '"';
}
……

$content = iconv("UTF-8", "GB2312//IGNORE", $content);

$post_html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">
<html>
<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=GB2312\">
<title>$title</title></head>
<body bgcolor=\"#FFFFFF\" text=\"#000000\">
……
</body></html>";

file_put_contents("legacy/post_" . $post->ID . ".html", $post_html);
$index++;
}

file_put_contents("articles_page{$page}.txt", ob_get_clean());
}

最后,再编写一个shell脚本,用于调起PHP生成所有文件内容并按照HTML模板完成写入。一个动态生成WordPress站点兼容旧设备静态页面的工具便完成了。脚本代码如下:

#!/bin/bash
……
# generate all post page
php generate.php

# render all page
for i in {0..9}; do
if [ -f "articles_page${i}.txt" ]; then
sed -i 's/&hellip;/.../g' articles_page${i}.txt

page=index.html
[ $i -gt 0 ] && page="index$((i+1)).html"

PAGINATION=""
[ -f "articles_page$((i+1)).txt" ] && PAGINATION="<a href=\"index$((i+2)).html\">��һҳ</a>"
[ $i -gt 0 ] && PAGINATION="<a href=\"index$((i)).html\">��һҳ</a> | $PAGINATION"

sed "/<!-- Begin Articles -->/,/<!-- End Articles -->/{
/<!-- Begin Articles -->/r articles_page${i}.txt
/<!-- Begin Articles -->/,/<!-- End Articles -->/d
};/<!-- Begin Pagination -->/,/<!-- End Pagination -->/c\
$PAGINATION" template.html > legacy/$page
fi
done

echo "Build OK!"

当然,为了不受HTTPS的影响,还有必要调整一下Apache的相关配置。我为这个兼容旧设备的子站点分配了一个独立的二级域名,在Apache处直接配置一个新站点,不开启HTTPS即可。

最后效果如下:

在ThinkPad 701c的IE3上,站点完全正常浏览。当然,受限于当时显示设备的色彩还原程度,图片略微存在一些噪点。而在HP 112和Sharp Zaurus 7500c这两个世纪初的移动设备上,站点依然可以很好的适配。同时由于显示设备的进步,图片显示也更加清晰。

http://legacy.yiqun-cheng.com,旧式门户欢迎访问。

相关代码已开源:https://github.com/j-10cc/WPSiteForLegacyBrowser.git。后续也将继续迭代相关内容,比如增添多媒体文件(视频、音频)等兼容性处理。欢迎关注。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注