Initial commit

master
gz 1 month ago
commit b8dc7fbd6d

@ -0,0 +1,140 @@
* Serving Flask app 'upload_service'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
127.0.0.1 - - [10/Jan/2025 16:15:49] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 16:19:34] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 16:23:23] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 16:23:23] "GET /upload HTTP/1.0" 200 -
* Serving Flask app 'upload_service'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
127.0.0.1 - - [10/Jan/2025 16:50:49] "GET /upload HTTP/1.0" 200 -
* Serving Flask app 'upload_service'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
127.0.0.1 - - [10/Jan/2025 16:52:39] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 16:53:30] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 16:56:53] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 16:57:20] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 16:57:35] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:30] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:35] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:37] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:41] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:44] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:44] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:44] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:45] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:45] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:45] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:32:45] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:33:18] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:34:43] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:35:36] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:36:31] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:36:54] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:37:07] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:37:38] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:37:55] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:38:09] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:38:25] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:38:41] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:38:58] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:38:59] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:39:17] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:39:20] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 17:39:25] "GET /upload HTTP/1.0" 200 -
* Serving Flask app 'upload_service'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
127.0.0.1 - - [10/Jan/2025 17:48:57] "GET /upload HTTP/1.0" 200 -
* Serving Flask app 'upload_service'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
127.0.0.1 - - [10/Jan/2025 17:49:55] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 18:00:45] "POST /upload HTTP/1.0" 200 -
Traceback (most recent call last):
File "/opt/service/python_prj/fileUpload/upload_service.py", line 1, in <module>
from flask import Flask, request, jsonify
ModuleNotFoundError: No module named 'flask'
Traceback (most recent call last):
File "/opt/service/python_prj/fileUpload/upload_service.py", line 1, in <module>
from flask import Flask, request, jsonify
ModuleNotFoundError: No module named 'flask'
Traceback (most recent call last):
File "/opt/service/python_prj/fileUpload/upload_service.py", line 1, in <module>
from flask import Flask, request, jsonify
ModuleNotFoundError: No module named 'flask'
* Serving Flask app 'upload_service'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
127.0.0.1 - - [10/Jan/2025 21:33:27] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:33:57] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:34:43] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:35:06] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:35:19] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:36:08] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:36:23] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:36:41] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:36:54] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:37:26] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:37:42] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:38:06] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:38:24] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:38:51] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:39:09] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:39:26] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:40:14] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:41:31] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:42:02] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:42:19] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:42:47] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:43:08] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:43:26] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:43:45] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:45:37] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:46:23] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:46:47] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:47:06] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:48:14] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:49:38] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:50:01] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [10/Jan/2025 21:50:16] "POST /upload HTTP/1.0" 200 -
71.6.147.254 - - [10/Jan/2025 22:47:31] "GET / HTTP/1.1" 404 -
71.6.147.254 - - [10/Jan/2025 22:47:31] "GET /favicon.ico HTTP/1.1" 404 -
35.203.210.172 - - [11/Jan/2025 03:53:45] code 400, message Bad request version ('À\x13À')
35.203.210.172 - - [11/Jan/2025 03:53:45] "\x16\x03\x01\x00î\x01\x00\x00ê\x03\x03a.Ó\x84ß\x96ä\x1c\x81v²®:Páï&r^¹¦\x16|»þµoð²aöw Z\x0c\x8eÜöa\x9bëNn\x7fÖíÎ8zÅõ9Tø\x9b\x08¯¤\x81$\x9b\x1fî\x06ë\x00&À+À/À,À0̨̩À\x09À\x13À" HTTPStatus.BAD_REQUEST -
35.203.210.172 - - [11/Jan/2025 03:53:45] code 400, message Bad HTTP/0.9 request type ("\x16\x03\x01\x00Ê\x01\x00\x00Æ\x03\x03̪H\x96\\ÝH[±¥åpaNò\x90wS¯\x02ßomª\x7fX\x0fª¤~19\x00\x00hÌ\x14Ì\x13À/À+À0À,À\x11À\x07À'À#À\x13À")
35.203.210.172 - - [11/Jan/2025 03:53:45] "\x16\x03\x01\x00Ê\x01\x00\x00Æ\x03\x03̪H\x96\\ÝH[±¥åpaNò\x90wS¯\x02ßomª\x7fX\x0fª¤~19\x00\x00hÌ\x14Ì\x13À/À+À0À,À\x11À\x07À'À#À\x13À\x09À(À$À\x14À" HTTPStatus.BAD_REQUEST -
127.0.0.1 - - [11/Jan/2025 07:33:18] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 07:34:15] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 07:35:26] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 07:35:49] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 07:38:30] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 07:39:28] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 07:40:53] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 08:09:33] "GET /upload HTTP/1.0" 200 -

