逃出七牛云

备份云中数据

由于众所周知的原因,国内的云存储服务不再可靠。但是我在过往中大量的使用了七牛云的外链,所以需要将七牛中的文件保存起来。

在海外vps或者国内服务仍然可用的情况下,使用七牛的SDK批量备份文件,代码如下:

# -*- coding: utf-8 -*-
# RUN TIME: Python 3.6.5
# CREATION ON: 7/30, 2018
from qiniu import Auth, BucketManager
from secret import key, secret
import urllib.request
import threading
import time,os,shutil

# 七牛相关配置
host = "rs.qbox.me"
q = Auth(key, secret)
bucket = BucketManager(q)

cdn_prefix = 'http://r9rqga72n.bkt.clouddn.com/'
bucket_name = "evink-southerneast"
prefix = 'images'

threads_count = 0


def get_file_list():
    bucket = BucketManager(q)
    # 前缀
    prefix = f"/{prefix}"
    # 列举条目
    limit = 1000
    # 列举出除'/'的所有文件以及以'/'为分隔的所有前缀
    delimiter = None
    # 标记
    marker = None
    ret, eof, info = bucket.list(bucket_name, prefix, marker, limit, delimiter)
    # print(info)
    items: list = ret.get('items')
    # 所有百分号(%25)都被转义为了百分号(%), 将 25 补充回去
    keys = []
    for item in items:
        key = item.get('key')
        idx = key.find('%')
        if idx >= 0:
            key = f'{key[:idx+1]}25{key[idx+1:]}'
        keys.append(key)
    print(keys)
    return keys

def download(key, cb):
    try:
        urllib.request.urlretrieve(cdn_prefix+key, key)
        cb(None, None)
    except Exception as e:
        cb(key, e)
        return False

def download_callback(key, e):
    global threads_count
    if key:
        print(e)
        print(f'Failed: {key}')

    threads_count -= 1



def download_task(key):
    global threads_count
    while True:
        if threads_count < 10:
            break
        time.sleep(1)

    t = threading.Thread(target=download, args=(key, download_callback))
    threads_count += 1
    t.start()

if __name__ == '__main__':
    keys = get_file_list()
    dp = __file__[:-7]
    shutil.rmtree(f'{dp}{imags}')
    os.mkdir(f'{dp}{imags}')
    for key in keys:
        download_task(key)

由于使用了多线程,大量访问可能会导致七牛云拒绝响应,或者下载超时,控制台会打印出失败的文件,需要手动处理,例如:

<urlopen error retrieval incomplete: got only 1048576 out of 1592895 bytes>
Failed: images/2017-01-26/IMG_3434.JPG
<urlopen error retrieval incomplete: got only 0 out of 2400540 bytes>
Failed: images/2021-11-07/2021_11_07_07_16_IMG_4437.jpg
<urlopen error retrieval incomplete: got only 0 out of 3448056 bytes>
Failed: images/2021-11-07/2021_11_06_12_47_IMG_4407.jpg
<urlopen error retrieval incomplete: got only 0 out of 2354957 bytes>
Failed: images/2021-11-07/2021_11_07_11_24_IMG_4438.jpg
<urlopen error retrieval incomplete: got only 0 out of 3071984 bytes>
Failed: images/IMG_0747.JPG

注:如果大量失败,需要手动调整代码以适用于不同的需求。

上传文件至新的云存储(B2Cloud)中

B2Cloud免费用户有10G存储空间和1G/天的流量,虽然1G流量看起来很少,但是套上CDN以后基本是用不完的。

B2Cloud提供了方便的CLI工具,备份文件可使用CLI上传,亦可以使用PicGo+B2上传插件上传,前者适用于VPS用户,而后者有GUI。

使用CLI

1.基于Python版本的B2 CLI

$ pip3 install --upgrade b2

或者下载对应平台的可执行文件,点这里查看下载页

2.B2 Auth

输入命令,并按照提示输入KeyId和AppKey

$ b2 authorize-account

如何获取Id和Key,见下图:

3.上传文件

$ b2 upload-file <your_bucket_name> <local_file_name> "<prefix>/<file_name>"

使用GUI上传

1.安装PicGo

2.安装B2上传插件

CDN的选择

虽然现在可以访问存储桶内的资源了,但速度慢如蜗牛,加上B2提供的外链丑陋,都是不想使用它的理由。而且如果直接使用B2外链,那么之前使用的cdn链接则处于失效状态。所以套接一个CDN网络很重要。

Cloudflare

Backblaze可以使用Cloudflare加速,并且官方出了教程,详见:https://help.backblaze.com/hc/en-us/articles/217666928-Using-Backblaze-B2-with-the-Cloudflare-CDN

另外使用CF的边缘网络进行URL重写(或者Workers),可以去掉冗余的/file/<your_bucket_name>前缀,具体效果如下:

https://cdn.evink.cn/file/test-bucket/picgo/20220423172558.png —> https://cdn.evink.cn/picgo/20220423172558.png

CloudImage

相较于Cloudfalre,CloudImage更专注于提供于图片分发服务,它有多达32种的预处理图片格式和每个月25G的免费流量和图片缓存,虽然CF也提供这些功能,但是CF需要额外收费,所以CloudImage是一个非常友好的替代品。

CloudImage使用也非常简单,它会提供一个token给你,然后你就可以加速互联网上的任意图片资源了。例如:

上面两个链接均指向此图:

这两个链接会指向同一张图片,当然CloudImage可设置CDN分发的域名白名单,这样别人就不能使用这个链接来加速他们的资源了。

除此之外,CloudImage还提供图片预处理功能,本站的相册之前一直使用的七牛云的预处理功能,迁移至CloudImage之后,只需少量的修改后便可以继续使用。而且CloudImage提供了alias,以缩短图片链接。

原图: https://img.evink.cn/light/fullsizeoutput_30b.jpeg

上面两个链接均指向此图:

最后

CF和CloudImage既可以单独使用,亦可以相互结合使用,更多功能不在此赘述。

这一次转移,有点仓促,但不完全出乎意料,很早以前我就在寻找七牛云的替代品了,并不是因为它难用,相反我认为它非常好用。但朝令夕改的政策,不自由的互联网环境,加上这次的清朗行动的铁拳,都是我逃离七牛云的原因。作为个人玩家,真的没有多余的精力去应付无休止的电话骚扰。