一向担心的事情仍是发生了。

作为hexo多年的运用者,微博图床一向是我的默认选项,hexo+typora+iPic更是我这几年写文章的黄金组合。而图床中,新浪图床一向都是我的默认选项,速度快、稳定一起支持大图片批量上传更是让其成为了许多图床东西的默认选项。虽然本年早些的时分,部分如「ws1、ws2……」的域名就现已无法运用了,但经过某些手法仍是能够让其存活的,而最近,一切调用的微博图床图片都无法加载并提示“403 Forbidden”了。

微博图床挂了!

Tips:图片中呈现的Tengine是淘宝在Nginx的基础上修改后开源的一款Web服务器,根本上,Tengine能够被看作一个更好的Nginx,或者是Nginx的超集,详情可参考淘宝Web服务器Tengine正式开源 – The Tengine Web Server

刚得知这个音讯的时分,我的第一主意其实是十分生气的,究竟自己这几年上千张图片都是用的微博图床,如今还没备份就被403了,可仔细一想,说到底仍是把东西交在他人手里的下场,微博又不是慈善企业,也要控制本钱,一向睁一只眼闭一只眼让咱们免费用就算了,出了问题仍是不太好怪到微博上来的。

那么有什么比较好的办法处理这个问题呢?

查遍了网上一堆复制/张贴出来的文章,不是敞开反向署理便是更改恳求头,真实愿意从根本上处理问题的没几个。

假如不想将自己沉淀的博客、文章保管在印象笔记、notion、语雀这些在线平台的话,想要彻底处理这个问题最好的办法是:自建图床!

为了更好的处理问题,咱们先弄明白,403是什么,以及咱们存在微博上的图片究竟是怎么被403的。

403

百度百科,关于403过错的解说很简略

403过错是一种在网站拜访过程中,常见的过错提示,表明资源不可用。服务器理解客户的恳求,但拒绝处理它,通常由于服务器上文件或目录的权限设置导致的WEB拜访过错。

所以说到底是由于拜访者无权拜访服务器端所供给的资源。而微博图床呈现403的原因首要在于微博敞开了防盗链。

防盗链的原理很简略,站点在得知有恳求时,会先判别恳求头中的信息,假如恳求头中有Referer信息,然后根据自己的规矩来判别Referer头信息是否符合要求,Referer 信息是恳求该图片的来历地址

假如盗用网站是 https 的 协议,而图片链接是 http 的话,则从 https 向 http 发起的恳求会由于安全性的规定,而不带 referer,从而实现防盗链的绕过。官方输出图片的时分,判别了来历(Referer),便是从哪个网站拜访这个图片,假如是你的网站去加载这个图片,那么 Referer 便是你的网站地址;你的网址必定没在官方的白名单内,(当然作为可操作性极强的浏览器来说 referer 是完全能够假造一个官方的 URL 这样也也就也能够饶过限制)所以就看不到图片了。

微博图床挂了!

处理问题

解说完原理之后咱们发现,其实只要想办法在自己的个人站点中设置好referer就能够处理这个问题,但说到底也只是治标不治本,真实处理这个问题便是想办法将图片迁移到自己的个人图床上。

现在的图床东西许多,iPic、uPic、PicGo等一堆东西既免费又开源,问题在于选择什么云存储服务作为自己的图床以及怎么替换自己这上千张图片。

  1. 选择什么云存储服务
  2. 怎么替换上千张图片

什么是OSS以及怎么选择

「OSS」的英文全称是Object Storage Service,翻译成中文便是「目标存储服务」,官方一点解说便是目标存储是一种运用HTTP API存储和检索非结构化数据和元数据目标的东西。

白话文解说便是将系统所要用的文件上传到云硬盘上,该云硬盘供给了文件下载、上传等一列服务,这样的服务以及技术能够统称为OSS,业内供给OSS服务的厂商许多,知名常用且成规划的有阿里云、腾讯云、百度云、七牛云、又拍云等。

关于咱们这些个人用户来说,这些云厂商供给的服务都是足够运用的,咱们所要关心的便是本钱。

笔者运用的是七牛云,它供给了10G的免费存储,根本现已够用了。

有人会考虑将GitHub/Gitee作为图床,并且这样的文章在中文互联网里广泛撒播,由于许多人的个人站点都是保管在GitHub Pages上的,但是个人主张是不要这么做。

首先GitHub在国内的拜访就很受限,许多场景都需求科学上网才干取得完整的浏览体验。再加上GitHub官方也不推荐将Git仓库存储大文件,GitHub主张仓库保持较小,抱负情况下小于 1 GB,强烈主张小于 5 GB。

怎么替换上千张图片

替换文章中的图片链接和“把大象放进冰箱里”步骤是差不多的

  1. 下载一切的微博图床的图片
  2. 上传一切的图片到自己的图床(xx云)
  3. 对文本文件执行replaceAll操作

考虑到咱们需求迁移的文件数量较多,手动操作必定是不太可行的,因此咱们能够选用代码的办法写一个脚本完成上述操作。考虑到自己现已是一个老练的Java工程师了,这个功用就爽性用Java写了。

为了减少代码量,精简代码结构,我这儿引进了几个第三方库,当然不引进也行,假如不引进有一些繁琐而又简略的事务逻辑需求自己实现,有点浪费时间了。