@ -0,0 +1,206 @@
from flask import Flask, request, jsonify
import os
# 默认配置
DEFAULT_UPLOAD_FOLDER = "/Users/zgz/Documents/images/"
ALLOWED_EXTENSIONS = {
'png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'webp', # 图片类型
'mp4', 'mov', 'wmv', 'avi', 'm4v', 'mpg', 'mpeg', 'flv', 'mkv', '3gp', 'webm' # 视频类型
}
MAX_FILE_SIZE = 88 * 1024 * 1024 # 88MB
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = DEFAULT_UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def list_files(directory):
"""列出指定目录下的所有文件并按时间排序"""
if not os.path.exists(directory):
return []
files = []
for root, _, filenames in os.walk(directory):
for filename in filenames:
files.append(os.path.relpath(os.path.join(root, filename), directory))
files.sort(key=lambda x: os.path.getmtime(os.path.join(directory, x)), reverse=True) # 按时间降序排序
return files
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 检查是否上传了文件
if 'file' not in request.files:
return jsonify({'status': 'error', 'message': 'No file part'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'status': 'error', 'message': 'No selected file'}), 400
# 获取自定义路径并检查合法性
custom_path = request.form.get('customPath', DEFAULT_UPLOAD_FOLDER).strip()
if custom_path:
# 验证路径合法性
if not os.path.isabs(custom_path): # 必须是绝对路径
return jsonify({'status': 'error', 'message': 'Path must be an absolute path'}), 400
if ".." in custom_path: # 禁止路径包含 ".." 防止目录跳跃
return jsonify({'status': 'error', 'message': 'Path not allowed'}), 400
else:
custom_path = DEFAULT_UPLOAD_FOLDER # 默认路径
# 创建完整保存路径
save_dir = os.path.abspath(custom_path)
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 检查文件类型并保存
if file and allowed_file(file.filename):
filepath = os.path.join(save_dir, file.filename)
file.save(filepath)
uploaded_files = list_files(save_dir) # 列举上传路径中的文件
return jsonify({'status': 'success', 'message': f'File uploaded to {save_dir}/{file.filename}',
'files': uploaded_files}), 200
return jsonify({'status': 'error', 'message': 'File type not allowed'}), 400
# GET 请求返回 HTML 页面
files = list_files(DEFAULT_UPLOAD_FOLDER)
# 显示最新10个文件超出部分隐藏
files_to_show = files[:10]
files_hidden = files[10:]
files_list_html = ''.join(
f'<li>{file}</li>'
for file in files_to_show
)
hidden_files_html = ''.join(
f'<li class="hidden-file">{file}</li>'
for file in files_hidden
)
files_all_json = jsonify(files=files) # 传回所有文件列表后续可以通过AJAX加载所有文件
html_template = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
<style>
body {{
font-family: Arial, sans-serif;
background-color: #f4f7fc;
padding: 20px;
}}
h1 {{
color: #4b4f56;
font-size: 2rem;
}}
ul {{
list-style-type: none;
padding-left: 0;
}}
li {{
margin: 5px 0;
padding: 10px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
}}
.hidden-file {{
display: none;
}}
button {{
padding: 10px 20px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}}
button:hover {{
background-color: #218838;
}}
</style>
<script>
function uploadFile(event) {{
event.preventDefault();
const formData = new FormData(document.getElementById('uploadForm'));
const uploadButton = document.getElementById('uploadButton');
const progressIndicator = document.getElementById('progressIndicator');
const messageBox = document.getElementById('messageBox');
// 禁用按钮显示上传中提示
uploadButton.disabled = true;
progressIndicator.style.display = 'inline-block';
fetch('/upload', {{
method: 'POST',
body: formData
}})
.then(response => response.json())
.then(data => {{
progressIndicator.style.display = 'none'; // 隐藏进度指示
uploadButton.disabled = false; // 启用按钮
if (data.status === 'success') {{
messageBox.innerHTML = '<p style="color:green;">' + data.message + '</p>';
updateFileList(data.files); // 更新文件列表
}} else {{
messageBox.innerHTML = '<p style="color:red;">' + data.message + '</p>';
}}
}})
.catch(error => {{
progressIndicator.style.display = 'none'; // 隐藏进度指示
uploadButton.disabled = false; // 启用按钮
messageBox.innerHTML = '<p style="color:red;">Upload failed</p>';
}});
}}
function updateFileList(files) {{
const filesListHtml = files.map(file => '<li>' + file + '</li>').join('');
document.getElementById('filesList').innerHTML = filesListHtml;
}}
function toggleFiles() {{
const hiddenFiles = document.querySelectorAll('.hidden-file');
const showMoreButton = document.getElementById('showMoreButton');
hiddenFiles.forEach(file => {{
file.style.display = file.style.display === 'none' ? 'list-item' : 'none';
}});
showMoreButton.innerText = showMoreButton.innerText === 'Show All Files' ? 'Show Less Files' : 'Show All Files';
}}
</script>
</head>
<body>
<h1>Upload a file</h1>
<form id="uploadForm" onsubmit="uploadFile(event)" enctype="multipart/form-data">
<label for="customPath">Custom Path (default: {DEFAULT_UPLOAD_FOLDER}):</label>
<input type="text" id="customPath" name="customPath" placeholder="Enter absolute path (e.g., /mnt/self/)">
<input type="file" name="file" required>
<button id="uploadButton" type="submit">Upload</button>
<span id="progressIndicator" style="display:none;">Uploading...</span>
</form>
<div id="messageBox"></div>
<h2>Uploaded files (Latest files displayed):</h2>
<ul id="filesList">
{files_list_html}
{hidden_files_html}
</ul>
<button id="showMoreButton" onclick="toggleFiles()">Show All Files</button>
</body>
</html>
"""
return html_template
if __name__ == '__main__':
if not os.path.exists(DEFAULT_UPLOAD_FOLDER):
os.makedirs(DEFAULT_UPLOAD_FOLDER)
app.run(host='0.0.0.0', port=8066)

@ -0,0 +1,115 @@
from flask import Flask, request, jsonify, render_template, send_from_directory
import os
# 默认配置
DEFAULT_UPLOAD_FOLDER = "/Users/zgz/Documents/images"
#DEFAULT_UPLOAD_FOLDER = "/mnt/sese"
ALLOWED_EXTENSIONS = {
'png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'webp','heic', # 图片类型
'mp4', 'mov', 'wmv', 'avi', 'm4v', 'mpg', 'mpeg', 'flv', 'mkv', '3gp', 'webm' # 视频类型
}
MAX_FILE_SIZE = 88 * 1024 * 1024 # 88MB
app = Flask(__name__, static_folder=None)
#app = Flask(__name__, static_folder=DEFAULT_UPLOAD_FOLDER, static_url_path="/files")
app.config['UPLOAD_FOLDER'] = DEFAULT_UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def list_files(directory):
"""列出指定目录下的所有文件并按时间排序"""
if not os.path.exists(directory):
return []
files = []
for root, _, filenames in os.walk(directory):
for filename in filenames:
files.append(os.path.relpath(os.path.join(root, filename), directory))
files.sort(key=lambda x: os.path.getmtime(os.path.join(directory, x)), reverse=True) # 按时间降序排序
return files
@app.route('/delete', methods=['DELETE'])
def delete_file():
# 获取当前选择的路径,默认路径为 DEFAULT_UPLOAD_FOLDER
upload_path = request.args.get('path', DEFAULT_UPLOAD_FOLDER)
file_name = request.args.get('file')
if not file_name:
return jsonify({'status': 'error', 'message': 'File name is required'}), 400
file_path = os.path.join(upload_path, file_name)
if not os.path.exists(file_path):
return jsonify({'status': 'error', 'message': 'File not found'}), 404
try:
os.remove(file_path)
return jsonify({'status': 'success', 'message': f'File {file_name} deleted successfully'})
except Exception as e:
return jsonify({'status': 'error', 'message': f'Failed to delete file: {str(e)}'}), 500
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 检查是否上传了文件
if 'file' not in request.files:
return jsonify({'status': 'error', 'message': 'No file part'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'status': 'error', 'message': 'No selected file'}), 400
# 获取用户选择的上传路径,默认为 DEFAULT_UPLOAD_FOLDER
upload_path = request.form.get('uploadPath', DEFAULT_UPLOAD_FOLDER)
# 检查路径是否合法
allowed_paths = ['/mnt/sese', '/mnt/self', '/mnt/temp', '/Users/zgz/Documents/images', '/Users/zgz/Documents/images/share']
if upload_path not in allowed_paths:
return jsonify({'status': 'error', 'message': 'Invalid upload path'}), 400
# 检查文件类型并保存
if file and allowed_file(file.filename):
save_dir = os.path.abspath(upload_path)
if not os.path.exists(save_dir):
os.makedirs(save_dir)
filepath = os.path.join(save_dir, file.filename)
file.save(filepath)
uploaded_files = list_files(save_dir) # 列举上传路径中的文件
return jsonify({'status': 'success', 'message': f'File uploaded to {filepath}',
'files': uploaded_files}), 200
return jsonify({'status': 'error', 'message': 'File type not allowed'}), 400
# GET 请求返回 HTML 页面
files = list_files(DEFAULT_UPLOAD_FOLDER)
return render_template('index.html', files=files[:10], total_files=len(files))
@app.route('/files', methods=['GET'])
def get_files():
# 获取当前选择的路径,默认路径为 DEFAULT_UPLOAD_FOLDER
upload_path = request.args.get('path', DEFAULT_UPLOAD_FOLDER)
# 获取该路径下的文件列表
files = list_files(upload_path)
return jsonify(files=files)
@app.route('/static/<path:filename>')
def serve_dynamic_static(filename):
directory = request.args.get('path', '/mnt/sese')
full_path = os.path.join(directory, filename)
print(f"Serving file from directory: {directory}, file: {filename}")
print(f"Full path: {full_path}")
try:
return send_from_directory(directory, filename)
except FileNotFoundError:
return jsonify({"error": "File not found", "directory": directory, "filename": filename}), 404
if __name__ == '__main__':
if not os.path.exists(DEFAULT_UPLOAD_FOLDER):
os.makedirs(DEFAULT_UPLOAD_FOLDER)
app.run(host='0.0.0.0', port=8066)

@ -0,0 +1,138 @@
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
127.0.0.1 - - [11/Jan/2025 10:35:15] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:41:29] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:51:01] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:51:21] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:53:08] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:54:58] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:56:14] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:56:44] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:57:32] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:58:00] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:58:39] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:59:37] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 10:59:52] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:00:05] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:00:19] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:01:21] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:02:27] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:03:21] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:05:07] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:07:24] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:08:20] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:10:13] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:14:49] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:17:07] "GET /files/IMG_3172.gif HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:17:10] "GET /files/IMG_6922.gif HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:17:14] "GET /files/IMG_4709.gif HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:20:03] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:26:11] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:29:10] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:31:46] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 11:31:48] "GET /files/她的派里渴望满满的奶油.jpeg HTTP/1.0" 200 -
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
127.0.0.1 - - [11/Jan/2025 18:49:00] "GET /files/IMG_3865.jpeg HTTP/1.0" 404 -
127.0.0.1 - - [11/Jan/2025 18:49:02] "GET /files/0InspNN87tbR8Vki.mp4 HTTP/1.0" 404 -
127.0.0.1 - - [11/Jan/2025 18:49:05] "GET /files/IMG_3172.gif HTTP/1.0" 404 -
127.0.0.1 - - [11/Jan/2025 18:49:08] "GET /files/0InspNN87tbR8Vki.mp4 HTTP/1.0" 404 -
127.0.0.1 - - [11/Jan/2025 18:49:09] "GET /files/IMG_3865.jpeg HTTP/1.0" 404 -
127.0.0.1 - - [11/Jan/2025 18:49:12] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 18:49:12] "GET /files?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 18:49:24] "GET /static/IMG_3865.jpeg?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 18:49:33] "GET /static/IMG_4709.gif?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 18:49:36] "GET /static/IMG_3172.gif?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 18:50:58] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 18:57:08] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 18:57:45] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 18:59:49] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 19:06:17] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 19:07:30] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 19:10:42] "POST /upload HTTP/1.0" 200 -
34.77.181.91 - - [11/Jan/2025 19:52:00] "GET / HTTP/1.1" 404 -
127.0.0.1 - - [11/Jan/2025 21:16:48] "GET /static/她的派里渴望满满的奶油.jpeg?path=/mnt/self HTTP/1.0" 404 -
127.0.0.1 - - [11/Jan/2025 21:16:53] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 21:16:53] "GET /files?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 21:16:57] "GET /static/她的派里渴望满满的奶油.jpeg?path=/mnt/sese HTTP/1.0" 304 -
127.0.0.1 - - [11/Jan/2025 21:17:03] "GET /static/bd45d1292k6138be60eca14446395082.gif?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 21:17:07] "GET /static/IMG_3865.jpeg?path=/mnt/sese HTTP/1.0" 304 -
127.0.0.1 - - [11/Jan/2025 21:17:37] "GET /files?path=/mnt/self HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 21:17:41] "GET /files?path=/mnt/temp HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 21:17:43] "GET /files?path=/mnt/self HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 21:17:46] "GET /files?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 21:17:50] "GET /files?path=/mnt/temp HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:30:42] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:30:42] "GET /files?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:30:49] "GET /files?path=/mnt/temp HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:32:24] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:43:48] "GET /files?path=/mnt/self HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:43:50] "GET /static/d125515bc7e4929f5920cb39ea6595ea.jpeg?path=/mnt/self HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:44:24] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:45:52] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:46:22] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:46:59] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:47:32] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:48:03] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:48:42] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:49:16] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:49:47] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:50:15] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:51:02] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:51:17] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:51:47] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:52:18] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:52:45] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:53:42] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:53:59] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [11/Jan/2025 22:54:05] "GET /static/IMG_2058.jpeg?path=/mnt/self HTTP/1.0" 200 -
47.251.92.46 - - [12/Jan/2025 08:56:23] code 400, message Bad request version ('À\x13À')
47.251.92.46 - - [12/Jan/2025 08:56:23] "\x16\x03\x01\x00î\x01\x00\x00ê\x03\x03I\x06²ù¼\x99iÀYûøºe-\x83Ud]\x0e\x8d·b8´gÆÝ9dZ8f gÁ\x81i³öò˰©\x19NÖ½\x95g|püé<Z¡\x90T·é¥8'O;\x00&À+À/À,À0̨̩À\x09À\x13À" HTTPStatus.BAD_REQUEST -
47.251.92.46 - - [12/Jan/2025 08:56:33] "GET / HTTP/1.1" 404 -
127.0.0.1 - - [12/Jan/2025 11:48:08] "GET /files?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [12/Jan/2025 11:48:10] "GET /files?path=/mnt/temp HTTP/1.0" 200 -
127.0.0.1 - - [12/Jan/2025 11:49:08] "GET /files?path=/mnt/temp HTTP/1.0" 200 -
127.0.0.1 - - [12/Jan/2025 11:52:36] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [12/Jan/2025 11:53:46] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [12/Jan/2025 11:56:39] "POST /upload HTTP/1.0" 200 -
127.0.0.1 - - [12/Jan/2025 12:02:25] "GET /files?path=/mnt/self HTTP/1.0" 200 -
127.0.0.1 - - [12/Jan/2025 12:02:27] "GET /files?path=/mnt/temp HTTP/1.0" 200 -
127.0.0.1 - - [12/Jan/2025 12:02:38] "GET /static/2997334308573012904.mp4?path=/mnt/temp HTTP/1.0" 206 -
127.0.0.1 - - [12/Jan/2025 12:02:39] "GET /static/2997334308573012904.mp4?path=/mnt/temp HTTP/1.0" 206 -
127.0.0.1 - - [12/Jan/2025 12:02:45] "GET /static/2997334308573012904.mp4?path=/mnt/temp HTTP/1.0" 206 -
127.0.0.1 - - [12/Jan/2025 12:02:45] "GET /static/2997334308573012904.mp4?path=/mnt/temp HTTP/1.0" 206 -
127.0.0.1 - - [12/Jan/2025 12:03:18] "GET /static/2997334308573012904.mp4?path=/mnt/temp HTTP/1.0" 206 -
127.0.0.1 - - [12/Jan/2025 12:03:19] "GET /static/2997334308573012904.mp4?path=/mnt/temp HTTP/1.0" 206 -
127.0.0.1 - - [12/Jan/2025 12:03:21] "GET /static/2997334308573012904.mp4?path=/mnt/temp HTTP/1.0" 206 -
127.0.0.1 - - [12/Jan/2025 12:03:21] "GET /static/2997334308573012904.mp4?path=/mnt/temp HTTP/1.0" 206 -
127.0.0.1 - - [12/Jan/2025 12:03:39] "DELETE /delete?file=Tumblr%20-%20%20-%202024-11-17%2007-54-21.mp4&path=/mnt/temp HTTP/1.0" 200 -
162.216.150.28 - - [12/Jan/2025 14:59:34] code 400, message Bad request version ('À\x13À')
162.216.150.28 - - [12/Jan/2025 14:59:34] "\x16\x03\x01\x00î\x01\x00\x00ê\x03\x03Õ¥j\x8b\x9e×N"®\x90n\x14cªº\x80ì8Èï´\x08ðç\x83\x1c³bñ\x83ià \x06B\x92Ð\x85a\x1a¡§1ûϤ\x0bíæÕÅð\x8ai¤\x8c\x97N`\x02M\x94\x86\\\x81\x00&À+À/À,À0̨̩À\x09À\x13À" HTTPStatus.BAD_REQUEST -
162.216.150.28 - - [12/Jan/2025 14:59:35] code 400, message Bad request version ('À(À$À\x14À')
162.216.150.28 - - [12/Jan/2025 14:59:35] "\x16\x03\x01\x00Ê\x01\x00\x00Æ\x03\x03k á\x87{J§\x0bÞ\x96}\x14\x8f\x15çhìm\x1a¥ø»§å\x95\x13\x11RhIb(\x00\x00hÌ\x14Ì\x13À/À+À0À,À\x11À\x07À'À#À\x13À\x09À(À$À\x14À" HTTPStatus.BAD_REQUEST -
123.160.223.72 - - [13/Jan/2025 02:08:45] "GET / HTTP/1.1" 404 -
111.7.106.104 - - [13/Jan/2025 02:09:29] "GET / HTTP/1.1" 404 -
123.160.223.74 - - [13/Jan/2025 02:10:07] "GET / HTTP/1.1" 404 -
111.7.106.104 - - [13/Jan/2025 02:11:11] "GET / HTTP/1.1" 404 -
111.7.96.174 - - [13/Jan/2025 02:13:23] "GET / HTTP/1.1" 404 -
123.160.223.75 - - [13/Jan/2025 02:14:44] "GET / HTTP/1.1" 404 -
111.7.106.107 - - [13/Jan/2025 02:15:39] "GET / HTTP/1.1" 404 -
123.160.223.75 - - [13/Jan/2025 02:16:58] "GET / HTTP/1.1" 404 -
162.216.149.170 - - [13/Jan/2025 06:07:36] code 400, message Bad request version ('À\x13À')
162.216.149.170 - - [13/Jan/2025 06:07:36] "\x16\x03\x01\x00î\x01\x00\x00ê\x03\x03û\x15±L\x14\x91µ,Ö}\x94¢ o²\x00\x92²\x08\x1aã\\È\x15Âá\x99m\\FH* `\x95GÚ°Cü\x99wgç\x17XôóÔ­%ÿX\x9aaö~?oø>Ä\x94xa\x00&À+À/À,À0̨̩À\x09À\x13À" HTTPStatus.BAD_REQUEST -
162.216.149.170 - - [13/Jan/2025 06:07:36] code 400, message Bad request version ('À(À$À\x14À')
162.216.149.170 - - [13/Jan/2025 06:07:36] "\x16\x03\x01\x00Ê\x01\x00\x00Æ\x03\x03 5f²ç|Q³Øhëueöv\x18}{?²1/fà~Ö¥"\x07Qs}\x00\x00hÌ\x14Ì\x13À/À+À0À,À\x11À\x07À'À#À\x13À\x09À(À$À\x14À" HTTPStatus.BAD_REQUEST -
192.155.90.118 - - [13/Jan/2025 06:59:43] code 400, message Bad request version ('À\x13À')
192.155.90.118 - - [13/Jan/2025 06:59:43] "\x16\x03\x01\x00î\x01\x00\x00ê\x03\x03].j×G\x09\x9a-\x8d\x94YWb\\¼\x90¢\x93o³È\x1dEgS>Åg»Û0Ó Èh3ñc·Ðªï7ú±Ð\x94\x83\x8f\x9b\x80FV\x92¨Í\x80ù&\x9a¤\x9e\x8eli\x00&À+À/À,À0̨̩À\x09À\x13À" HTTPStatus.BAD_REQUEST -
127.0.0.1 - - [13/Jan/2025 09:08:02] "GET /files?path=/mnt/self HTTP/1.0" 200 -
127.0.0.1 - - [13/Jan/2025 09:13:49] "GET /static/IMG_1747.jpeg?path=/mnt/self HTTP/1.0" 200 -

@ -0,0 +1,301 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f7fc;
padding: 20px;
}
h1 {
color: #4b4f56;
font-size: 2rem;
}
ul {
list-style-type: none;
padding-left: 0;
}
li {
margin: 5px 0;
padding: 10px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.file-name {
flex-grow: 1;
color: #007bff;
text-decoration: underline;
cursor: pointer;
}
.delete-button {
margin-left: 10px;
padding: 5px 10px;
background-color: #dc3545;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
.delete-button:hover {
background-color: #c82333;
}
button {
padding: 10px 20px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #218838;
}
#previewOverlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8); /* 半透明遮罩 */
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
#previewContent {
background-color: #fff;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
max-width: 90%; /* 最大宽度为屏幕的90% */
max-height: 90%; /* 最大高度为屏幕的90% */
width: auto; /* 自适应宽度 */
height: auto; /* 自适应高度 */
}
img, video {
max-width: 100%; /* 图片最大宽度为容器的宽度 */
max-height: 100%; /* 图片最大高度为容器的高度 */
object-fit: contain; /* 保持原图比例,避免拉伸或裁剪 */
display: block;
}
/* 新增:确保内容区域的适配,避免裁剪 */
.preview-container {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
<script>
let allFiles = [];
let showAll = false;
let currentPath = '/mnt/sese'; // 当前选择的路径,初始为默认路径
async function uploadFile(event) {
event.preventDefault();
const formData = new FormData(document.getElementById('uploadForm'));
// 获取选择的上传路径
const uploadPath = document.getElementById('uploadPath').value;
formData.append('uploadPath', uploadPath);
const uploadButton = document.getElementById('uploadButton');
const progressIndicator = document.getElementById('progressIndicator');
const messageBox = document.getElementById('messageBox');
uploadButton.disabled = true;
progressIndicator.style.display = 'inline';
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
progressIndicator.style.display = 'none';
uploadButton.disabled = false;
if (data.status === 'success') {
messageBox.innerHTML = `<p style="color:green;">${data.message}</p>`;
allFiles = data.files;
renderFileList();
} else {
messageBox.innerHTML = `<p style="color:red;">${data.message}</p>`;
}
} catch (error) {
progressIndicator.style.display = 'none';
uploadButton.disabled = false;
messageBox.innerHTML = '<p style="color:red;">Upload failed</p>';
}
}
async function fetchFiles() {
// 获取当前选择的路径
const uploadPath = document.getElementById('uploadPath').value || currentPath;
// 请求获取该路径下的文件
const response = await fetch(`/files?path=${encodeURIComponent(uploadPath)}`);
const data = await response.json();
if (data.files) {
allFiles = data.files;
renderFileList();
} else {
console.error("Error fetching files:", data.message);
}
}
function renderFileList() {
const filesList = document.getElementById('filesList');
filesList.innerHTML = ''; // 清空现有的文件列表
const filesToShow = showAll ? allFiles : allFiles.slice(0, 10);
filesToShow.forEach(file => {
const listItem = document.createElement('li');
const fileNameSpan = document.createElement('span');
fileNameSpan.textContent = file;
fileNameSpan.className = 'file-name';
fileNameSpan.onclick = () => previewFile(file); // 点击文件名调用预览函数
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Del';
deleteButton.className = 'delete-button';
deleteButton.onclick = () => confirmDelete(file);
listItem.appendChild(fileNameSpan);
listItem.appendChild(deleteButton);
filesList.appendChild(listItem);
});
const toggleButton = document.getElementById('toggleButton');
toggleButton.textContent = showAll ? 'Show Less Files' : 'Show All Files';
toggleButton.style.display = allFiles.length > 10 ? 'inline-block' : 'none';
}
function toggleFileList() {
showAll = !showAll;
renderFileList();
}
async function confirmDelete(fileName) {
const confirmation = confirm(`Are you sure you want to delete ${fileName}?`);
if (confirmation) {
await deleteFile(fileName);
}
}
async function deleteFile(fileName) {
try {
const uploadPath = document.getElementById('uploadPath').value || currentPath; // 获取当前路径
const response = await fetch(`/delete?file=${encodeURIComponent(fileName)}&path=${encodeURIComponent(uploadPath)}`, { method: 'DELETE' });
const data = await response.json();
if (data.status === 'success') {
allFiles = allFiles.filter(file => file !== fileName);
renderFileList();
} else {
alert(`Error deleting file: ${data.message}`);
}
} catch (error) {
alert('Failed to delete file.');
}
}
function previewFile(fileName) {
const uploadPath = document.getElementById('uploadPath').value || currentPath; // 获取当前路径
const overlay = document.getElementById('previewOverlay');
const previewContent = document.getElementById('previewContent');
previewContent.innerHTML = ''; // 清空现有的预览内容
//const filePath = `/files?path=${encodeURIComponent(uploadPath)}/${fileName}`; // 拼接完整的文件路径
const filePath = `/static/${encodeURIComponent(fileName)}?path=${encodeURIComponent(uploadPath)}`; // 拼接完整的文件路径
console.log("Dynamic Static URL:", filePath);
const fileExtension = fileName.split('.').pop().toLowerCase();
const previewContainer = document.createElement('div');
previewContainer.className = 'preview-container';
if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(fileExtension)) {
const img = document.createElement('img');
img.src = filePath;
previewContainer.appendChild(img);
} else if (['mp4', 'webm', 'ogg'].includes(fileExtension)) {
const video = document.createElement('video');
video.src = filePath;
video.controls = true;
previewContainer.appendChild(video);
} else {
previewContainer.textContent = 'Preview not supported for this file type.';
}
previewContent.appendChild(previewContainer);
overlay.style.display = 'flex';
}
document.addEventListener('DOMContentLoaded', () => {
// 监听上传路径下拉框的变化
document.getElementById('uploadPath').addEventListener('change', function() {
currentPath = this.value; // 更新当前选择的路径
fetchFiles(); // 重新加载文件列表
});
fetchFiles();
const overlay = document.getElementById('previewOverlay');
overlay.addEventListener('click', (event) => {
if (event.target === overlay) {
overlay.style.display = 'none';
}
});
});
</script>
</head>
<body>
<h1>Upload a file</h1>
<form id="uploadForm" onsubmit="uploadFile(event)" enctype="multipart/form-data">
<!-- 添加路径选择 -->
<label for="uploadPath">Choose upload path:</label>
<select id="uploadPath" name="uploadPath">
<option value="/mnt/sese">/mnt/sese</option>
<option value="/mnt/self">/mnt/self</option>
<option value="/mnt/temp">/mnt/temp</option>
<!---->
<option value="/Users/zgz/Documents/images">/Users/zgz/Documents/images (Local)</option>
<option value="/Users/zgz/Documents/images/share">/Users/zgz/Documents/images/share (Local)</option>
</select>
<input type="file" name="file" required>
<button id="uploadButton" type="submit">Upload</button>
<span id="progressIndicator" style="display:none;">Uploading...</span>
</form>
<div id="messageBox"></div>
<h2>Uploaded files:</h2>
<ul id="filesList"></ul>
<button id="toggleButton" onclick="toggleFileList()" style="display:none;">Show All Files</button>
<div id="previewOverlay">
<div id="previewContent"></div>
</div>
</body>
</html>

@ -0,0 +1,123 @@
from flask import Flask, request, jsonify, render_template, send_from_directory
import os
# 默认配置
#DEFAULT_UPLOAD_FOLDER = "/Users/zgz/Documents/images"
DEFAULT_UPLOAD_FOLDER = "/mnt/sssss"
ALLOWED_EXTENSIONS = {
'png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'webp','heic', # 图片类型
'mp4', 'mov', 'wmv', 'avi', 'm4v', 'mpg', 'mpeg', 'flv', 'mkv', '3gp', 'webm' # 视频类型
}
MAX_FILE_SIZE = 888 * 1024 * 1024 # 88MB
app = Flask(__name__, static_folder=None)
#app = Flask(__name__, static_folder=DEFAULT_UPLOAD_FOLDER, static_url_path="/files")
app.config['UPLOAD_FOLDER'] = DEFAULT_UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def list_files(directory):
"""列出指定目录下的所有文件并按时间排序"""
if not os.path.exists(directory):
return []
files = []
for root, _, filenames in os.walk(directory):
for filename in filenames:
files.append(os.path.relpath(os.path.join(root, filename), directory))
files.sort(key=lambda x: os.path.getmtime(os.path.join(directory, x)), reverse=True) # 按时间降序排序
return files
@app.route('/delete', methods=['DELETE'])
def delete_file():
# 获取当前选择的路径,默认路径为 DEFAULT_UPLOAD_FOLDER
upload_path = request.args.get('path', DEFAULT_UPLOAD_FOLDER)
file_name = request.args.get('file')
if not file_name:
return jsonify({'status': 'error', 'message': 'File name is required'}), 400
file_path = os.path.join(upload_path, file_name)
if not os.path.exists(file_path):
return jsonify({'status': 'error', 'message': 'File not found'}), 404
try:
os.remove(file_path)
return jsonify({'status': 'success', 'message': f'File {file_name} deleted successfully'})
except Exception as e:
return jsonify({'status': 'error', 'message': f'Failed to delete file: {str(e)}'}), 500
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 检查是否上传了文件
if 'files' not in request.files:
return jsonify({'status': 'error', 'message': 'No files part'}), 400
files = request.files.getlist('files') # 获取多个文件
if not files or all(file.filename == '' for file in files):
return jsonify({'status': 'error', 'message': 'No selected files'}), 400
# 获取用户选择的上传路径,默认为 DEFAULT_UPLOAD_FOLDER
upload_path = request.form.get('uploadPath', DEFAULT_UPLOAD_FOLDER)
# 检查路径是否合法
allowed_paths = [
'/mnt/sssss',
]
if upload_path not in allowed_paths:
return jsonify({'status': 'error', 'message': 'Invalid upload path'}), 400
save_dir = os.path.abspath(upload_path)
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 检查文件类型并逐一保存
uploaded_files = []
for file in files:
if file and allowed_file(file.filename):
filepath = os.path.join(save_dir, file.filename)
file.save(filepath)
uploaded_files.append(file.filename)
# 列举上传路径中的文件
current_files = list_files(save_dir)
if uploaded_files:
return jsonify({
'status': 'success',
'message': f'Uploaded files: {", ".join(uploaded_files)}',
'files': current_files,
}), 200
return jsonify({'status': 'error', 'message': 'No valid files to upload'}), 400
# GET 请求返回 HTML 页面
files = list_files(DEFAULT_UPLOAD_FOLDER)
return render_template('index.html', files=files[:10], total_files=len(files))
@app.route('/files', methods=['GET'])
def get_files():
# 获取当前选择的路径,默认路径为 DEFAULT_UPLOAD_FOLDER
upload_path = request.args.get('path', DEFAULT_UPLOAD_FOLDER)
# 获取该路径下的文件列表
files = list_files(upload_path)
return jsonify(files=files)
@app.route('/static/<path:filename>')
def serve_dynamic_static(filename):
directory = request.args.get('path', '/mnt/sssss')
full_path = os.path.join(directory, filename)
print(f"Serving file from directory: {directory}, file: {filename}")
print(f"Full path: {full_path}")
try:
return send_from_directory(directory, filename)
except FileNotFoundError:
return jsonify({"error": "File not found", "directory": directory, "filename": filename}), 404
if __name__ == '__main__':
if not os.path.exists(DEFAULT_UPLOAD_FOLDER):
os.makedirs(DEFAULT_UPLOAD_FOLDER)
app.run(host='0.0.0.0', port=8207)

@ -0,0 +1,20 @@
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:8066
* Running on http://172.16.0.2:8066
Press CTRL+C to quit
127.0.0.1 - - [13/Jan/2025 11:47:04] "GET /upload HTTP/1.0" 200 -
127.0.0.1 - - [13/Jan/2025 11:47:04] "GET /files?path=/mnt/sese HTTP/1.0" 200 -
127.0.0.1 - - [13/Jan/2025 11:47:07] "GET /files?path=/Users/zgz/Documents/images HTTP/1.0" 200 -
127.0.0.1 - - [13/Jan/2025 11:47:12] "GET /files?path=/mnt/temp HTTP/1.0" 200 -
127.0.0.1 - - [13/Jan/2025 11:47:13] "GET /files?path=/mnt/self HTTP/1.0" 200 -
127.0.0.1 - - [13/Jan/2025 11:47:14] "GET /static/IMG_2505.jpeg?path=/mnt/self HTTP/1.0" 304 -

@ -0,0 +1,650 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Sese Upload</title>
<style>
:root {
--bg-primary: #0f0f14;
--bg-secondary: #18181f;
--bg-card: #1e1e28;
--bg-hover: #252532;
--border: #2a2a3a;
--text-primary: #e8e8f0;
--text-secondary: #8888a0;
--accent: #7c6af5;
--accent-glow: rgba(124, 106, 245, 0.3);
--danger: #f0566a;
--danger-glow: rgba(240, 86, 106, 0.2);
--success: #3dd68c;
--radius: 14px;
--radius-sm: 8px;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
padding: 16px;
padding-bottom: 100px;
}
/* Header */
.header {
text-align: center;
padding: 20px 0 30px;
}
.header h1 {
font-size: 1.6rem;
font-weight: 600;
background: linear-gradient(135deg, #fff 0%, #a8a4f0 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: -0.5px;
}
.header p {
color: var(--text-secondary);
font-size: 0.85rem;
margin-top: 6px;
}
/* Path Selector */
.path-selector {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 14px 16px;
margin-bottom: 20px;
}
.path-selector label {
display: block;
font-size: 0.75rem;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 8px;
}
.path-selector select {
width: 100%;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
font-size: 0.95rem;
padding: 10px 12px;
outline: none;
appearance: none;
cursor: pointer;
}
.path-selector select:focus {
border-color: var(--accent);
}
/* Upload Zone */
.upload-zone {
position: relative;
background: var(--bg-card);
border: 2px dashed var(--border);
border-radius: var(--radius);
padding: 50px 20px;
text-align: center;
margin-bottom: 20px;
transition: all 0.2s;
cursor: pointer;
overflow: hidden;
z-index: 1;
}
.upload-zone.dragover {
border-color: var(--accent);
background: rgba(124, 106, 245, 0.05);
}
.upload-zone input[type="file"] {
display: none;
}
.upload-icon {
font-size: 2.5rem;
margin-bottom: 12px;
opacity: 0.6;
}
.upload-zone p {
color: var(--text-secondary);
font-size: 0.9rem;
}
.upload-zone .hint {
font-size: 0.75rem;
margin-top: 6px;
opacity: 0.5;
}
/* Upload Button */
.upload-btn {
width: 100%;
background: linear-gradient(135deg, var(--accent) 0%, #9d8fff 100%);
color: #fff;
border: none;
border-radius: var(--radius);
padding: 14px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
margin-bottom: 16px;
transition: opacity 0.2s, transform 0.1s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.upload-btn:active { transform: scale(0.98); }
.upload-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.progress-bar {
width: 100%;
height: 4px;
background: var(--bg-secondary);
border-radius: 2px;
margin-top: 10px;
display: none;
overflow: hidden;
}
.progress-bar.active { display: block; }
.progress-bar-fill {
height: 100%;
background: var(--accent);
width: 0%;
transition: width 0.3s ease;
}
/* Message Box */
.message {
padding: 12px 16px;
border-radius: var(--radius-sm);
font-size: 0.88rem;
margin-bottom: 20px;
display: none;
}
.message.success {
background: rgba(61, 214, 140, 0.1);
border: 1px solid rgba(61, 214, 140, 0.3);
color: var(--success);
}
.message.error {
background: var(--danger-glow);
border: 1px solid rgba(240, 86, 106, 0.3);
color: var(--danger);
}
.message.show { display: block; }
/* Files Section */
.files-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
}
.files-header h2 {
font-size: 1rem;
font-weight: 600;
color: var(--text-secondary);
}
.files-count {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 20px;
padding: 4px 12px;
font-size: 0.75rem;
color: var(--text-secondary);
}
.file-list {
list-style: none;
}
.file-item {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
margin-bottom: 10px;
overflow: hidden;
transition: border-color 0.2s;
}
.file-item:hover { border-color: var(--accent); }
.file-row {
display: flex;
align-items: center;
padding: 14px 16px;
cursor: pointer;
}
.file-thumb {
width: 44px;
height: 44px;
border-radius: 8px;
background: var(--bg-secondary);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
margin-right: 14px;
flex-shrink: 0;
overflow: hidden;
}
.file-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
}
.file-info {
flex: 1;
min-width: 0;
}
.file-name {
font-size: 0.9rem;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--text-primary);
}
.file-meta {
font-size: 0.75rem;
color: var(--text-secondary);
margin-top: 3px;
}
.expand-icon {
color: var(--text-secondary);
font-size: 1.2rem;
padding: 0 4px;
transition: transform 0.2s, color 0.2s;
display: inline-block;
}
.file-actions {
display: flex;
gap: 8px;
padding: 10px 16px;
background: var(--bg-secondary);
border-top: 1px solid var(--border);
}
.action-btn {
flex: 1;
padding: 8px;
border: none;
border-radius: var(--radius-sm);
font-size: 0.82rem;
cursor: pointer;
transition: opacity 0.2s;
text-align: center;
}
.action-btn:active { opacity: 0.7; }
.action-btn.preview {
background: var(--accent);
color: #fff;
}
.action-btn.delete {
background: var(--danger-glow);
color: var(--danger);
border: 1px solid rgba(240, 86, 106, 0.3);
}
.toggle-btn {
width: 100%;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
color: var(--text-secondary);
padding: 12px;
font-size: 0.88rem;
cursor: pointer;
margin-top: 14px;
transition: background 0.2s;
}
.toggle-btn:hover { background: var(--bg-hover); }
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-secondary);
}
.empty-state .icon { font-size: 3rem; opacity: 0.3; margin-bottom: 16px; }
.empty-state p { font-size: 0.9rem; }
/* Preview Overlay */
#previewOverlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.92);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
padding: 20px;
}
#previewOverlay.show { display: flex; }
.preview-box {
background: var(--bg-card);
border-radius: var(--radius);
max-width: 100%;
max-height: 100%;
overflow: hidden;
position: relative;
}
.preview-close {
position: absolute;
top: 12px; right: 12px;
width: 36px; height: 36px;
border-radius: 50%;
background: rgba(255,255,255,0.1);
border: none;
color: #fff;
font-size: 1.2rem;
cursor: pointer;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
}
.preview-box img, .preview-box video {
max-width: 90vw;
max-height: 85vh;
display: block;
}
.preview-box video { width: 100%; }
/* Loading spinner */
.spinner {
display: inline-block;
width: 16px; height: 16px;
border: 2px solid rgba(255,255,255,0.3);
border-top-color: #fff;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* Responsive */
@media (max-width: 480px) {
body { padding: 12px; }
.header h1 { font-size: 1.4rem; }
.upload-zone { padding: 24px 16px; }
}
</style>
<script>
let allFiles = [];
let showAll = false;
const currentPath = '/mnt/sssss';
function getFileIcon(filename) {
const ext = filename.split('.').pop().toLowerCase();
const imgExts = ['jpg','jpeg','png','gif','webp','bmp','heic'];
const vidExts = ['mp4','mov','avi','wmv','m4v','mpg','flv','mkv','3gp','webm'];
if (imgExts.includes(ext)) return '🖼';
if (vidExts.includes(ext)) return '🎬';
if (ext === 'pdf') return '📄';
if (['zip','rar','7z','tar','gz'].includes(ext)) return '📦';
return '📁';
}
function formatFileSize(filename) {
// This is approximate since we don't have real size data
return '';
}
async function uploadFile(event) {
event.preventDefault();
const fileInput = document.getElementById('fileInput');
const files = fileInput.files;
if (files.length === 0) {
showMessage('请至少选择一个文件', 'error');
return;
}
const formData = new FormData();
for (let i = 0; i < files.length; i++) formData.append('files', files[i]);
formData.append('uploadPath', document.getElementById('uploadPath').value);
const btn = document.getElementById('uploadBtn');
const progressBar = document.getElementById('progressBar');
btn.disabled = true;
btn.innerHTML = '<span class="spinner"></span> 上传中...';
progressBar.classList.add('active');
try {
console.log('Starting upload, files:', files.length);
const response = await fetch('/upload', { method: 'POST', body: formData });
console.log('Response status:', response.status, 'ok:', response.ok);
const data = await response.json();
console.log('Upload success:', data);
if (data.status === 'success') {
allFiles = data.files;
renderFiles();
fileInput.value = '';
showMessage(`上传成功:${data.message}`, 'success');
} else {
showMessage(`上传失败:${data.message}`, 'error');
}
} catch (e) {
console.error('Upload error:', e);
showMessage('网络错误,上传失败', 'error');
} finally {
btn.disabled = false;
btn.innerHTML = '🚀 开始上传';
progressBar.classList.remove('active');
}
}
function showMessage(text, type) {
const el = document.getElementById('messageBox');
el.className = `message ${type} show`;
el.textContent = text;
setTimeout(() => { el.className = 'message'; }, 4000);
}
async function fetchFiles() {
const uploadPath = document.getElementById('uploadPath').value || currentPath;
try {
const resp = await fetch(`/files?path=${encodeURIComponent(uploadPath)}`);
const data = await resp.json();
if (data.files) {
allFiles = data.files;
renderFiles();
}
} catch (e) { console.error(e); }
}
function renderFiles() {
const list = document.getElementById('fileList');
const countEl = document.getElementById('filesCount');
const toggleBtn = document.getElementById('toggleBtn');
list.innerHTML = '';
countEl.textContent = allFiles.length + ' 个文件';
if (allFiles.length === 0) {
list.innerHTML = '<div class="empty-state"><div class="icon">📂</div><p>暂无文件</p></div>';
toggleBtn.style.display = 'none';
return;
}
const filesToShow = showAll ? allFiles : allFiles.slice(0, 20);
filesToShow.forEach((file, idx) => {
const item = document.createElement('div');
item.className = 'file-item';
item.innerHTML = `
<div class="file-row" onclick="toggleFileActions(${idx})">
<div class="file-thumb">${getFileIcon(file)}</div>
<div class="file-info">
<div class="file-name" title="${file}">${file}</div>
<div class="file-meta">点击展开</div>
</div>
<div style="display:flex;align-items:center;gap:6px">
<button class="action-btn delete" style="padding:6px 10px;font-size:0.8rem;background:var(--danger-glow);color:var(--danger);border:1px solid rgba(240,86,106,0.3);border-radius:6px" onclick="event.stopPropagation(); confirmDelete('${encodeURIComponent(file)}')">🗑</button>
<span class="expand-icon" id="expandIcon${idx}" style="color:var(--text-secondary);font-size:1.2rem;padding:0 4px"></span>
</div>
</div>
<div class="file-actions" id="actions${idx}" style="display:none">
<button class="action-btn preview" onclick="event.stopPropagation(); previewFile('${encodeURIComponent(file)}')">👁 预览</button>
</div>
`;
list.appendChild(item);
});
toggleBtn.style.display = allFiles.length > 20 ? 'block' : 'none';
toggleBtn.textContent = showAll ? '🔼 收起列表' : '🔽 展开全部';
}
function toggleFileActions(idx) {
const actions = document.getElementById('actions' + idx);
const expandIcon = document.getElementById('expandIcon' + idx);
const currentlyVisible = actions.style.display !== 'none';
document.querySelectorAll('.file-actions').forEach(el => { el.style.display = 'none'; });
document.querySelectorAll('.expand-icon').forEach(el => { el.textContent = ''; el.style.color = 'var(--text-secondary)'; });
if (!currentlyVisible) {
actions.style.display = 'flex';
if (expandIcon) { expandIcon.textContent = '∧'; expandIcon.style.color = 'var(--accent)'; }
}
}
function toggleShowAll() {
showAll = !showAll;
renderFiles();
}
async function confirmDelete(filename) {
if (!confirm('确定要删除这个文件吗?')) return;
const uploadPath = document.getElementById('uploadPath').value || currentPath;
try {
const resp = await fetch(`/delete?file=${filename}&path=${encodeURIComponent(uploadPath)}`, { method: 'DELETE' });
const data = await resp.json();
if (data.status === 'success') {
allFiles = allFiles.filter(f => decodeURIComponent(filename) !== f);
renderFiles();
showMessage('删除成功', 'success');
} else {
showMessage('删除失败:' + data.message, 'error');
}
} catch (e) { showMessage('删除失败', 'error'); }
}
function previewFile(filename) {
const uploadPath = document.getElementById('uploadPath').value || currentPath;
const overlay = document.getElementById('previewOverlay');
const box = document.getElementById('previewBox');
const ext = decodeURIComponent(filename).split('.').pop().toLowerCase();
const imgExts = ['jpg','jpeg','png','gif','webp','bmp','heic'];
const vidExts = ['mp4','mov','avi','wmv','m4v','mpg','flv','mkv','3gp','webm'];
const src = `/static/${filename}?path=${encodeURIComponent(uploadPath)}`;
box.innerHTML = '<button class="preview-close" onclick="closePreview()"></button>';
if (imgExts.includes(ext)) {
const img = document.createElement('img');
img.src = src;
img.onerror = () => { box.innerHTML = '<p style="padding:40px;color:#888">图片加载失败</p>'; };
box.appendChild(img);
} else if (vidExts.includes(ext)) {
const vid = document.createElement('video');
vid.src = src;
vid.controls = true;
vid.autoplay = true;
box.appendChild(vid);
} else {
box.innerHTML += '<p style="padding:40px;color:#888">该文件类型不支持预览</p>';
}
overlay.classList.add('show');
}
function closePreview() {
document.getElementById('previewOverlay').classList.remove('show');
document.getElementById('previewBox').innerHTML = '';
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('uploadPath').addEventListener('change', fetchFiles);
const zone = document.getElementById('uploadZone');
const input = document.getElementById('fileInput');
zone.addEventListener('click', e => { e.stopPropagation(); input.click(); });
zone.addEventListener('dragover', e => { e.preventDefault(); zone.classList.add('dragover'); });
zone.addEventListener('dragleave', () => zone.classList.remove('dragover'));
zone.addEventListener('drop', e => {
e.preventDefault();
zone.classList.remove('dragover');
input.files = e.dataTransfer.files;
showFileCount();
});
input.addEventListener('change', showFileCount);
function showFileCount() {
const files = input.files;
const zoneText = document.getElementById('zoneText');
const zoneHint = document.getElementById('zoneHint');
if (files.length > 0) {
zoneText.textContent = `已选 ${files.length} 个文件`;
zoneText.style.color = 'var(--accent)';
zoneHint.textContent = files.length > 3 ? '继续添加或直接点上传' : '点击上传或继续添加';
} else {
zoneText.textContent = '点击或拖拽文件到此处';
zoneText.style.color = '';
zoneHint.textContent = '支持图片、视频文件';
}
}
document.getElementById('previewOverlay').addEventListener('click', e => {
if (e.target.id === 'previewOverlay') closePreview();
});
fetchFiles();
});
</script>
</head>
<body>
<div class="header">
<h1>📁 文件管理器</h1>
<p>私有隐私空间</p>
</div>
<form id="uploadForm" onsubmit="uploadFile(event)">
<div class="path-selector">
<label>存储路径</label>
<select id="uploadPath" name="uploadPath">
<option value="/mnt/sssss">/mnt/sssss</option>
</select>
</div>
<div class="upload-zone" id="uploadZone">
<div class="upload-icon">📤</div>
<p id="zoneText">点击或拖拽文件到此处</p>
<div class="hint" id="zoneHint">支持图片、视频文件</div>
</div>
<input type="file" id="fileInput" name="files" multiple accept="image/*,video/*" style="display:none">
<button type="submit" class="upload-btn" id="uploadBtn">🚀 开始上传</button>
<div class="progress-bar" id="progressBar"><div class="progress-bar-fill"></div></div>
</form>
<div id="messageBox" class="message"></div>
<div class="files-header">
<h2>📂 文件列表</h2>
<span class="files-count" id="filesCount">0 个文件</span>
</div>
<ul class="file-list" id="fileList"></ul>
<button class="toggle-btn" id="toggleBtn" onclick="toggleShowAll()" style="display:none">🔽 展开全部</button>
<div id="previewOverlay">
<div class="preview-box" id="previewBox"></div>
</div>
</body>
</html>

@ -0,0 +1,323 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f7fc;
padding: 20px;
}
h1 {
color: #4b4f56;
font-size: 2rem;
}
ul {
list-style-type: none;
padding-left: 0;
}
li {
margin: 5px 0;
padding: 10px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.file-name {
flex-grow: 1;
color: #007bff;
text-decoration: underline;
cursor: pointer;
}
.delete-button {
margin-left: 10px;
padding: 5px 10px;
background-color: #dc3545;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
.delete-button:hover {
background-color: #c82333;
}
button {
padding: 10px 20px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #218838;
}
#previewOverlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8); /* 半透明遮罩 */
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
#previewContent {
background-color: #fff;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
max-width: 90%; /* 最大宽度为屏幕的90% */
max-height: 90%; /* 最大高度为屏幕的90% */
width: auto; /* 自适应宽度 */
height: auto; /* 自适应高度 */
}
img, video {
max-width: 100%; /* 图片最大宽度为容器的宽度 */
max-height: 100%; /* 图片最大高度为容器的高度 */
object-fit: contain; /* 保持原图比例,避免拉伸或裁剪 */
display: block;
}
/* 新增:确保内容区域的适配,避免裁剪 */
.preview-container {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
<script>
let allFiles = [];
let showAll = false;
let currentPath = '/mnt/sese'; // 当前选择的路径,初始为默认路径
async function uploadFile(event) {
event.preventDefault();
const formData = new FormData();
// 获取文件输入框中的所有文件
const fileInput = document.getElementById('fileInput');
const files = fileInput.files;
if (files.length === 0) {
alert('Please select at least one file to upload.');
return;
}
// 将所有选中的文件添加到 FormData
for (let i = 0; i < files.length; i++) {
formData.append('files', files[i]); // 'files' 是后端接收字段名
}
// 获取选择的上传路径
const uploadPath = document.getElementById('uploadPath').value;
formData.append('uploadPath', uploadPath);
// 获取按钮和显示组件
const uploadButton = document.getElementById('uploadButton');
const progressIndicator = document.getElementById('progressIndicator');
const messageBox = document.getElementById('messageBox');
// 禁用按钮并显示上传进度
uploadButton.disabled = true;
progressIndicator.style.display = 'inline';
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
// 上传完成后恢复按钮状态和隐藏进度指示器
progressIndicator.style.display = 'none';
uploadButton.disabled = false;
if (data.status === 'success') {
messageBox.innerHTML = `<p style="color:green;">${data.message}</p>`;
allFiles = data.files; // 假设后端返回文件列表
renderFileList();
} else {
messageBox.innerHTML = `<p style="color:red;">${data.message}</p>`;
}
} catch (error) {
// 处理上传失败情况
progressIndicator.style.display = 'none';
uploadButton.disabled = false;
messageBox.innerHTML = '<p style="color:red;">Upload failed</p>';
}
}
async function fetchFiles() {
// 获取当前选择的路径
const uploadPath = document.getElementById('uploadPath').value || currentPath;
// 请求获取该路径下的文件
const response = await fetch(`/files?path=${encodeURIComponent(uploadPath)}`);
const data = await response.json();
if (data.files) {
allFiles = data.files;
renderFileList();
} else {
console.error("Error fetching files:", data.message);
}
}
function renderFileList() {
const filesList = document.getElementById('filesList');
filesList.innerHTML = ''; // 清空现有的文件列表
const filesToShow = showAll ? allFiles : allFiles.slice(0, 10);
filesToShow.forEach(file => {
const listItem = document.createElement('li');
const fileNameSpan = document.createElement('span');
fileNameSpan.textContent = file;
fileNameSpan.className = 'file-name';
fileNameSpan.onclick = () => previewFile(file); // 点击文件名调用预览函数
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Del';
deleteButton.className = 'delete-button';
deleteButton.onclick = () => confirmDelete(file);
listItem.appendChild(fileNameSpan);
listItem.appendChild(deleteButton);
filesList.appendChild(listItem);
});
const toggleButton = document.getElementById('toggleButton');
toggleButton.textContent = showAll ? 'Show Less Files' : 'Show All Files';
toggleButton.style.display = allFiles.length > 10 ? 'inline-block' : 'none';
}
function toggleFileList() {
showAll = !showAll;
renderFileList();
}
async function confirmDelete(fileName) {
const confirmation = confirm(`Are you sure you want to delete ${fileName}?`);
if (confirmation) {
await deleteFile(fileName);
}
}
async function deleteFile(fileName) {
try {
const uploadPath = document.getElementById('uploadPath').value || currentPath; // 获取当前路径
const response = await fetch(`/delete?file=${encodeURIComponent(fileName)}&path=${encodeURIComponent(uploadPath)}`, { method: 'DELETE' });
const data = await response.json();
if (data.status === 'success') {
allFiles = allFiles.filter(file => file !== fileName);
renderFileList();
} else {
alert(`Error deleting file: ${data.message}`);
}
} catch (error) {
alert('Failed to delete file.');
}
}
function previewFile(fileName) {
const uploadPath = document.getElementById('uploadPath').value || currentPath; // 获取当前路径
const overlay = document.getElementById('previewOverlay');
const previewContent = document.getElementById('previewContent');
previewContent.innerHTML = ''; // 清空现有的预览内容
//const filePath = `/files?path=${encodeURIComponent(uploadPath)}/${fileName}`; // 拼接完整的文件路径
const filePath = `/static/${encodeURIComponent(fileName)}?path=${encodeURIComponent(uploadPath)}`; // 拼接完整的文件路径
console.log("Dynamic Static URL:", filePath);
const fileExtension = fileName.split('.').pop().toLowerCase();
const previewContainer = document.createElement('div');
previewContainer.className = 'preview-container';
if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(fileExtension)) {
const img = document.createElement('img');
img.src = filePath;
previewContainer.appendChild(img);
} else if (['mp4','mov','avi','wmv', 'webm', 'ogg'].includes(fileExtension)) {
const video = document.createElement('video');
video.src = filePath;
video.controls = true;
previewContainer.appendChild(video);
} else {
previewContainer.textContent = 'Preview not supported for this file type.';
}
previewContent.appendChild(previewContainer);
overlay.style.display = 'flex';
}
document.addEventListener('DOMContentLoaded', () => {
// 监听上传路径下拉框的变化
document.getElementById('uploadPath').addEventListener('change', function() {
currentPath = this.value; // 更新当前选择的路径
fetchFiles(); // 重新加载文件列表
});
fetchFiles();
const overlay = document.getElementById('previewOverlay');
overlay.addEventListener('click', (event) => {
if (event.target === overlay) {
overlay.style.display = 'none';
}
});
});
</script>
</head>
<body>
<h1>Upload a file</h1>
<form id="uploadForm" onsubmit="uploadFile(event)" enctype="multipart/form-data">
<!-- 添加路径选择 -->
<label for="uploadPath">Choose upload path:</label>
<select id="uploadPath" name="uploadPath">
<option value="/mnt/sese">/mnt/sese</option>
<option value="/mnt/self">/mnt/self</option>
<option value="/mnt/ttings">/mnt/ttings</option>
<option value="/mnt/temp">/mnt/temp</option>
<!--loc-->
<option value="/Users/zgz/Documents/images">/Users/zgz/Documents/images (Local)</option>
<option value="/Users/zgz/Documents/images/share">/Users/zgz/Documents/images/share (Local)</option>
</select>
<input type="file" id="fileInput" name="files" multiple/>
<button id="uploadButton" type="submit">Upload</button>
<span id="progressIndicator" style="display:none;">Uploading...</span>
</form>
<div id="messageBox"></div>
<h2>Uploaded files:</h2>
<ul id="filesList"></ul>
<button id="toggleButton" onclick="toggleFileList()" style="display:none;">Show All Files</button>
<div id="previewOverlay">
<div id="previewContent"></div>
</div>
</body>
</html>

@ -0,0 +1,323 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f7fc;
padding: 20px;
}
h1 {
color: #4b4f56;
font-size: 2rem;
}
ul {
list-style-type: none;
padding-left: 0;
}
li {
margin: 5px 0;
padding: 10px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.file-name {
flex-grow: 1;
color: #007bff;
text-decoration: underline;
cursor: pointer;
}
.delete-button {
margin-left: 10px;
padding: 5px 10px;
background-color: #dc3545;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
.delete-button:hover {
background-color: #c82333;
}
button {
padding: 10px 20px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #218838;
}
#previewOverlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8); /* 半透明遮罩 */
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
#previewContent {
background-color: #fff;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
max-width: 90%; /* 最大宽度为屏幕的90% */
max-height: 90%; /* 最大高度为屏幕的90% */
width: auto; /* 自适应宽度 */
height: auto; /* 自适应高度 */
}
img, video {
max-width: 100%; /* 图片最大宽度为容器的宽度 */
max-height: 100%; /* 图片最大高度为容器的高度 */
object-fit: contain; /* 保持原图比例,避免拉伸或裁剪 */
display: block;
}
/* 新增:确保内容区域的适配,避免裁剪 */
.preview-container {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
<script>
let allFiles = [];
let showAll = false;
let currentPath = '/mnt/sese'; // 当前选择的路径,初始为默认路径
async function uploadFile(event) {
event.preventDefault();
const formData = new FormData();
// 获取文件输入框中的所有文件
const fileInput = document.getElementById('fileInput');
const files = fileInput.files;
if (files.length === 0) {
alert('Please select at least one file to upload.');
return;
}
// 将所有选中的文件添加到 FormData
for (let i = 0; i < files.length; i++) {
formData.append('files', files[i]); // 'files' 是后端接收字段名
}
// 获取选择的上传路径
const uploadPath = document.getElementById('uploadPath').value;
formData.append('uploadPath', uploadPath);
// 获取按钮和显示组件
const uploadButton = document.getElementById('uploadButton');
const progressIndicator = document.getElementById('progressIndicator');
const messageBox = document.getElementById('messageBox');
// 禁用按钮并显示上传进度
uploadButton.disabled = true;
progressIndicator.style.display = 'inline';
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
// 上传完成后恢复按钮状态和隐藏进度指示器
progressIndicator.style.display = 'none';
uploadButton.disabled = false;
if (data.status === 'success') {
messageBox.innerHTML = `<p style="color:green;">${data.message}</p>`;
allFiles = data.files; // 假设后端返回文件列表
renderFileList();
} else {
messageBox.innerHTML = `<p style="color:red;">${data.message}</p>`;
}
} catch (error) {
// 处理上传失败情况
progressIndicator.style.display = 'none';
uploadButton.disabled = false;
messageBox.innerHTML = '<p style="color:red;">Upload failed</p>';
}
}
async function fetchFiles() {
// 获取当前选择的路径
const uploadPath = document.getElementById('uploadPath').value || currentPath;
// 请求获取该路径下的文件
const response = await fetch(`/files?path=${encodeURIComponent(uploadPath)}`);
const data = await response.json();
if (data.files) {
allFiles = data.files;
renderFileList();
} else {
console.error("Error fetching files:", data.message);
}
}
function renderFileList() {
const filesList = document.getElementById('filesList');
filesList.innerHTML = ''; // 清空现有的文件列表
const filesToShow = showAll ? allFiles : allFiles.slice(0, 10);
filesToShow.forEach(file => {
const listItem = document.createElement('li');
const fileNameSpan = document.createElement('span');
fileNameSpan.textContent = file;
fileNameSpan.className = 'file-name';
fileNameSpan.onclick = () => previewFile(file); // 点击文件名调用预览函数
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Del';
deleteButton.className = 'delete-button';
deleteButton.onclick = () => confirmDelete(file);
listItem.appendChild(fileNameSpan);
listItem.appendChild(deleteButton);
filesList.appendChild(listItem);
});
const toggleButton = document.getElementById('toggleButton');
toggleButton.textContent = showAll ? 'Show Less Files' : 'Show All Files';
toggleButton.style.display = allFiles.length > 10 ? 'inline-block' : 'none';
}
function toggleFileList() {
showAll = !showAll;
renderFileList();
}
async function confirmDelete(fileName) {
const confirmation = confirm(`Are you sure you want to delete ${fileName}?`);
if (confirmation) {
await deleteFile(fileName);
}
}
async function deleteFile(fileName) {
try {
const uploadPath = document.getElementById('uploadPath').value || currentPath; // 获取当前路径
const response = await fetch(`/delete?file=${encodeURIComponent(fileName)}&path=${encodeURIComponent(uploadPath)}`, { method: 'DELETE' });
const data = await response.json();
if (data.status === 'success') {
allFiles = allFiles.filter(file => file !== fileName);
renderFileList();
} else {
alert(`Error deleting file: ${data.message}`);
}
} catch (error) {
alert('Failed to delete file.');
}
}
function previewFile(fileName) {
const uploadPath = document.getElementById('uploadPath').value || currentPath; // 获取当前路径
const overlay = document.getElementById('previewOverlay');
const previewContent = document.getElementById('previewContent');
previewContent.innerHTML = ''; // 清空现有的预览内容
//const filePath = `/files?path=${encodeURIComponent(uploadPath)}/${fileName}`; // 拼接完整的文件路径
const filePath = `/static/${encodeURIComponent(fileName)}?path=${encodeURIComponent(uploadPath)}`; // 拼接完整的文件路径
console.log("Dynamic Static URL:", filePath);
const fileExtension = fileName.split('.').pop().toLowerCase();
const previewContainer = document.createElement('div');
previewContainer.className = 'preview-container';
if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(fileExtension)) {
const img = document.createElement('img');
img.src = filePath;
previewContainer.appendChild(img);
} else if (['mp4', 'webm', 'ogg'].includes(fileExtension)) {
const video = document.createElement('video');
video.src = filePath;
video.controls = true;
previewContainer.appendChild(video);
} else {
previewContainer.textContent = 'Preview not supported for this file type.';
}
previewContent.appendChild(previewContainer);
overlay.style.display = 'flex';
}
document.addEventListener('DOMContentLoaded', () => {
// 监听上传路径下拉框的变化
document.getElementById('uploadPath').addEventListener('change', function() {
currentPath = this.value; // 更新当前选择的路径
fetchFiles(); // 重新加载文件列表
});
fetchFiles();
const overlay = document.getElementById('previewOverlay');
overlay.addEventListener('click', (event) => {
if (event.target === overlay) {
overlay.style.display = 'none';
}
});
});
</script>
</head>
<body>
<h1>Upload a file</h1>
<form id="uploadForm" onsubmit="uploadFile(event)" enctype="multipart/form-data">
<!-- 添加路径选择 -->
<label for="uploadPath">Choose upload path:</label>
<select id="uploadPath" name="uploadPath">
<option value="/mnt/sese">/mnt/sese</option>
<option value="/mnt/self">/mnt/self</option>
<option value="/mnt/ttings">/mnt/ttings</option>
<option value="/mnt/temp">/mnt/temp</option>
<!--loc-->
<option value="/Users/zgz/Documents/images">/Users/zgz/Documents/images (Local)</option>
<option value="/Users/zgz/Documents/images/share">/Users/zgz/Documents/images/share (Local)</option>
</select>
<input type="file" id="fileInput" name="files" multiple/>
<button id="uploadButton" type="submit">Upload</button>
<span id="progressIndicator" style="display:none;">Uploading...</span>
</form>
<div id="messageBox"></div>
<h2>Uploaded files:</h2>
<ul id="filesList"></ul>
<button id="toggleButton" onclick="toggleFileList()" style="display:none;">Show All Files</button>
<div id="previewOverlay">
<div id="previewContent"></div>
</div>
</body>
</html>

@ -0,0 +1,32 @@
[uwsgi]
uid = uwsgi
gid = uwsgi
chdir = /opt/service/python_prj/fileUpload2/upload/ver3
# 启动服务监听的地址和端口
http-socket = 0.0.0.0:8207
# 指定虚拟环境路径
virtualenv = /opt/service/python_prj/pictoHub.env
# 指定 Flask 应用文件的路径
wsgi-file = /opt/service/python_prj/fileUpload2/upload/ver3/app.py
# 设置 Flask 的应用实例
callable = app
# 设置静态文件目录映射
static-map = /static=/opt/service/python_prj/fileUpload2/upload/ver3/static
# 日志文件
logto = /var/log/uwsgi/uwsgi.log
# 设置进程数
processes = 4
# 启动时的 Python 环境路径
home = /opt/service/python_prj/pictoHub.env
# 确保应用正常启动
touch-reload = /opt/service/python_prj/fileUpload2/upload/ver3/app.py

@ -0,0 +1,42 @@
*** Starting uWSGI 2.0.28 (64bit) on [Mon Jan 13 14:30:26 2025] ***
compiled with version: 9.4.0 on 13 January 2025 03:35:46
os: Linux-5.15.0-1070-oracle #76~20.04.1-Ubuntu SMP Wed Oct 9 14:35:51 UTC 2024
nodename: 24-11-18-2234
machine: aarch64
clock source: unix
detected number of CPU cores: 2
current working directory: /opt/service/python_prj/fileUpload/upload/ver3
detected binary path: /opt/service/python_prj/pictoHub.env/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
uWSGI running as root, you can use --uid/--gid/--chroot options
*** WARNING: you are running uWSGI as root !!! (use the --uid flag) ***
chdir() to /opt/service/python_prj/fileUpload/upload/ver3 # 应用的工作目录
chdir(): No such file or directory [core/uwsgi.c line 2617]
*** Starting uWSGI 2.0.28 (64bit) on [Mon Jan 13 14:37:07 2025] ***
compiled with version: 9.4.0 on 13 January 2025 03:35:46
os: Linux-5.15.0-1070-oracle #76~20.04.1-Ubuntu SMP Wed Oct 9 14:35:51 UTC 2024
nodename: 24-11-18-2234
machine: aarch64
clock source: unix
detected number of CPU cores: 2
current working directory: /opt/service/python_prj/fileUpload/upload/ver3
detected binary path: /opt/service/python_prj/pictoHub.env/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
uWSGI running as root, you can use --uid/--gid/--chroot options
*** WARNING: you are running uWSGI as root !!! (use the --uid flag) ***
chdir() to /opt/service/python_prj/fileUpload/upload/ver3 # 应用的工作目录
chdir(): No such file or directory [core/uwsgi.c line 2617]
*** Starting uWSGI 2.0.28 (64bit) on [Mon Jan 13 14:40:44 2025] ***
compiled with version: 9.4.0 on 13 January 2025 03:35:46
os: Linux-5.15.0-1070-oracle #76~20.04.1-Ubuntu SMP Wed Oct 9 14:35:51 UTC 2024
nodename: 24-11-18-2234
machine: aarch64
clock source: unix
detected number of CPU cores: 2
current working directory: /opt/service/python_prj/fileUpload/upload/ver3
detected binary path: /opt/service/python_prj/pictoHub.env/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
uWSGI running as root, you can use --uid/--gid/--chroot options
*** WARNING: you are running uWSGI as root !!! (use the --uid flag) ***
chdir() to /opt/service/python_prj/fileUpload/upload/ver3 # 应用的工作目录
chdir(): No such file or directory [core/uwsgi.c line 2617]

@ -0,0 +1,209 @@
from flask import Flask, request, jsonify
import os
# 默认配置
DEFAULT_UPLOAD_FOLDER = "/mnt/sese/"
ALLOWED_EXTENSIONS = {
'png', 'jpg', 'jpeg', 'gif', 'bmp', 'tiff', 'webp', # 图片类型
'mp4', 'mov', 'wmv', 'avi', 'm4v', 'mpg', 'mpeg', 'flv', 'mkv', '3gp', 'webm' # 视频类型
}
MAX_FILE_SIZE = 88 * 1024 * 1024 # 88MB
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = DEFAULT_UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def list_files(directory):
"""列出指定目录下的所有文件并按时间排序"""
if not os.path.exists(directory):
return []
files = []
for root, _, filenames in os.walk(directory):
for filename in filenames:
files.append(os.path.relpath(os.path.join(root, filename), directory))
files.sort(key=lambda x: os.path.getmtime(os.path.join(directory, x)), reverse=True) # 按时间降序排序
return files
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 检查是否上传了文件
if 'file' not in request.files:
return jsonify({'status': 'error', 'message': 'No file part'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'status': 'error', 'message': 'No selected file'}), 400
# 获取自定义路径并检查合法性
custom_path = request.form.get('customPath', DEFAULT_UPLOAD_FOLDER).strip()
if custom_path:
# 验证路径合法性
if not os.path.isabs(custom_path): # 必须是绝对路径
return jsonify({'status': 'error', 'message': 'Path must be an absolute path'}), 400
if ".." in custom_path: # 禁止路径包含 ".." 防止目录跳跃
return jsonify({'status': 'error', 'message': 'Path not allowed'}), 400
else:
custom_path = DEFAULT_UPLOAD_FOLDER # 默认路径
# 创建完整保存路径
save_dir = os.path.abspath(custom_path)
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 检查文件类型并保存
if file and allowed_file(file.filename):
filepath = os.path.join(save_dir, file.filename)
file.save(filepath)
uploaded_files = list_files(save_dir) # 列举上传路径中的文件
return jsonify({'status': 'success', 'message': f'File uploaded to {save_dir}/{file.filename}',
'files': uploaded_files}), 200
return jsonify({'status': 'error', 'message': 'File type not allowed'}), 400
# GET 请求返回 HTML 页面
files = list_files(DEFAULT_UPLOAD_FOLDER)
# 显示最新10个文件超出部分隐藏
files_to_show = files[:10]
files_hidden = files[10:]
files_list_html = ''.join(
f'<li>{file}</li>'
for file in files_to_show
)
hidden_files_html = ''.join(
f'<li class="hidden-file">{file}</li>'
for file in files_hidden
)
files_all_json = jsonify(files=files) # 传回所有文件列表后续可以通过AJAX加载所有文件
html_template = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
<style>
body {{
font-family: Arial, sans-serif;
background-color: #f4f7fc;
padding: 20px;
}}
h1 {{
color: #4b4f56;
font-size: 2rem;
}}
ul {{
list-style-type: none;
padding-left: 0;
}}
li {{
margin: 5px 0;
padding: 10px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
}}
.hidden-file {{
display: none;
}}
button {{
padding: 10px 20px;
background-color: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}}
button:hover {{
background-color: #218838;
}}
</style>
<script>
function uploadFile(event) {{
event.preventDefault();
const formData = new FormData(document.getElementById('uploadForm'));
const uploadButton = document.getElementById('uploadButton');
const progressIndicator = document.getElementById('progressIndicator');
const messageBox = document.getElementById('messageBox');
// 禁用按钮,显示上传中提示
uploadButton.disabled = true;
progressIndicator.style.display = 'inline-block';
fetch('/upload', {{
method: 'POST',
body: formData
}})
.then(response => response.json())
.then(data => {{
progressIndicator.style.display = 'none'; // 隐藏进度指示
uploadButton.disabled = false; // 启用按钮
if (data.status === 'success') {{
messageBox.innerHTML = '<p style="color:green;">' + data.message + '</p>';
updateFileList(data.files); // 更新文件列表
}} else {{
messageBox.innerHTML = '<p style="color:red;">' + data.message + '</p>';
}}
}})
.catch(error => {{
progressIndicator.style.display = 'none'; // 隐藏进度指示
uploadButton.disabled = false; // 启用按钮
messageBox.innerHTML = '<p style="color:red;">Upload failed</p>';
}});
}}
function updateFileList(files) {{
const filesListHtml = files.map(file => '<li>' + file + '</li>').join('');
document.getElementById('filesList').innerHTML = filesListHtml;
}}
function showAllFiles() {{
// 读取所有文件并显示
fetch('/upload')
.then(response => response.json())
.then(data => {{
if (data.files) {{
updateFileList(data.files); // 更新文件列表显示
document.getElementById('showMoreButton').style.display = 'none'; // 隐藏"查看全部"按钮
}}
}});
}}
</script>
</head>
<body>
<h1>Upload a file</h1>
<form id="uploadForm" onsubmit="uploadFile(event)" enctype="multipart/form-data">
<label for="customPath">Custom Path (default: {DEFAULT_UPLOAD_FOLDER}):</label>
<input type="text" id="customPath" name="customPath" placeholder="Enter absolute path (e.g., /mnt/self/)">
<input type="file" name="file" required>
<button id="uploadButton" type="submit">Upload</button>
<span id="progressIndicator" style="display:none;">Uploading...</span>
</form>
<div id="messageBox"></div>
<h2>Uploaded files (Latest files displayed):</h2>
<ul id="filesList">
{files_list_html}
{hidden_files_html}
</ul>
<button id="showMoreButton" onclick="showAllFiles()">Show All Files</button>
</body>
</html>
"""
return html_template
if __name__ == '__main__':
if not os.path.exists(DEFAULT_UPLOAD_FOLDER):
os.makedirs(DEFAULT_UPLOAD_FOLDER)
app.run(host='0.0.0.0', port=8066)

