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()