自动为图床添加图片(纯AI生成)

好的,非常乐意!

我们从最初的设想,经历了一系列典型且宝贵的调试过程,最终成功理清了所有环节。现在,我将把我们所有的经验和最终的正确方案,整合成一份完整、细致的从零到一的教程。


教程:使用 GitHub Actions 自动同步壁纸到私有图床

本项目旨在创建一个全自动化的工作流,该工作流可以定时(或手动触发)从 必应(Bing)NASA APOD(每日天文图) 获取最新壁纸,并将其上传到您自托管的、基于 CloudFlare 的图床服务中。

先决条件

在开始之前,请确保您拥有:

  1. 一个 GitHub 账户和代码仓库。
  2. 一个正在运行的、有上传 API 的图床服务。
  3. 您的图床域名 (例如 img.yourdomain.com)。
  4. 您的图床上传所需的认证码 (authCode)。
  5. (可选) 一个 NASA APOD 的 API 密钥 (API_KEY),以获得更高的请求额度。默认使用 DEMO_KEY 也可以。

第一步:在 GitHub 仓库中设置 Secrets

Secrets 是存储敏感信息(如密码、API 密钥)的最佳方式,它们不会在日志中暴露。

  1. 打开您的 GitHub 仓库,点击 Settings > Secrets and variables > Actions
  2. 点击 New repository secret 按钮,依次创建以下三个 Secret:
Secret 名称 存储内容 说明
YOUR_DOMAIN img.yourdomain.com 仅域名,不要包含 https:// 或末尾的 /。脚本会自动处理。
AUTH_CODE your_secret_auth_code 您的图床 API 文档中指定的 authCode
NASA_API_KEY your_nasa_api_key (可选) 您的 NASA API 密钥。如果留空,脚本将使用公共的 DEMO_KEY

第二步:创建 GitHub Actions Workflow 文件

这是自动化流程的核心,它定义了何时运行、如何运行。

  1. 在您的仓库根目录,创建文件夹路径:.github/workflows/
  2. 在该文件夹下,创建一个名为 sync.yml (或任何你喜欢的 .yml 文件名) 的文件。
  3. 将以下完整的 YAML 代码粘贴到 sync.yml 文件中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# .github/workflows/sync.yml

name: Sync Daily Wallpaper

on:
# 允许手动触发工作流
workflow_dispatch:
inputs:
source:
description: '选择图片源 (bing 或 nasa)'
required: true
default: 'bing'
type: choice
options:
- bing
- nasa

# 定时触发,使用 Cron 表达式 (UTC 时间)
schedule:
# 每天 16:05 UTC (北京时间 00:05) 自动同步必应壁纸
- cron: '5 16 * * *'

jobs:
sync_wallpaper:
runs-on: ubuntu-latest

# 定义环境变量,供 Python 脚本使用
env:
YOUR_DOMAIN: ${{ secrets.YOUR_DOMAIN }}
AUTH_CODE: ${{ secrets.AUTH_CODE }}
NASA_API_KEY: ${{ secrets.NASA_API_KEY }}

steps:
# 第一步:检出你的代码仓库
- name: Checkout repository
uses: actions/checkout@v4

# 第二步:设置 Python 环境
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'

# 第三步:安装 Python 依赖库
- name: Install dependencies
run: pip install requests

# 第四步:执行同步脚本 (处理定时任务)
# 仅在定时触发时运行 (github.event_name == 'schedule')
- name: Run sync script for Bing (Scheduled)
if: github.event_name == 'schedule'
run: python sync_bing_wallpaper.py bing

# 第五步:执行同步脚本 (处理手动触发)
# 仅在手动触发时运行 (github.event_name == 'workflow_dispatch')
- name: Run sync script (Manual Trigger)
if: github.event_name == 'workflow_dispatch'
run: python sync_bing_wallpaper.py ${{ github.event.inputs.source }}

Workflow 文件详解:

  • on: 定义触发条件。workflow_dispatch 允许您在 GitHub Actions 页面手动点击运行并选择 bingnasaschedule 则按设定的 cron 时间自动运行。
  • jobs: 定义一个名为 sync_wallpaper 的任务。
  • env: 将我们设置的 Secrets 注入到任务的环境变量中,这样 Python 脚本才能读取到它们。
  • steps:
    1. Checkout repository: 下载你的代码到运行环境中。
    2. Set up Python: 安装 Python。
    3. Install dependencies: 安装脚本所需的 requests 库。
    4. Run sync script (Scheduled): 如果是定时任务,默认执行 python sync_bing_wallpaper.py bing
    5. Run sync script (Manual Trigger): 如果是手动触发,则根据你选择的输入 (github.event.inputs.source) 来执行命令。

第三步:创建 Python 执行脚本

这是实际执行下载和上传工作的脚本。

  1. 在您的仓库根目录,创建一个名为 sync_bing_wallpaper.py 的文件。
  2. 将以下完整的 Python 代码粘贴到文件中。这段代码是经过我们多次调试后的最终版本,它精确匹配了您的 API 文档。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# sync_bing_wallpaper.py

import os
import sys
import requests
from datetime import datetime

# --- 1. 从环境变量加载配置 ---
YOUR_DOMAIN = os.environ.get("YOUR_DOMAIN")
AUTH_CODE = os.environ.get("AUTH_CODE")
NASA_API_KEY = os.environ.get("NASA_API_KEY", "DEMO_KEY") # 如果未设置,则使用 DEMO_KEY