@ -0,0 +1,104 @@
from flask import Flask, request, jsonify
import os
# 配置上传目录和文件限制
UPLOAD_FOLDER = '/mnt/sese'
ALLOWED_EXTENSIONS = {
'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'md', 'doc', 'docx','mp4','mov','wmv','avi','m4v','mpg',
'xlsx', 'xls', 'rar', 'zip', 'java', 'sql', 'py', 'css','conf','sql','properties','yaml','html','htm','jsp','js','json','yml'
}
MAX_FILE_SIZE = 88 * 1024 * 1024 # 88MB
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
return jsonify({'status': 'error', 'message': 'No file part'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'status': 'error', 'message': 'No selected file'}), 400
if file and allowed_file(file.filename):
filepath = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
file.save(filepath)
return jsonify({'status': 'success', 'message': 'File uploaded successfully'}), 200
return jsonify({'status': 'error', 'message': 'File type not allowed'}), 400
# GET 请求返回 HTML 页面
files = os.listdir(app.config['UPLOAD_FOLDER'])
files_list_html = ''.join(
f'<li><a href="/temp/{file}" target="_blank">{file}</a></li>'
for file in files
)
html_template = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
<script>
function uploadFile(event) {{
event.preventDefault();
const formData = new FormData(document.getElementById('uploadForm'));
const uploadButton = document.getElementById('uploadButton');
const progressIndicator = document.getElementById('progressIndicator');
const messageBox = document.getElementById('messageBox');
// 禁用按钮,显示上传中提示
uploadButton.disabled = true;
progressIndicator.style.display = 'inline-block';
fetch('/upload', {{
method: 'POST',
body: formData
}})
.then(response => response.json())
.then(data => {{
progressIndicator.style.display = 'none'; // 隐藏进度指示
uploadButton.disabled = false; // 启用按钮
if (data.status === 'success') {{
messageBox.innerHTML = '<p style="color:green;">' + data.message + '</p>';
location.reload(); // 刷新页面更新文件列表
}} else {{
messageBox.innerHTML = '<p style="color:red;">' + data.message + '</p>';
}}
}})
.catch(error => {{
progressIndicator.style.display = 'none'; // 隐藏进度指示
uploadButton.disabled = false; // 启用按钮
messageBox.innerHTML = '<p style="color:red;">Upload failed</p>';
}});
}}
</script>
</head>
<body>
<h1>Upload a file</h1>
<form id="uploadForm" onsubmit="uploadFile(event)">
<input type="file" name="file" required>
<button id="uploadButton" type="submit">Upload</button>
<span id="progressIndicator" style="display:none;">Uploading...</span>
</form>
<div id="messageBox"></div>
<h2>Uploaded list(注意:每日零晨清空列表):</h2>
<ul>
{files_list_html}
</ul>
</body>
</html>
"""
return html_template
if __name__ == '__main__':
if not os.path.exists(UPLOAD_FOLDER):
os.makedirs(UPLOAD_FOLDER)
app.run(host='0.0.0.0', port=8066)
Loading…
Cancel
Save