本文目录导读:

我来帮你实现一个文件快递柜功能的脚本,这个系统可以实现文件的临时存储、分享和下载功能。
完整实现代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
文件快递柜系统 - 基于Flask的临时文件分享服务
"""
import os
import hashlib
import uuid
import time
import shutil
from datetime import datetime, timedelta
from flask import Flask, request, render_template_string, redirect, url_for, send_file, flash, jsonify
from werkzeug.utils import secure_filename
# ==================== 配置 ====================
app = Flask(__name__)
app.secret_key = os.urandom(24)
# 文件存储配置
UPLOAD_FOLDER = 'file_locker'
EXPIRE_DAYS = 7 # 文件默认保留天数
MAX_CONTENT_LENGTH = 100 * 1024 * 1024 # 最大文件大小 (100MB)
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'zip', 'rar', '7z', 'mp4', 'mp3'}
# ==================== HTML模板 ====================
INDEX_TEMPLATE = '''
<!DOCTYPE html>
<html>
<head>📁 文件快递柜</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.header {
text-align: center;
margin-bottom: 30px;
}
.header h1 {
color: #333;
font-size: 2.5em;
margin-bottom: 10px;
}
.header p {
color: #666;
font-size: 1.1em;
}
.upload-area {
border: 2px dashed #ccc;
border-radius: 15px;
padding: 40px;
text-align: center;
transition: all 0.3s;
cursor: pointer;
}
.upload-area:hover {
border-color: #667eea;
background: #f8f9ff;
}
.upload-icon {
font-size: 48px;
color: #667eea;
}
.upload-btn {
background: #667eea;
color: white;
border: none;
padding: 12px 30px;
border-radius: 25px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
margin-top: 15px;
}
.upload-btn:hover {
background: #5a67d8;
transform: translateY(-2px);
}
.input-group {
margin: 15px 0;
text-align: left;
}
.input-group label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: 500;
}
.input-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 14px;
}
.file-list {
margin-top: 30px;
}
.file-list h2 {
color: #333;
margin-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
padding-bottom: 10px;
}
.file-item {
background: #f8f9fa;
border-radius: 10px;
padding: 15px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
transition: all 0.3s;
}
.file-item:hover {
background: #eef0ff;
transform: translateX(5px);
}
.file-info {
flex: 1;
}
.file-name {
font-weight: 600;
color: #333;
}
.file-meta {
color: #888;
font-size: 0.8em;
margin-top: 5px;
}
.file-actions {
display: flex;
gap: 10px;
}
.btn {
padding: 8px 15px;
border: none;
border-radius: 5px;
cursor: pointer;
text-decoration: none;
font-size: 13px;
transition: all 0.3s;
}
.btn-download {
background: #48bb78;
color: white;
}
.btn-download:hover {
background: #38a169;
}
.btn-delete {
background: #f56565;
color: white;
}
.btn-delete:hover {
background: #e53e3e;
}
.btn-share {
background: #4299e1;
color: white;
}
.btn-share:hover {
background: #3182ce;
}
.info-box {
background: #ebf8ff;
border-left: 4px solid #4299e1;
padding: 15px;
border-radius: 8px;
margin: 20px 0;
}
.error-box {
background: #fff5f5;
border-left: 4px solid #fc8181;
padding: 15px;
border-radius: 8px;
margin: 20px 0;
}
.flash-message {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { transform: translateY(-20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.flash-success { background: #c6f6d5; color: #22543d; }
.flash-error { background: #fed7d7; color: #9b2c2c; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📁 文件快递柜</h1>
<p>安全、便捷的文件临时存储与分享服务</p>
</div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="flash-message flash-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="upload-area" onclick="document.getElementById('fileInput').click()">
<div class="upload-icon">📤</div>
<p style="margin: 15px 0; color: #666;">点击上传文件或拖拽文件到此处</p>
<form action="/upload" method="post" enctype="multipart/form-data" id="uploadForm">
<input type="file" name="file" id="fileInput" style="display: none;" onchange="document.getElementById('uploadForm').submit()">
<div class="input-group">
<label for="expire">过期时间(天):</label>
<input type="number" name="expire" id="expire" value="7" min="1" max="30">
</div>
<div class="input-group">
<label for="password">设置密码(可选):</label>
<input type="text" name="password" id="password" placeholder="留空则无需密码">
</div>
<button type="submit" class="upload-btn">📤 上传文件</button>
</form>
</div>
<div class="file-list">
<h2>📂 文件列表</h2>
{% for file in files %}
<div class="file-item">
<div class="file-info">
<div class="file-name">{{ file.original_name }}</div>
<div class="file-meta">
大小: {{ file.size }} |
过期: {{ file.expire_time }} |
分享码: {{ file.share_code }}
</div>
</div>
<div class="file-actions">
<a href="/share/{{ file.share_code }}" class="btn btn-share">🔗 分享</a>
<a href="/download/{{ file.share_code }}" class="btn btn-download">⬇ 下载</a>
<a href="/delete/{{ file.share_code }}" class="btn btn-delete" onclick="return confirm('确定删除这个文件吗?')">🗑 删除</a>
</div>
</div>
{% else %}
<div class="info-box">
📭 快递柜为空,上传文件后这里会显示
</div>
{% endfor %}
</div>
</div>
</body>
</html>
'''
SHARE_TEMPLATE = '''
<!DOCTYPE html>
<html>
<head>📁 文件分享 - {{ file.original_name }}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
max-width: 500px;
width: 100%;
background: white;
border-radius: 20px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.header {
text-align: center;
margin-bottom: 30px;
}
.file-icon {
font-size: 64px;
margin-bottom: 15px;
}
.file-name {
font-size: 1.3em;
font-weight: 600;
color: #333;
margin-bottom: 10px;
}
.file-info {
color: #666;
font-size: 0.9em;
margin-bottom: 20px;
}
.btn-download {
display: block;
width: 100%;
padding: 15px;
background: #48bb78;
color: white;
border: none;
border-radius: 10px;
font-size: 18px;
text-align: center;
text-decoration: none;
cursor: pointer;
transition: all 0.3s;
margin-bottom: 15px;
}
.btn-download:hover {
background: #38a169;
transform: translateY(-2px);
}
.password-form {
margin-top: 20px;
}
.password-form input {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 14px;
margin-bottom: 10px;
}
.btn-submit {
width: 100%;
padding: 12px;
background: #4299e1;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
}
.btn-back {
display: block;
text-align: center;
color: #667eea;
text-decoration: none;
margin-top: 20px;
}
.error {
color: #e53e3e;
margin-bottom: 15px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="file-icon">📄</div>
<div class="file-name">{{ file.original_name }}</div>
<div class="file-info">
大小: {{ file.size }} | 过期时间: {{ file.expire_time }}
</div>
</div>
{% if error %}
<div class="error">{{ error }}</div>
{% endif %}
{% if file.has_password %}
<form action="/verify/{{ file.share_code }}" method="post" class="password-form">
<input type="password" name="password" placeholder="请输入文件密码" required>
<button type="submit" class="btn-submit">🔓 验证密码</button>
</form>
{% else %}
<a href="/download/{{ file.share_code }}" class="btn-download">⬇ 下载文件</a>
{% endif %}
<a href="/" class="btn-back">← 返回首页</a>
</div>
</body>
</html>
'''
# ==================== 文件系统管理 ====================
class FileLocker:
"""文件快递柜管理系统"""
def __init__(self, base_path=UPLOAD_FOLDER):
self.base_path = base_path
self.metadata_file = os.path.join(base_path, 'metadata.json')
self.metadata = {}
self._ensure_directories()
self._load_metadata()
def _ensure_directories(self):
"""确保目录结构存在"""
if not os.path.exists(self.base_path):
os.makedirs(self.base_path)
def _load_metadata(self):
"""加载文件元数据"""
import json
try:
if os.path.exists(self.metadata_file):
with open(self.metadata_file, 'r', encoding='utf-8') as f:
self.metadata = json.load(f)
except Exception as e:
print(f"加载元数据失败: {e}")
self.metadata = {}
def _save_metadata(self):
"""保存文件元数据"""
import json
try:
with open(self.metadata_file, 'w', encoding='utf-8') as f:
json.dump(self.metadata, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存元数据失败: {e}")
def _generate_share_code(self):
"""生成唯一的分享码"""
return uuid.uuid4().hex[:8]
def _calculate_file_size(self, filepath):
"""计算文件大小(友好格式)"""
size = os.path.getsize(filepath)
for unit in ['B', 'KB', 'MB', 'GB']:
if size < 1024:
return f"{size:.1f} {unit}"
size /= 1024
return f"{size:.1f} TB"
def _check_expired_files(self):
"""检查并清理过期文件"""
now = datetime.now()
expired_codes = []
for code, info in list(self.metadata.items()):
expire_time = datetime.fromisoformat(info['expire_time'])
if expire_time < now:
expired_codes.append(code)
# 删除物理文件
file_path = os.path.join(self.base_path, info['stored_name'])
if os.path.exists(file_path):
os.remove(file_path)
# 删除过期元数据
for code in expired_codes:
del self.metadata[code]
if expired_codes:
self._save_metadata()
return expired_codes
def upload_file(self, file, expire_days=7, password=None):
"""上传文件到快递柜"""
if not file or file.filename == '':
return None, "请选择要上传的文件"
# 检查文件扩展名
filename = secure_filename(file.filename)
if not filename:
return None, "无效的文件名"
# 生成存储信息
share_code = self._generate_share_code()
stored_name = f"{share_code}_{filename}"
file_path = os.path.join(self.base_path, stored_name)
# 计算过期时间
expire_time = datetime.now() + timedelta(days=expire_days)
# 保存文件
try:
file.save(file_path)
except Exception as e:
return None, f"文件保存失败: {str(e)}"
# 保存元数据
self.metadata[share_code] = {
'original_name': filename,
'stored_name': stored_name,
'size': self._calculate_file_size(file_path),
'upload_time': datetime.now().isoformat(),
'expire_time': expire_time.isoformat(),
'password': password,
'has_password': bool(password),
'download_count': 0
}
self._save_metadata()
return share_code, f"文件上传成功!分享码: {share_code}"
def get_file_info(self, share_code):
"""获取文件信息"""
self._check_expired_files()
return self.metadata.get(share_code)
def verify_password(self, share_code, password):
"""验证文件密码"""
info = self.metadata.get(share_code)
if not info:
return False
return info.get('password') == password
def download_file(self, share_code):
"""获取文件下载路径"""
info = self.metadata.get(share_code)
if not info:
return None, "文件不存在或已过期"
file_path = os.path.join(self.base_path, info['stored_name'])
if not os.path.exists(file_path):
del self.metadata[share_code]
self._save_metadata()
return None, "文件已被删除"
# 更新下载计数
info['download_count'] = info.get('download_count', 0) + 1
self._save_metadata()
return file_path, info['original_name']
def delete_file(self, share_code):
"""删除文件"""
info = self.metadata.get(share_code)
if not info:
return False, "文件不存在"
file_path = os.path.join(self.base_path, info['stored_name'])
if os.path.exists(file_path):
os.remove(file_path)
del self.metadata[share_code]
self._save_metadata()
return True, "文件已删除"
def list_files(self):
"""列出所有有效文件"""
self._check_expired_files()
files = []
for code, info in self.metadata.items():
files.append({
'share_code': code,
**info
})
return sorted(files, key=lambda x: x['upload_time'], reverse=True)
# ==================== Flask 路由 ====================
locker = FileLocker()
@app.route('/')
def index():
"""首页"""
files = locker.list_files()
return render_template_string(INDEX_TEMPLATE, files=files)
@app.route('/upload', methods=['POST'])
def upload():
"""上传文件"""
file = request.files.get('file')
expire_days = int(request.form.get('expire', EXPIRE_DAYS))
password = request.form.get('password', '').strip() or None
share_code, message = locker.upload_file(file, expire_days, password)
if share_code:
flash(message, 'success')
else:
flash(message, 'error')
return redirect(url_for('index'))
@app.route('/share/<share_code>')
def share(share_code):
"""文件分享页面"""
info = locker.get_file_info(share_code)
if not info:
flash('文件不存在或已过期', 'error')
return redirect(url_for('index'))
error = request.args.get('error')
return render_template_string(SHARE_TEMPLATE, file=info, error=error, share_code=share_code)
@app.route('/verify/<share_code>', methods=['POST'])
def verify(share_code):
"""验证密码"""
password = request.form.get('password', '')
if locker.verify_password(share_code, password):
return redirect(url_for('download', share_code=share_code))
else:
return redirect(url_for('share', share_code=share_code, error='密码错误'))
@app.route('/download/<share_code>')
def download(share_code):
"""下载文件"""
file_path, filename = locker.download_file(share_code)
if file_path:
return send_file(
file_path,
as_attachment=True,
download_name=filename
)
else:
flash(filename, 'error')
return redirect(url_for('index'))
@app.route('/delete/<share_code>')
def delete(share_code):
"""删除文件"""
success, message = locker.delete_file(share_code)
if success:
flash(message, 'success')
else:
flash(message, 'error')
return redirect(url_for('index'))
# ==================== 主程序入口 ====================
if __name__ == '__main__':
print("""
╔═══════════════════════════════════════════╗
║ 📁 文件快递柜系统 v1.0 ║
╠═══════════════════════════════════════════╣
║ 启动成功! ║
║ 访问地址: http://localhost:5000 ║
║ ║
║ 功能说明: ║
║ • 文件上传与分享 ║
║ • 密码保护 ║
║ • 自动过期清理 ║
║ • 下载统计 ║
╚═══════════════════════════════════════════╝
""")
# 清理过期文件
expired = locker._check_expired_files()
if expired:
print(f"已清理 {len(expired)} 个过期文件")
app.run(debug=True, host='0.0.0.0', port=5000)
安装与运行
安装依赖
pip install flask werkzeug
运行服务
python file_locker.py
访问服务
打开浏览器访问:http://localhost:5000
主要功能
📤 文件上传
- 支持多种文件格式
- 可设置过期时间(1-30天)
- 可选密码保护
🔗 文件分享
- 生成唯一分享码
- 分享页面展示文件信息
- 密码验证访问
⬇ 文件下载
- 直接下载或密码验证后下载
- 自动记录下载次数
- 原始文件名下载
🗑 文件管理
- 自动清理过期文件
- 手动删除文件
- 文件列表展示
配置说明
在脚本顶部的配置区域可以修改:
UPLOAD_FOLDER: 文件存储目录EXPIRE_DAYS: 默认过期天数MAX_CONTENT_LENGTH: 最大文件大小ALLOWED_EXTENSIONS: 允许的文件类型
使用示例
-
上传文件
- 点击上传区域选择文件
- 设置过期时间
- 可选设置访问密码
- 点击上传
-
分享文件
- 复制分享码或分享链接
- 发送给需要的人
-
下载文件
- 访问分享链接
- 输入密码(如果设置了)
- 点击下载
这个文件快递柜系统就像现实中的快递柜一样,方便临时存放和分享文件,自动管理文件生命周期!
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播