docker搭建云盘
使用minio搭建自己的文件存储服务(新版和旧版)
一开始使用了上述教程,可以迅速搭建好云盘,但是这个云盘会自动重定向,这就导致我没办法设置反向代理,于是考虑新的办法
之后使用docker部署flask,部署过程为
一.搭建后端
1.创建 app.py 和 requirements.txt 文件
app.py 文件
此文件为 Flask 应用的主程序,简单示例如下:
from flask import Flask, request, send_file, jsonify
import os
from datetime import datetime
app = Flask(__name__)
UPLOAD_FOLDER = '/app/data'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB
# 配置日志
#logging.basicConfig(level=logging.DEBUG)
@app.route('/api/upload', methods=['POST'])
def upload():
"""文件上传接口"""
if 'file' not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "No selected file"}), 400
# 保存文件
filename = f"{datetime.now().timestamp()}_{file.filename}"
save_path = os.path.join(UPLOAD_FOLDER, filename)
file.save(save_path)
return jsonify({
"filename": filename,
"size": os.path.getsize(save_path),
"upload_time": datetime.now().isoformat()
})
@app.route('/api/download/<filename>')
def download(filename):
"""文件下载接口"""
file_path = os.path.join(UPLOAD_FOLDER, filename)
if not os.path.exists(file_path):
return jsonify({"error": "File not found"}), 404
return send_file(file_path, as_attachment=True)
@app.route('/api/files')
def list_files():
"""文件列表接口"""
files = []
for f in os.listdir(UPLOAD_FOLDER):
path = os.path.join(UPLOAD_FOLDER, f)
files.append({
"filename": f,
"size": os.path.getsize(path),
"upload_time": datetime.fromtimestamp(os.path.getctime(path)).isoformat()
})
return jsonify(files)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
requirements.txt 文件
该文件用于列出项目所需的 Python 依赖,这里仅需要 Flask:
flask
2.创建 Dockerfile
# 使用轻量级Python镜像
FROM python:3.11-alpine
# 设置工作目录
WORKDIR /app
# 复制依赖清单并安装
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制代码
COPY app.py .
# 创建数据目录
RUN mkdir -p /app/data
# 暴露端口
EXPOSE 6000
# 启动命令
CMD ["python", "app.py"]
3.构建 Docker 镜像
在包含 Dockerfile 的目录下执行以下命令来构建镜像:
docker build -t file-server .
4.运行 Docker 容器
构建好镜像后,使用以下命令运行容器:
sudo docker run -d -p 5000:5000 -v $(pwd)/data:/app/data --name my-file-server file-server
5.验证
打开浏览器,访问 http://localhost:5000/api/files,若有输出,则表明 Flask 应用在 Docker 容器中成功运行。
但我在测试时总是有问题,浏览器显示
终端显示
之后一直找不到问题,但是豆包说可以先获得容器的IP,在宿主机中执行以下命令获取容器 IP:
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' sweet_johnson
然后通过容器 IP 和端口访问:
curl http://<容器IP>:6000
居然就成功了!!!
豆包说,如果成功,说明是宿主机 localhost 解析问题(可能与 Docker 网络配置冲突)。
二、搭建前端
在Nginx的html目录下,添加index.html文件和script.js文件
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文件管理</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container mt-5">
<!-- 上传区域 -->
<div class="card mb-4 shadow">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">文件上传</h5>
</div>
<div class="card-body">
<div class="mb-3">
<input type="file" class="form-control" id="fileInput" multiple>
</div>
<button class="btn btn-success" onclick="uploadFile()">开始上传</button>
<div class="progress mt-3" style="height: 25px;">
<div id="uploadProgress" class="progress-bar progress-bar-striped"
style="width: 0%"></div>
</div>
<div id="uploadStatus" class="mt-2 text-muted small"></div>
</div>
</div>
<!-- 文件列表 -->
<div class="card shadow">
<div class="card-header bg-info text-white">
<h5 class="mb-0">已上传文件</h5>
</div>
<div class="card-body">
<table class="table table-hover">
<thead>
<tr>
<th>文件名</th>
<th>大小</th>
<th>上传时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="fileList">
<!-- 动态加载 -->
</tbody>
</table>
<button class="btn btn-sm btn-outline-secondary" onclick="loadFiles()">
刷新列表
</button>
</div>
</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
<script src="script.js"></script>
</body>
</html>
// 初始化加载文件列表
document.addEventListener('DOMContentLoaded', loadFiles);
async function loadFiles() {
try {
const response = await fetch('/api/files');
const files = await response.json();
renderFileList(files);
} catch (error) {
showAlert('获取文件列表失败: ' + error.message, 'danger');
}
}
function renderFileList(files) {
const tbody = document.getElementById('fileList');
tbody.innerHTML = files.map(file => `
<tr>
<td>${file.filename}</td>
<td>${formatFileSize(file.size)}</td>
<td>${new Date(file.upload_time).toLocaleString()}</td>
<td>
<button class="btn btn-sm btn-outline-primary"
onclick="downloadFile('${file.filename}')">
下载
</button>
</td>
</tr>
`).join('');
}
async function uploadFile() {
const fileInput = document.getElementById('fileInput');
if (fileInput.files.length === 0) {
showAlert('请先选择文件', 'warning');
return;
}
const file = fileInput.files[0];
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`上传失败: ${response.statusText}`);
}
showAlert('上传成功', 'success');
await loadFiles(); // 刷新列表
fileInput.value = ''; // 清空选择
} catch (error) {
showAlert(error.message, 'danger');
}
}
function downloadFile(filename) {
window.open(`/api/download/${encodeURIComponent(filename)}`, '_blank');
}
// 辅助函数
function formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const units = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + units[i];
}
function showAlert(message, type = 'info') {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type} alert-dismissible fade show mt-3`;
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.container').prepend(alertDiv);
setTimeout(() => {
alertDiv.classList.remove('show');
setTimeout(() => alertDiv.remove(), 150);
}, 3000);
}
三、部署Nginx代理
修改Nginx配置文件
server {
listen 8080;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
# location / {
# proxy_pass http://172.17.0.1:9000;
# }
location / {
root html;
index index.html index.htm;
}
# 代理后端API请求
location /api {
proxy_pass http://172.17.0.3:5000; # Flask后端地址
proxy_set_header Host $host;
#proxy_set_header X-Real-IP $remote_addr; #用于传递客户端的真实 IP 地址
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;#会将客户端的 IP 地址和之前代理服务器的 IP 地址拼接起来
# 文件上传需要调整超时时间
client_max_body_size 100M;
proxy_read_timeout 300s;
}