整个脚本逻辑十分简略,流程如下:

微博图床挂了!

获取博客文件夹下的Markdown文件

这儿咱们直接运用hutool这个三方库,它内置了许多十分有用的东西类,获取一切markdown文件也变得十分容易

/**
 * 筛选出一切的markdown文件
 */
public static List<File> listAllMDFile() {
    List<File> files = FileUtil.loopFiles(VAULT_PATH);
    return files.stream()
      	.filter(Objects::nonNull)
        .filter(File::isFile)
        .filter(file -> StringUtils.endsWith(file.getName(), ".md"))
        .collect(Collectors.toList());
}

获取文件中的一切包含微博图床的域名

经过Hutools内置的FileReader咱们能够直接读取markdown文件的内容,因此咱们只需求解析出文章里包含微博图床的链接即可。咱们能够凭借正则表达式快速获取一段文本内容里的一切url,然后做一下filter即可。

/**
 * 获取一段文本内容里的一切url
 *
 * @param content 文本内容
 * @return 一切的url
 */
public static List<String> getAllUrlsFromContent(String content) {
    List<String> urls = new ArrayList<>();
    Pattern pattern = Pattern.compile(
        "\\b(((ht|f)tp(s?)\\:\\/\\/|~\\/|\\/)|www.)" + "(\\w+:\\w+@)?(([-\\w]+\\.)+(com|org|net|gov"
            + "|mil|biz|info|mobi|name|aero|jobs|museum" + "|travel|[a-z]{2}))(:[\\d]{1,5})?"
            + "(((\\/([-\\w~!$+|.,=]|%[a-f\\d]{2})+)+|\\/)+|\\?|#)?" + "((\\?([-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?"
            + "([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)" + "(&(?:[-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?"
            + "([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)*)*" + "(#([-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)?\\b");
    Matcher matcher = pattern.matcher(content);
    while (matcher.find()) {
        urls.add(matcher.group());
    }
    return urls;
}

下载图片

用Java下载文件的代码在互联网上属实是重复率最高的一批检索内容了,这儿就直接贴出代码了。

public static void download(String urlString, String fileName) throws IOException {
    File file = new File(fileName);
    if (file.exists()) {
        return;
    }
    URL url = null;
    OutputStream os = null;
    InputStream is = null;
    try {
        url = new URL(urlString);
        URLConnection con = url.openConnection();
        // 输入流
        is = con.getInputStream();
        // 1K的数据缓冲
        byte[] bs = new byte[1024];
        // 读取到的数据长度
        int len;
        // 输出的文件流
        os = Files.newOutputStream(Paths.get(fileName));
        // 开端读取
        while ((len = is.read(bs)) != -1) {
            os.write(bs, 0, len);
        }
    } finally {
        if (os != null) {
            os.close();
        }
        if (is != null) {
            is.close();
        }
    }
}

上传图片

下载完图片后咱们便要着手将下载下来的图片上传至咱们自己的云存储服务了,这儿直接给出七牛云上传图片的文档链接了,文档里写的十分详细,我就不赘述了

Java SDK_SDK 下载_目标存储 – 七牛开发者中心

大局处理

经过阅读代码的细节,咱们能够发现,咱们的办法粒度是单文件的,但事实上,咱们能够先将一切的文件遍历一遍,一致进行图片的下载、上传与替换,这样能够节约点时间。

一致替换的逻辑也很简略,咱们声明一个大局Map,

private static final Map<String, String> URL_MAP = Maps.newHashMap();

其中,key是旧的新浪图床的链接,value是新的自定义图床的链接。

咱们将listAllMDFile这一步中所获取到的一切文件里的一切链接保存于此,下载时只需遍历这个Map的key即可获取到需求下载的图片链接。然后将上传后得到的新链接作为value存在到该Map中即可。

全文替换链接并更新文件

有了上述这些处理步骤,接下来一步就变的反常简略,只需求遍历每个文件,将匹配到大局Map中key的链接替换成Map中的value即可。

/**
 * 替换一切的图片链接
 */
private static String replaceUrl(String content, Map<String, String> urlMap) {
    for (Map.Entry<String, String> entry : urlMap.entrySet()) {
        String oldUrl = entry.getKey();
        String newUrl = entry.getValue();
        if (StringUtils.isBlank(newUrl)) {
            continue;
        }
				content = RegExUtils.replaceAll(content, oldUrl, newUrl);
    }
    return content;
}

咱们凭借commons-lang实现字符串匹配替换,凭借Hutools实现文件的读取和写入。

files.forEach(file -> {
    try {
        FileReader fileReader = new FileReader(file.getPath());
        String content = fileReader.readString();
        String replaceContent = replaceUrl(content, URL_MAP);
        FileWriter writer = new FileWriter(file.getPath());
        writer.write(replaceContent);
    } catch (Throwable e) {
        log.error("write file error, errorMsg:{}", e.getMessage());
    }
});

为了安全起见,最好把文件放在新的目录中,不要直接替换掉本来的文件,不然程序呈现意外就麻烦了。

接下来咱们只需求运行程序,静待备份结果跑完即可。

以上便是本文的全部内容了,希望对你有所协助