# --- 2. 检查关键配置是否存在 ---
if not all([YOUR_DOMAIN, AUTH_CODE]):
print("错误:关键环境变量 YOUR_DOMAIN, AUTH_CODE 未设置。")
exit(1)

# --- 3. 根据 API 文档,构造全局上传 URL ---
# API 端点是 /upload,而不是 /api/upload
upload_url = f"https://{YOUR_DOMAIN.strip('/')}/upload"

print(f"配置加载成功,将上传至: {YOUR_DOMAIN}")


def upload_to_your_bed(img_filename, img_data, params):
"""根据API文档,上传图片到图床"""
print(f"正在上传 {img_filename} 到图床...")
try:
# 根据API文档,文件参数名应为 'file'
files = {'file': (img_filename, img_data)}

# 发送 POST 请求
response = requests.post(upload_url, params=params, files=files, timeout=60)

# 检查HTTP请求是否成功 (例如 4xx 或 5xx 错误)
response.raise_for_status()

# 根据API文档解析响应:格式为 [{"src": "/file/abc.jpg"}]
result = response.json()

if isinstance(result, list) and len(result) > 0 and 'src' in result[0]:
# API返回的是相对路径,我们需要自己拼接成完整URL
relative_path = result[0]['src']
full_url = f"https://{YOUR_DOMAIN.strip('/')}{relative_path}"
print(f"上传成功!图片URL: {full_url}")
return full_url
else:
print(f"上传失败,API返回了非预期的格式: {result}")
return None

except requests.exceptions.HTTPError as e:
print(f"上传失败,HTTP错误: {e}")
print(f"响应内容: {e.response.text}")
return None
except requests.exceptions.RequestException as e:
print(f"上传请求失败: {e}")
return None
except Exception as e:
print(f"处理上传时发生未知错误: {e}")
return None


def sync_bing():
"""获取必应每日壁纸并上传"""
print("--- 开始同步 [bing] 图片 ---")
try:
# 获取必应壁纸信息
bing_api_url = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN"
res = requests.get(bing_api_url, timeout=30)
res.raise_for_status()
img_info = res.json()['images'][0]
img_url = "https://www.bing.com" + img_info['url']
img_title = img_info['title'].replace(' ', '_') + ".jpg"

print(f"成功获取到今日必应壁纸: {img_title}")
print(f"图片地址: {img_url}")

# 下载图片
print("正在下载图片...")
img_res = requests.get(img_url, timeout=60)
img_res.raise_for_status()
img_data = img_res.content

# 准备上传参数
params = {
"authCode": AUTH_CODE,
"uploadNameType": "origin",
"returnFormat": "full",
"uploadFolder": "bing_wallpaper"
}

# 上传
upload_to_your_bed(img_title, img_data, params)

except Exception as e:
print(f"同步必应壁纸时出错: {e}")


def sync_nasa():
"""获取NASA每日天文图并上传"""
print("--- 开始同步 [nasa] 图片 ---")
try:
# 获取NASA APOD信息
nasa_api_url = f"https://api.nasa.gov/planetary/apod?api_key={NASA_API_KEY}"
print("正在请求 NASA APOD API...")
res = requests.get(nasa_api_url, timeout=30)
res.raise_for_status()
apod_info = res.json()

if apod_info.get("media_type") != "image":
print(f"今日APOD非图片,类型为: {apod_info.get('media_type')},跳过同步。")
return

img_url = apod_info['hdurl'] if 'hdurl' in apod_info else apod_info['url']
img_title = apod_info['title'].replace(' ', '_') + ".jpg"

print(f"成功获取到今日NASA天文图: {img_title}")
print(f"图片地址: {img_url}")

# 下载图片
print("正在下载图片...")
img_res = requests.get(img_url, timeout=120) # 天文图可能较大,超时时间长一点
img_res.raise_for_status()
img_data = img_res.content

# 准备上传参数
params = {
"authCode": AUTH_CODE,
"uploadNameType": "origin",
"returnFormat": "full",
"uploadFolder": "nasa_apod"
}

# 上传
upload_to_your_bed(img_title, img_data, params)

except Exception as e:
print(f"同步NASA APOD时出错: {e}")


if __name__ == "__main__":
start_time = datetime.now()
source = "bing" # 默认源
if len(sys.argv) > 1:
source = sys.argv[1].lower()

print(f"Running from {source.upper()} trigger. [{start_time}]")

if source == "bing":
sync_bing()
elif source == "nasa":
sync_nasa()
else:
print(f"错误:未知的图片源 '{source}'。请使用 'bing' 或 'nasa'。")
exit(1)

end_time = datetime.now()
print(f"--- 同步任务结束 [{end_time}],耗时: {end_time - start_time} ---")

第四步:运行与验证

现在,所有设置都已完成,您可以手动触发一次来测试它。

  1. 进入您仓库的 Actions 标签页。
  2. 在左侧边栏,点击 Sync Daily Wallpaper 工作流。
  3. 您会看到一个提示 “This workflow has a workflow_dispatch event”。点击右侧的 Run workflow 下拉按钮。
  4. “选择图片源” 下拉菜单中选择 bingnasa
  5. 点击绿色的 Run workflow 按钮。

工作流将开始运行。您可以点击进入任务详情,实时查看日志输出。如果一切顺利,您将在日志末尾看到 “上传成功!” 的消息,并在您的图床服务中看到新上传的图片。


这份教程总结了我们所有的调试步骤和最终的正确配置,希望能帮助您顺利地完成自动化部署!