最近TG图床关闭,我怕已经存档的图片丢失,便想依靠手中的url批量下载图片。没成想,没有类似的网站;只好自己做一个了。
在GPT4的部分帮助下,我得到了如图的Flask代码:
from flask import Flask, request, send_file, jsonify
import requests
import os
import zipfile
from io import BytesIO
app = Flask(__name__)
@app.route('/download-images', methods=['POST'])
def download_images():
data = request.get_json()
urls = data.get('urls', [])
if not urls:
return jsonify({"error": "No URLs provided"}), 400
zip_buffer = BytesIO()
with zipfile.ZipFile(zip_buffer, 'w') as zip_file:
for index, url in enumerate(urls):
try:
response = requests.get(url)
response.raise_for_status()
# 将图片写入 ZIP 文件
filename = f'image_{index + 1}.{url.split(".")[-1]}'
zip_file.writestr(filename, response.content)
except requests.RequestException as e:
return jsonify({"error": f"Failed to download image from {url}. Error: {e}"}), 500
zip_buffer.seek(0)
return send_file(zip_buffer, mimetype='application/zip', as_attachment=True, download_name='images.zip')
if __name__ == '__main__':
app.run(debug=True)
和这样的前端代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片批量下载</title>
<style>
body {
margin: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(to bottom right, #0072FF, #00C6FF, #E0F7FA, #FFFFFF);
font-family: Arial, sans-serif;
}
.download-panel {
background-color: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.1);
max-width: 600px;
text-align: center;
}
.download-panel h1 {
font-size: 24px;
color: #0072FF;
margin-bottom: 20px;
}
.download-panel textarea {
width: 100%;
height: 150px;
margin-bottom: 20px;
padding: 10px;
border-radius: 10px;
border: 1px solid #ccc;
font-size: 16px;
resize: none;
}
.download-panel button {
background-color: #0072FF;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
.download-panel button:hover {
background-color: #005BBB;
}
.footer {
position: absolute;
bottom: 10px;
width: 100%;
text-align: center;
font-size: 12px;
color: #888;
}
</style>
</head>
<body>
<div class="download-panel">
<h1>图片URL批量下载</h1>
<textarea id="image-urls" placeholder="请输入图片链接,每行一个"></textarea>
<button onclick="downloadImages()">下载图片</button>
<h4>图片链接每行一个,将会被打包为ZIP文件.<br>如果图片过大/数量过多,需等待一会.<br>如图片不能被服务器访问(服务器位置为香港),则无法被下载.<br>开源,保证不收集任何信息,包括您输入的URL/图片文件。</h4>
</div>
<div class="footer">
© 2024 DuckXu.Com. All rights reserved. 联系我:duckxu@duckxu.com
</div>
<script>
function downloadImages() {
const urls = document.getElementById('image-urls').value.trim().split('\n');
if (urls.length === 0) {
alert('请输入至少一个图片链接.');
return;
}
fetch('https://misaka.665656.xyz/download-images', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ urls })
})
.then(response => response.blob())
.then(blob => {
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'images.zip';
link.click();
})
.catch(error => {
console.error('下载失败:', error);
});
}
</script>
</body>
</html>
到这一步还是比较简单的,我在服务器上更新软件库,通过
pip install flask requests
安装了Flask,在媒体站里放了py文件。紧接着,第一个问题就来了:无法访问端口。
检查了一下,是宝塔拦截的,把宝塔防火墙关了就行了。我没关,但是单独放行了5000端口。
部署域名picdown.duckxu.com,保持后端运行。我使用了systemd。
先创建一个服务文件
sudo nano /etc/systemd/system/flask_app.service
再写入以下内容
[Unit]
Description=Flask App
[Service]
ExecStart=/usr/bin/python /path/to/your/app.py
WorkingDirectory=/path/to/your/app
Restart=always
User=yourusername
[Install]
WantedBy=multi-user.target
注意,ExecStart=/usr/bin/python /path/to/your/app.py这里面的/usr/bin/python 一定要保留,只修改后面,否则无法找到路径。
重新加载systemd,启动服务。
sudo systemctl daemon-reload
sudo systemctl start flask_app.service
sudo systemctl enable flask_app.service
可以通过
sudo systemctl status flask_app.service
接着,我遇到了这个问题:
picdown.duckxu.com/:1 Access to fetch at 'http://...:5000/' from origin 'http://picdown.duckxu.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
查了一下,原来是HTTPS加密的地址不能去请求HTTP未加密的地址,是“CORS”。
那安装一下吧。运行以下命令:
pip3 install flask-cors
再在Flask代码里添加CORS的内容:
from flask_cors import CORS # 导入 CORS
app = Flask(__name__)
CORS(app) # 启用 CORS
也可以限制请求来源网址,不过我这是公共API,就没有做。
CORS(app, resources={r"/*": {"origins": "http://picdown.duckxu.com"}})
接着,我发现IP地址无法申请SSL证书。那只好开一个子域名绑定到服务器5000端口上了。
server {
listen 80 443;
server_name example.com www.example.com;
location / {
proxy_pass http://localhost:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
你可以修改或只保留这个nginx配置的一部分,简单来说就是让你的域名监听5000端口。但是,域名只监听5000端口,后缀得加上。比如说我之前的IP上开放的是...:5000/download-images,那我请求的域名就得是https://example.com/download-images。很简单,在第一个html的JS里更改fetch即可,比如说我的:
fetch('https://misaka.665656.xyz/download-images', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ urls })
})
再次运行,成功请求。
haha,这个我也最近才学 不过你这刚上手就搞了个上线的项目出来好牛呀哈哈
LiuShen 2024-09-21 10:14
HTML模板是很久之前就写过的,自己实际上只写了后端。把屎山和HTML一起扔给GPT-4o,让它帮忙修改错误。出现CORS之类问题也是GPT-4o帮忙解决的。实际上我只是看了看b站python教程再加上GPT帮助,GPT在代码理解这方面是很好的老师。
DuckXu 2024-09-21 17:23
一眼看成「Flash 开发」
风清 2024-09-21 09:17
Deprecated: Function create_function() is deprecated in /www/wwwroot/i.duckxu.com/usr/plugins/AntiSpam/Plugin.php on line 75
移动端会出现上述代码在博客顶部,博主可以检查一下。
葉珊 2024-09-16 13:30
不好意思,是昨晚装上的防垃圾评论插件失效了,现在应该已经解决。感谢提醒!
DuckXu 2024-09-16 15:37