pr28416 commited on
Commit
c814783
Β·
1 Parent(s): 4ecd191

Add lossless compression support for large file uploads

Browse files
Files changed (2) hide show
  1. requirements.txt +2 -0
  2. streamlit_app.py +110 -14
requirements.txt CHANGED
@@ -8,3 +8,5 @@ Pillow>=9.5.0
8
  imageio>=2.28.0
9
  plotly>=5.14.0
10
  streamlit-cropper>=0.2.1
 
 
 
8
  imageio>=2.28.0
9
  plotly>=5.14.0
10
  streamlit-cropper>=0.2.1
11
+ lz4>=4.3.0
12
+ zstandard>=0.21.0
streamlit_app.py CHANGED
@@ -1,6 +1,11 @@
1
  import os
2
  import time
3
  import tempfile
 
 
 
 
 
4
 
5
  # Set environment variables for large uploads before importing streamlit
6
  os.environ["STREAMLIT_SERVER_MAX_UPLOAD_SIZE"] = "1024"
@@ -22,6 +27,69 @@ from PIL import Image # type: ignore
22
 
23
  from main import inspect_and_preview, _count_dots_on_preview
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  # Force configure upload limits using Streamlit's internal config
26
  try:
27
  from streamlit import config # type: ignore
@@ -56,9 +124,9 @@ except:
56
  # Upload first with better error handling
57
  st.markdown("### πŸ“ File Upload")
58
  uploaded = st.file_uploader(
59
- "Upload .tif/.tiff image",
60
- type=["tif", "tiff"],
61
- help="Large files (>500MB) may take several minutes to upload. Please be patient.",
62
  )
63
 
64
  # Show alternative for very large files
@@ -78,25 +146,53 @@ with col2:
78
  with col1:
79
  pass # File uploader is above
80
 
81
- # Show upload progress for large files
82
  if uploaded is not None:
83
  try:
84
  file_size_mb = len(uploaded.getvalue()) / (1024 * 1024)
85
  st.success(f"βœ… Upload successful! File size: {file_size_mb:.1f}MB")
