feat(init): initial commit for Flask-based temporary file transfer service (web UI, API, SQLite)
This commit is contained in:
31
templates/download.html
Normal file
31
templates/download.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ file.filename }} - 临时文件下载</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f5f5f5; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
|
||||
.container { background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 100%; max-width: 500px; text-align: center; }
|
||||
h1 { color: #333; margin-bottom: 20px; }
|
||||
.file-info { background: #f8f9fa; padding: 20px; border-radius: 4px; margin: 20px 0; }
|
||||
.file-info p { margin: 8px 0; color: #666; }
|
||||
.file-info strong { color: #333; }
|
||||
.expires { color: #dc3545; font-size: 14px; margin-top: 10px; }
|
||||
a.download-btn { display: inline-block; padding: 12px 30px; background: #28a745; color: white; text-decoration: none; border-radius: 4px; font-size: 16px; }
|
||||
a.download-btn:hover { background: #218838; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>文件下载</h1>
|
||||
<div class="file-info">
|
||||
<p><strong>文件名:</strong>{{ file.filename }}</p>
|
||||
<p><strong>大小:</strong>{{ (file.filesize / 1024 / 1024) | round(2) }} MB</p>
|
||||
<p class="expires">过期时间:{{ file.expires_at }}</p>
|
||||
</div>
|
||||
<a href="{{ url_for('serve_file', file_id=file.id) }}" class="download-btn">下载文件</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
124
templates/index.html
Normal file
124
templates/index.html
Normal file
@@ -0,0 +1,124 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>临时文件传输</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f5f5f5; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
|
||||
.container { background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); width: 100%; max-width: 500px; }
|
||||
h1 { text-align: center; margin-bottom: 30px; color: #333; }
|
||||
.upload-area { border: 2px dashed #ccc; border-radius: 8px; padding: 40px; text-align: center; cursor: pointer; transition: all 0.3s; }
|
||||
.upload-area:hover, .upload-area.dragover { border-color: #007bff; background: #f0f8ff; }
|
||||
.upload-area input { display: none; }
|
||||
.upload-area p { color: #666; font-size: 16px; }
|
||||
.expiry { margin: 20px 0; }
|
||||
.expiry label { display: block; margin-bottom: 8px; color: #333; font-weight: 500; }
|
||||
.expiry select { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; }
|
||||
button { width: 100%; padding: 12px; background: #007bff; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; }
|
||||
button:hover { background: #0056b3; }
|
||||
button:disabled { background: #ccc; cursor: not-allowed; }
|
||||
.result { margin-top: 20px; padding: 15px; background: #d4edda; border-radius: 4px; display: none; }
|
||||
.result a { color: #155724; word-break: break-all; }
|
||||
.error { margin-top: 20px; padding: 15px; background: #f8d7da; border-radius: 4px; color: #721c24; display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>临时文件传输</h1>
|
||||
<div class="upload-area" id="uploadArea">
|
||||
<input type="file" id="fileInput">
|
||||
<p>点击或拖拽文件到此处</p>
|
||||
</div>
|
||||
<div class="expiry">
|
||||
<label>过期时间</label>
|
||||
<select id="expiry">
|
||||
{% for key, seconds in expiry_options.items() %}
|
||||
<option value="{{ key }}">{% if key == '1h' %}1小时{% elif key == '24h' %}24小时{% else %}7天{% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<button id="uploadBtn" onclick="uploadFile()">上传文件</button>
|
||||
<div class="result" id="result">
|
||||
<p>分享链接:<a href="" id="shareLink" target="_blank"></a></p>
|
||||
</div>
|
||||
<div class="error" id="error"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const uploadArea = document.getElementById('uploadArea');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
|
||||
uploadArea.addEventListener('click', () => fileInput.click());
|
||||
|
||||
uploadArea.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.add('dragover');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('dragleave', () => {
|
||||
uploadArea.classList.remove('dragover');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.remove('dragover');
|
||||
if (e.dataTransfer.files.length) {
|
||||
fileInput.files = e.dataTransfer.files;
|
||||
uploadArea.querySelector('p').textContent = e.dataTransfer.files[0].name;
|
||||
}
|
||||
});
|
||||
|
||||
fileInput.addEventListener('change', () => {
|
||||
if (fileInput.files.length) {
|
||||
uploadArea.querySelector('p').textContent = fileInput.files[0].name;
|
||||
}
|
||||
});
|
||||
|
||||
function uploadFile() {
|
||||
if (!fileInput.files.length) {
|
||||
showError('请选择文件');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', fileInput.files[0]);
|
||||
formData.append('expiry', document.getElementById('expiry').value);
|
||||
|
||||
const btn = document.getElementById('uploadBtn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '上传中...';
|
||||
|
||||
fetch('/upload', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
showError(data.error);
|
||||
} else {
|
||||
const link = document.getElementById('shareLink');
|
||||
link.href = data.share_url;
|
||||
link.textContent = data.share_url;
|
||||
document.getElementById('result').style.display = 'block';
|
||||
document.getElementById('error').style.display = 'none';
|
||||
}
|
||||
})
|
||||
.catch(err => showError('上传失败: ' + err.message))
|
||||
.finally(() => {
|
||||
btn.disabled = false;
|
||||
btn.textContent = '上传文件';
|
||||
});
|
||||
}
|
||||
|
||||
function showError(msg) {
|
||||
const errorDiv = document.getElementById('error');
|
||||
errorDiv.textContent = msg;
|
||||
errorDiv.style.display = 'block';
|
||||
document.getElementById('result').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user