|
|
@@ -12,12 +12,114 @@ from werkzeug.utils import secure_filename
|
|
|
from flask import Flask, request, jsonify, render_template, send_from_directory, abort, make_response
|
|
|
from werkzeug.security import generate_password_hash
|
|
|
import re
|
|
|
+import zipfile
|
|
|
+import tarfile
|
|
|
+try:
|
|
|
+ import rarfile
|
|
|
+ RARFILE_AVAILABLE = True
|
|
|
+except ImportError:
|
|
|
+ RARFILE_AVAILABLE = False
|
|
|
|
|
|
def generate_secret_key(length=32):
|
|
|
"""Generate a random secret key with specified length"""
|
|
|
alphabet = string.ascii_letters + string.digits + string.punctuation
|
|
|
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
|
|
|
|
|
+def get_archive_contents(file_path, mime_type):
|
|
|
+ """Get contents of archive files (ZIP, TAR, RAR)"""
|
|
|
+ try:
|
|
|
+ # Check if it's a ZIP file
|
|
|
+ if zipfile.is_zipfile(file_path):
|
|
|
+ with zipfile.ZipFile(file_path, 'r') as zip_ref:
|
|
|
+ file_list = []
|
|
|
+ for info in zip_ref.infolist():
|
|
|
+ if not info.is_dir():
|
|
|
+ file_list.append({
|
|
|
+ 'name': info.filename,
|
|
|
+ 'size': info.file_size,
|
|
|
+ 'compressed_size': info.compress_size,
|
|
|
+ 'date_time': info.date_time,
|
|
|
+ 'type': 'file'
|
|
|
+ })
|
|
|
+ else:
|
|
|
+ file_list.append({
|
|
|
+ 'name': info.filename,
|
|
|
+ 'size': 0,
|
|
|
+ 'type': 'directory'
|
|
|
+ })
|
|
|
+ return {'type': 'zip', 'files': file_list}
|
|
|
+
|
|
|
+ # Check if it's a TAR file
|
|
|
+ elif tarfile.is_tarfile(file_path):
|
|
|
+ with tarfile.open(file_path, 'r:*') as tar_ref:
|
|
|
+ file_list = []
|
|
|
+ for member in tar_ref.getmembers():
|
|
|
+ file_list.append({
|
|
|
+ 'name': member.name,
|
|
|
+ 'size': member.size,
|
|
|
+ 'type': 'file' if member.isfile() else ('directory' if member.isdir() else 'other'),
|
|
|
+ 'mode': oct(member.mode) if member.mode else None,
|
|
|
+ 'uid': member.uid,
|
|
|
+ 'gid': member.gid,
|
|
|
+ 'mtime': member.mtime
|
|
|
+ })
|
|
|
+ return {'type': 'tar', 'files': file_list}
|
|
|
+
|
|
|
+ # Check if it's a RAR file (if rarfile is available)
|
|
|
+ elif RARFILE_AVAILABLE and mime_type and 'rar' in mime_type.lower():
|
|
|
+ try:
|
|
|
+ with rarfile.RarFile(file_path) as rar_ref:
|
|
|
+ file_list = []
|
|
|
+ for info in rar_ref.infolist():
|
|
|
+ file_list.append({
|
|
|
+ 'name': info.filename,
|
|
|
+ 'size': info.file_size,
|
|
|
+ 'compressed_size': info.compress_size,
|
|
|
+ 'date_time': info.date_time,
|
|
|
+ 'type': 'file'
|
|
|
+ })
|
|
|
+ return {'type': 'rar', 'files': file_list}
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+
|
|
|
+ return None
|
|
|
+ except Exception as e:
|
|
|
+ print(f"Error reading archive {file_path}: {e}")
|
|
|
+ return None
|
|
|
+
|
|
|
+def is_archive_file(file_path, mime_type, filename):
|
|
|
+ """Check if file is a supported archive format"""
|
|
|
+ # Check by file extension
|
|
|
+ lower_filename = filename.lower()
|
|
|
+ if any(lower_filename.endswith(ext) for ext in ['.zip', '.tar', '.tar.gz', '.tar.bz2', '.tar.xz', '.tgz']):
|
|
|
+ return True
|
|
|
+
|
|
|
+ if RARFILE_AVAILABLE and lower_filename.endswith('.rar'):
|
|
|
+ return True
|
|
|
+
|
|
|
+ # Check by MIME type
|
|
|
+ if mime_type:
|
|
|
+ archive_mimes = [
|
|
|
+ 'application/zip',
|
|
|
+ 'application/x-zip-compressed',
|
|
|
+ 'application/x-tar',
|
|
|
+ 'application/x-gtar',
|
|
|
+ 'application/x-compressed-tar',
|
|
|
+ 'application/vnd.rar',
|
|
|
+ 'application/x-rar-compressed'
|
|
|
+ ]
|
|
|
+ if any(mime in mime_type.lower() for mime in archive_mimes):
|
|
|
+ return True
|
|
|
+
|
|
|
+ # Check by actual file content
|
|
|
+ try:
|
|
|
+ if zipfile.is_zipfile(file_path) or tarfile.is_tarfile(file_path):
|
|
|
+ return True
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+
|
|
|
+ return False
|
|
|
+
|
|
|
def parse_arguments():
|
|
|
"""Parse command line arguments"""
|
|
|
parser = argparse.ArgumentParser(description='Flask Temporary File Upload Server')
|
|
|
@@ -251,6 +353,13 @@ def download_page(file_id):
|
|
|
# If can't read as text, treat as binary
|
|
|
is_text_file = False
|
|
|
|
|
|
+ # Check if it's an archive file for preview
|
|
|
+ is_archive_file_flag = is_archive_file(full_path, mime_type, original_filename)
|
|
|
+ archive_contents = None
|
|
|
+
|
|
|
+ if is_archive_file_flag:
|
|
|
+ archive_contents = get_archive_contents(full_path, mime_type)
|
|
|
+
|
|
|
return render_template('download.html',
|
|
|
file_id=file_id,
|
|
|
filename=original_filename,
|
|
|
@@ -258,7 +367,9 @@ def download_page(file_id):
|
|
|
mime_type=mime_type,
|
|
|
expiration_date=expiration_date,
|
|
|
is_text_file=is_text_file,
|
|
|
- file_content=file_content)
|
|
|
+ file_content=file_content,
|
|
|
+ is_archive_file=is_archive_file_flag,
|
|
|
+ archive_contents=archive_contents)
|
|
|
|
|
|
@app.route('/download/<file_id>/direct')
|
|
|
def download_file_direct(file_id):
|