86
-
87
- if file_size_mb > 200:
88
- st.info(
89
- f"πŸ“ Large file detected ({file_size_mb:.1f}MB). Processing may take a few minutes..."
90
- )
91
- elif file_size_mb > 500:
92
- st.warning(
93
- f"⚠️ Very large file ({file_size_mb:.1f}MB). Upload and processing will take time. Please keep the browser tab open."
94
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  except Exception as e:
96
- st.error(f"❌ Upload error: {str(e)}")
97
  st.error("Please try a smaller file or refresh the page and try again.")
98
  uploaded = None
99
 
 
 
 
 
 
100
 
101
  # Helper to render settings panel next to slice preview
102
  def render_settings_panel():
 
1
  import os
2
  import time
3
  import tempfile
4
+ import zipfile
5
+ import io
6
+ import gzip
7
+ import lz4.frame
8
+ import zstandard as zstd
9
 
10
  # Set environment variables for large uploads before importing streamlit
11
  os.environ["STREAMLIT_SERVER_MAX_UPLOAD_SIZE"] = "1024"
 
27
 
28
  from main import inspect_and_preview, _count_dots_on_preview
29
 
30
+ # Compression utilities
31
+ def decompress_file(compressed_data, compression_type):
32
+ """Decompress uploaded file data"""
33
+ try:
34
+ if compression_type == "zip":
35
+ with zipfile.ZipFile(io.BytesIO(compressed_data), 'r') as zip_ref:
36
+ # Get the first .tif/.tiff file in the archive
37
+ for filename in zip_ref.namelist():
38
+ if filename.lower().endswith(('.tif', '.tiff')):
39
+ return zip_ref.read(filename), filename
40
+ raise ValueError("No TIFF files found in ZIP archive")
41
+
42
+ elif compression_type == "gzip":
43
+ return gzip.decompress(compressed_data), "decompressed.tif"
44
+
45
+ elif compression_type == "lz4":
46
+ return lz4.frame.decompress(compressed_data), "decompressed.tif"
47
+
48
+ elif compression_type == "zstd":
49
+ dctx = zstd.ZstdDecompressor()
50
+ return dctx.decompress(compressed_data), "decompressed.tif"
51
+
52
+ else:
53
+ return compressed_data, "uploaded.tif"
54
+
55
+ except Exception as e:
56
+ st.error(f"Failed to decompress file: {str(e)}")
57
+ return None, None
58
+
59
+ def detect_compression_type(filename):
60
+ """Detect compression type from file extension"""
61
+ filename_lower = filename.lower()
62
+ if filename_lower.endswith('.zip'):
63
+ return "zip"
64
+ elif filename_lower.endswith('.gz'):
65
+ return "gzip"
66
+ elif filename_lower.endswith('.lz4'):
67
+ return "lz4"
68
+ elif filename_lower.endswith('.zst'):
69
+ return "zstd"
70
+ else:
71
+ return None
72
+
73
+ def show_compression_guide():
74
+ """Display compression guidance for users"""
75
+ st.markdown("""
76
+ ### πŸ’Ύ **File Size Too Large?**
77
+
78
+ For files >200MB, try compressing first:
79
+
80
+ **Recommended compression tools:**
81
+ - **ZIP**: Built into most operating systems
82
+ - **7-Zip**: Free, excellent compression ratios
83
+ - **WinRAR/WinZip**: Popular on Windows
84
+
85
+ **Expected compression ratios for TIFF files:**
86
+ - ZIP: 20-40% reduction
87
+ - 7-Zip: 30-50% reduction
88
+ - LZ4: 15-25% reduction (fastest)
89
+
90
+ **Or use our local version** for unlimited file sizes!
91
+ """)
92
+
93
  # Force configure upload limits using Streamlit's internal config
94
  try:
95
  from streamlit import config # type: ignore
 
124
  # Upload first with better error handling
125
  st.markdown("### πŸ“ File Upload")
126
  uploaded = st.file_uploader(
127
+ "Upload .tif/.tiff image (or compressed .zip/.gz/.lz4/.zst)",
128
+ type=["tif", "tiff", "zip", "gz", "lz4", "zst"],
129
+ help="πŸ’‘ Tip: Compress large files to reduce upload time! ZIP, GZ, LZ4, and ZSTD formats supported.",
130
  )
131
 
132
  # Show alternative for very large files
 
146
  with col1:
147
  pass # File uploader is above
148
 
149
+ # Show upload progress and handle compression
150
  if uploaded is not None:
151
  try:
152
  file_size_mb = len(uploaded.getvalue()) / (1024 * 1024)
153
  st.success(f"βœ… Upload successful! File size: {file_size_mb:.1f}MB")
154
+
155
+ # Detect and handle compression
156
+ compression_type = detect_compression_type(uploaded.name)
157
+
158
+ if compression_type:
159
+ st.info(f"πŸ—œοΈ Compressed file detected ({compression_type.upper()}). Decompressing...")
160
+
161
+ # Decompress the file
162
+ decompressed_data, original_filename = decompress_file(uploaded.getvalue(), compression_type)
163
+
164
+ if decompressed_data is not None:
165
+ # Calculate compression ratio
166
+ original_size_mb = len(decompressed_data) / (1024 * 1024)
167
+ compression_ratio = (1 - file_size_mb / original_size_mb) * 100
168
+
169
+ st.success(f"βœ… Decompressed successfully! Original size: {original_size_mb:.1f}MB (saved {compression_ratio:.1f}%)")
170
+
171
+ # Create a new uploaded file object with decompressed data
172
+ uploaded = type('MockUpload', (), {
173
+ 'getvalue': lambda: decompressed_data,
174
+ 'name': original_filename
175
+ })()
176
+
177
+ file_size_mb = original_size_mb
178
+ else:
179
+ uploaded = None
180
+
181
+ if uploaded and file_size_mb > 200:
182
+ st.info(f"πŸ“ Large file detected ({file_size_mb:.1f}MB). Processing may take a few minutes...")
183
+ elif uploaded and file_size_mb > 500:
184
+ st.warning(f"⚠️ Very large file ({file_size_mb:.1f}MB). Processing will take time. Please keep the browser tab open.")
185
+
186
  except Exception as e:
187
+ st.error(f"❌ Upload/processing error: {str(e)}")
188
  st.error("Please try a smaller file or refresh the page and try again.")
189
  uploaded = None
190
 
191
+ # Show compression guide if upload failed or file is too large
192
+ if uploaded is None or (uploaded and len(uploaded.getvalue()) / (1024 * 1024) > 200):
193
+ with st.expander("πŸ’Ύ Need to compress your file?"):
194
+ show_compression_guide()
195
+
196
 
197
  # Helper to render settings panel next to slice preview
198
  def render_settings_panel():