Assistant / filemanager.py
Really-amin's picture
Upload 7 files
7da6612 verified
import streamlit as st
from pathlib import Path
from datetime import datetime
import zipfile
from PIL import Image
import time
import humanize
# تنظیمات صفحه
st.set_page_config(
page_title="سیستم مدیریت فایل",
page_icon="💼",
layout="wide",
initial_sidebar_state="expanded"
)
# استایل‌های سفارشی با تم چت‌بات
CUSTOM_CSS = """
<style>
/* فونت و تنظیمات پایه */
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;700&display=swap');
* {
font-family: 'Vazirmatn', sans-serif;
direction: rtl;
}
/* کانتینر اصلی */
.main-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
border-radius: 20px;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
}
/* کارت فایل */
.file-card {
background: white;
border-radius: 15px;
padding: 1.5rem;
margin: 1rem 0;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
animation: slideIn 0.3s ease-out;
}
.file-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
/* دکمه‌های عملیات */
.action-button {
background: #2196F3;
color: white;
border: none;
border-radius: 10px;
padding: 0.8rem 1.2rem;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.action-button:hover {
background: #1976D2;
transform: scale(1.05);
}
/* ناحیه آپلود */
.upload-area {
border: 2px dashed #2196F3;
border-radius: 15px;
padding: 2rem;
text-align: center;
background: rgba(255, 255, 255, 0.9);
cursor: pointer;
transition: all 0.3s ease;
}
.upload-area:hover {
border-color: #1976D2;
background: rgba(255, 255, 255, 1);
}
/* نوار پیشرفت */
.progress-bar {
height: 4px;
background: #e0e0e0;
border-radius: 2px;
overflow: hidden;
margin: 1rem 0;
}
.progress-fill {
height: 100%;
background: #2196F3;
width: 0%;
animation: fillProgress 2s ease-out forwards;
}
/* انیمیشن‌ها */
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fillProgress {
from { width: 0%; }
to { width: 100%; }
}
/* فیلتر و جستجو */
.search-box {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
}
.search-input {
flex: 1;
padding: 0.8rem 1.2rem;
border: 2px solid #e0e0e0;
border-radius: 10px;
font-size: 1rem;
transition: all 0.3s ease;
}
.search-input:focus {
border-color: #2196F3;
outline: none;
}
/* پیام‌های اطلاع‌رسانی */
.info-message {
background: rgba(33, 150, 243, 0.1);
border-left: 4px solid #2196F3;
padding: 1rem;
border-radius: 0 10px 10px 0;
margin: 1rem 0;
}
</style>
"""
st.markdown(CUSTOM_CSS, unsafe_allow_html=True)
class FileManager:
def __init__(self):
self.root_path = Path("uploads")
self.root_path.mkdir(exist_ok=True)
def get_file_info(self, filename):
"""دریافت اطلاعات کامل فایل"""
path = self.root_path / filename
stats = path.stat()
return {
'size': humanize.naturalsize(stats.st_size),
'modified': datetime.fromtimestamp(stats.st_mtime).strftime('%Y/%m/%d %H:%M'),
'type': path.suffix[1:].upper(),
'icon': self._get_file_icon(path.suffix)
}
def _get_file_icon(self, suffix):
"""انتخاب آیکون مناسب برای هر نوع فایل"""
icons = {
'.txt': '📄',
'.pdf': '📕',
'.jpg': '🖼️',
'.jpeg': '🖼️',
'.png': '🖼️',
'.gif': '🎞️',
'.zip': '📦',
'.mp3': '🎵',
'.mp4': '🎥',
'.doc': '📘',
'.docx': '📘',
'.xls': '📊',
'.xlsx': '📊',
}
return icons.get(suffix.lower(), '📎')
def upload_file(self, file):
"""آپلود فایل با نمایش پیشرفت"""
try:
progress_text = st.empty()
progress_bar = st.progress(0)
dest_path = self.root_path / file.name
total_size = file.size
with open(dest_path, "wb") as f:
bytes_data = file.getbuffer()
f.write(bytes_data)
# شبیه‌سازی پیشرفت آپلود
for i in range(100):
time.sleep(0.01)
progress = (i + 1) / 100
progress_bar.progress(progress)
progress_text.text(f"در حال آپلود... {int(progress * 100)}%")
progress_text.empty()
progress_bar.empty()
return "success", f"✅ فایل '{file.name}' با موفقیت آپلود شد"
except Exception as e:
return "error", f"❌ خطا در آپلود فایل: {str(e)}"
def list_files(self, search_term="", file_type=None):
"""لیست فایل‌ها با قابلیت جستجو و فیلتر"""
files = []
for f in self.root_path.iterdir():
if f.is_file():
if file_type and file_type != "همه" and f.suffix.lower() != file_type.lower():
continue
if search_term and search_term.lower() not in f.name.lower():
continue
files.append(f.name)
return sorted(files)
def preview_file(self, filename):
"""پیش‌نمایش پیشرفته فایل"""
try:
path = self.root_path / filename
if path.suffix.lower() in ['.jpg', '.jpeg', '.png', '.gif']:
img = Image.open(path)
return "image", img
elif path.suffix.lower() == '.txt':
with open(path, "r", encoding="utf-8") as f:
content = f.read(1000)
if len(content) >= 1000:
content += "\n...[ادامه متن]"
return "text", content
return "unsupported", "⚠️ پیش‌نمایش برای این نوع فایل در دسترس نیست"
except Exception as e:
return "error", f"❌ خطا در نمایش فایل: {str(e)}"
def compress_files(self, files):
"""فشرده‌سازی فایل‌ها با نمایش پیشرفت"""
try:
if not files:
return "error", "⚠️ لطفاً حداقل یک فایل انتخاب کنید"
zip_name = f"compressed_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip"
zip_path = self.root_path / zip_name
progress_text = st.empty()
progress_bar = st.progress(0)
with zipfile.ZipFile(zip_path, 'w') as zipf:
for i, file in enumerate(files, 1):
file_path = self.root_path / file
zipf.write(file_path, file)
progress = i / len(files)
progress_bar.progress(progress)
progress_text.text(f"در حال فشرده‌سازی... {int(progress * 100)}%")
progress_text.empty()
progress_bar.empty()
return "success", f"✅ فایل‌ها با موفقیت در '{zip_name}' فشرده شدند"
except Exception as e:
return "error", f"❌ خطا در فشرده‌سازی: {str(e)}"
def main():
st.markdown('<div class="main-container">', unsafe_allow_html=True)
st.title("💼 سیستم مدیریت فایل پیشرفته")
file_manager = FileManager()
# بخش آپلود فایل
st.markdown("""
<div class="upload-area">
<h3>📤 آپلود فایل</h3>
<p>فایل خود را اینجا رها کنید یا کلیک کنید</p>
<div class="progress-bar">
<div class="progress-fill"></div>
</div>
</div>
""", unsafe_allow_html=True)
uploaded_file = st.file_uploader(
"",
type=["jpg", "jpeg", "png", "gif", "txt", "pdf", "doc", "docx", "xls", "xlsx", "mp3", "mp4", "zip"],
accept_multiple_files=False
)
if uploaded_file:
status, message = file_manager.upload_file(uploaded_file)
if status == "success":
st.success(message)
else:
st.error(message)
# بخش جستجو و فیلتر
col1, col2 = st.columns([2, 1])
with col1:
search_term = st.text_input("🔍 جستجوی فایل", placeholder="نام فایل را وارد کنید...")
with col2:
file_type = st.selectbox(
"📁 نوع فایل",
["همه", ".jpg", ".jpeg", ".png", ".gif", ".txt", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".mp3", ".mp4", ".zip"]
)
# نمایش لیست فایل‌ها
files = file_manager.list_files(search_term, file_type)
if not files:
st.markdown("""
<div class="info-message">
📭 هیچ فایلی یافت نشد
</div>
""", unsafe_allow_html=True)
else:
for file in files:
file_info = file_manager.get_file_info(file)
with st.container():
st.markdown(f"""
<div class="file-card">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>
<h3>{file_info['icon']} {file}</h3>
<p>اندازه: {file_info['size']} | نوع: {file_info['type']} | آخرین تغییر: {file_info['modified']}</p>
</div>
</div>
</div>
""", unsafe_allow_html=True)
col1, col2, col3, col4 = st.columns([1, 1, 1, 1])
with col1:
if st.button("👁️ نمایش", key=f"preview_{file}"):
preview_type, preview_content = file_manager.preview_file(file)
if preview_type == "image":
st.image(preview_content, use_column_width=True)
elif preview_type == "text":
st.text(preview_content)
else:
st.warning(preview_content)
with col2:
if st.button("🗑️ حذف", key=f"delete_{file}"):
if st.session_state.get('admin_logged_in', False):
status, message = file_manager.delete_file(file)
st.success(message) if status == "success" else st.error(message)
else:
st.error("⛔ فقط مدیر می‌تواند فایل‌ها را حذف کند")
with col3:
with open(file_manager.root_path / file, 'rb') as f:
st.download_button(
"⬇️ دانلود",
f.read(),
file_name=file,
key=f"download_{file}"
)
# بخش فشرده‌سازی
st.markdown("""
<div class="file-card">
<h3>🗜️ فشرده‌سازی فایل‌ها</h3>
</div>
""", unsafe_allow_html=True)
selected_files = st.multiselect(
"فایل‌های مورد نظر را انتخاب کنید",
files,
key="compress_files"
)
if st.button("📦 ساخت فایل ZIP"):
if selected_files:
status, message = file_manager.compress_files(selected_files)
if status == "success":
st.success(message)
else:
st.error(message)
else:
st.warning("⚠️ لطفاً حداقل یک فایل انتخاب کنید")
st.markdown('</div>', unsafe_allow_html=True)
if __name__ == "__main__":
main()