定期自动备份又拍云存储
前言
数据无价,备份无忧。
将其他平台数据备份到不同平台的好处:
- 增强数据安全性:通过定期备份到GitHub,即使又拍云服务出现问题,你的数据仍然安全。这为你的数据提供了额外的一层保护,尤其是使用国内免费服务的用户。
- 便于版本管理:GitHub支持Git版本控制系统,这意味着你可以轻松追踪数据的变化历史,这对于需要保存多个版本的数据集或文档非常有用。
- 自动化操作:使用GitHub Actions,你可以设置自动化的工作流来定期执行备份脚本。这不仅减少了手动操作的需求,还降低了人为错误的风险。
- 成本效益:对于小规模项目或个人开发者来说,使用GitHub作为备份存储可能比租用额外的云存储空间更经济。特别是当使用GitHub的免费层级时。
- 易于访问和分享:GitHub上的数据可以通过简单的链接分享给他人,这对于公开数据集或开源项目尤其有益。
预览
又拍云目录:
github目录:
操作示例
1. 新增 Workflow YML 文件
upyun_images_sync/.github/workflows/sync-images.yml
name: Sync Images from UpYun to GitHub
on:
schedule:
- cron: '0 0 * * 3' # 每周三凌晨 0 点运行
workflow_dispatch: # 允许手动触发
jobs:
sync-images:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install gitpython
- name: Run sync script
env:
UPYUN_FTP_HOST: $
UPYUN_FTP_USER: $
UPYUN_FTP_PASSWORD: $
UPYUN_FTP_PATH: $
LOCAL_DOWNLOAD_PATH: ./tmp
REPO_PATH: ./repo
GH_TOKEN: $
REPO_OWNER: $
REPO_NAME: $
BRANCH_NAME: $
run: |
python3 sync_images.py
2. 新建 Python 同步脚本
upyun_images_sync/sync_images.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import os
from ftplib import FTP
import git
import shutil
class MyFTP:
def __init__(self, host, port=21):
self.host = host
self.port = port
self.ftp = FTP()
self.ftp.encoding = 'utf8'
self.file_list = []
def login(self, username, password):
try:
self.ftp.connect(self.host, self.port)
self.ftp.login(username, password)
print(f"成功登录到 {self.host}")
print(f"当前工作目录: {self.ftp.pwd()}")
except Exception as err:
print(f"FTP 连接或登录失败,错误描述为:{err}")
sys.exit(1)
def download_file(self, local_file, remote_file):
if os.path.exists(local_file):
print(f"{local_file} 已存在,跳过下载")
return
try:
print(f"下载文件 {remote_file} 到 {local_file}")
buf_size = 1024
with open(local_file, 'wb') as file_handler:
self.ftp.retrbinary(f'RETR {remote_file}', file_handler.write, buf_size)
except Exception as err:
print(f"下载文件出错,出现异常:{err}")
def download_file_tree(self, local_path, remote_path):
try:
print(f"尝试切换到远程目录: {remote_path}")
self.ftp.cwd(remote_path)
print(f"成功切换到远程目录: {remote_path}")
except Exception as err:
print(f"远程目录 {remote_path} 不存在,继续... 具体错误描述为:{err}")
return
if not os.path.isdir(local_path):
os.makedirs(local_path)
print(f"创建本地目录 {local_path}")
print(f"当前工作目录: {self.ftp.pwd()}")
# 列出目录内容
remote_files = self.ftp.nlst()
print(f"远程目录内容: {remote_files}")
for remote_file in remote_files:
local = os.path.join(local_path, remote_file)
if remote_file in ['.', '..']:
continue
try:
self.ftp.cwd(remote_file)
print(f"下载目录:{remote_file}")
self.download_file_tree(local, os.path.join(remote_path, remote_file))
self.ftp.cwd("..")
except Exception:
print(f"下载文件:{remote_file}")
self.download_file(local, os.path.join(remote_path, remote_file))
def close(self):
print("FTP退出")
self.ftp.quit()
def sync_to_github(repo_path, branch_name):
if os.path.exists(repo_path):
shutil.rmtree(repo_path)
os.makedirs(repo_path)
repo_url = f"https://{GH_TOKEN}@github.com/{REPO_OWNER}/{REPO_NAME}.git"
print(f"克隆仓库: {repo_url}")
try:
repo = git.Repo.clone_from(repo_url, repo_path, branch=branch_name)
print(f"克隆仓库到 {repo_path}")
except git.exc.GitCommandError as e:
print(f"克隆仓库失败: {e}")
return
images_dir = os.path.join(repo_path, 'images')
if not os.path.exists(images_dir):
os.makedirs(images_dir)
shutil.copytree(LOCAL_DOWNLOAD_PATH, images_dir, dirs_exist_ok=True)
print(f"复制文件到 {images_dir}")
repo.git.add(all=True)
if repo.is_dirty():
repo.index.commit("Sync images from UpYun")
origin = repo.remote(name='origin')
origin.push()
print("推送更改到 GitHub 仓库")
else:
print("没有更改需要提交")
if __name__ == "__main__":
# 从环境变量中读取配置
UPYUN_FTP_HOST = os.getenv('UPYUN_FTP_HOST')
UPYUN_FTP_USER = os.getenv('UPYUN_FTP_USER')
UPYUN_FTP_PASSWORD = os.getenv('UPYUN_FTP_PASSWORD')
UPYUN_FTP_PATH = os.getenv('UPYUN_FTP_PATH', '/')
LOCAL_DOWNLOAD_PATH = os.getenv('LOCAL_DOWNLOAD_PATH', './tmp')
REPO_PATH = os.getenv('REPO_PATH', './repo')
GH_TOKEN = os.getenv('GH_TOKEN')
REPO_OWNER = os.getenv('REPO_OWNER')
REPO_NAME = os.getenv('REPO_NAME')
BRANCH_NAME = os.getenv('BRANCH_NAME', 'master')
# 创建 FTP 对象
my_ftp = MyFTP(UPYUN_FTP_HOST)
# 登录 FTP 服务器
my_ftp.login(UPYUN_FTP_USER, UPYUN_FTP_PASSWORD)
# 下载目录
my_ftp.download_file_tree(LOCAL_DOWNLOAD_PATH, UPYUN_FTP_PATH)
# 关闭 FTP 连接
my_ftp.close()
# 同步到 GitHub
sync_to_github(REPO_PATH, BRANCH_NAME)
3. 授予 Workflow 读写权限
将本地的项目提交到github,然后设置Workflow 读写权限
4. 新增 Secrets 变量
在github上设置Secrets 变量
序号 | 变量名 | 释义 |
---|---|---|
1 | UPYUN_FTP_HOST | 又拍云 FTP 主机地址 |
2 | UPYUN_FTP_USER | 又拍云 FTP 用户名 |
3 | UPYUN_FTP_PASSWORD | 又拍云 FTP 密码 |
4 | UPYUN_FTP_PATH | 又拍云 FTP 路径 |
5 | GH_TOKEN | GitHub 个人访问令牌 |
6 | REPO_OWNER | 仓库所有者 |
7 | REPO_NAME | 仓库名称 |
8 | BRANCH_NAME | 分支名称(默认为 master) |
变量详解:
UPYUN_FTP_HOST
:参考又拍云文档,直接使用v0.ftp.upyun.com
就行了UPYUN_FTP_USER
:格式为 operator/bucket,在又拍云 - 云存储 - 选择对应的bucket点击配置 - 存储管理 - 操作员授权 - 自己添加操作用户名和密码,假设你的又拍云云储存bucket的名称为upai-img,自定义的用户名为user,密码为123456,则UPYUN_FTP_USER就是user/upai-img
,如果你看不懂我说的,参考 又拍云视频教程UPYUN_FTP_PASSWORD
:就是上面你自己设置的123456
,图片示例UPYUN_FTP_PATH
:直接使用/
(根目录)就行了GH_TOKEN
:选择github的 Settings - 点击右侧列表最下面的 Developer Settings - Personal access tokens - tokens (classic) - Generated new token (classic),Note名字可以随意,Expiration时间选择 no Expiration,下面权限全选了,点击Generated token 就生成了想要的token,记得保存,他只显示一次,示例:abc_123456789pUS123454321WmJkE987654321
,图片示例REPO_OWNER
:就是你的github的用户名,我在github给这个项目创建仓库为https://github.com/gorpeln/upyun_images_sync,示例:gorpeln
REPO_NAME
:仓库链接后面的就是仓库名,示例:upyun_images_sync
,图片示例BRANCH_NAME
:项目分支名,一般为master/main,示例:master