Spaces:
Sleeping
Sleeping
| 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() | |