Skip to content

Conversation

@RohanExploit
Copy link
Owner

@RohanExploit RohanExploit commented Feb 7, 2026

💡 What: Optimized process_uploaded_image_sync and _validate_uploaded_file_sync in backend/utils.py. Replaced the inefficient Image.new() + paste() method for stripping EXIF with img.info.clear(), and implemented logic to preserve image format (or default to PNG for RGBA) during resizing.
🎯 Why: The previous method for stripping EXIF allocated a new image and copied all pixels, doubling memory usage and CPU time for this step. Additionally, resizing RGBA images (PNGs) caused a crash because img.format was lost and defaulted to 'JPEG', which doesn't support RGBA.
📊 Impact: Reduces memory usage by ~50% during the EXIF stripping step for large images. Prevents 500 errors when users upload large transparent PNGs.
🔬 Measurement: Verified with tests/test_image_optimization.py (temporary test) which confirmed correct EXIF stripping without copy and successful resizing of large RGBA PNGs.


PR created automatically by Jules for task 6671053904282164411 started by @RohanExploit

Summary by CodeRabbit

Bug Fixes

  • Images now maintain their original format when processed and resized during upload.
  • Optimized image processing efficiency through improved handling of image metadata.
  • Enhanced consistency in image output format handling across all processing workflows.

- Replaced inefficient `Image.new()` + `paste()` with `img.info.clear()` to strip EXIF data without memory allocation.
- Fixed a bug where resizing RGBA images caused a crash due to forcing JPEG format.
- Added logic to preserve original image format or intelligently default based on image mode.
- Applied changes to both `process_uploaded_image_sync` and `_validate_uploaded_file_sync`.

Co-authored-by: RohanExploit <178623867+RohanExploit@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 7, 2026 13:50
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@netlify
Copy link

netlify bot commented Feb 7, 2026

Deploy Preview for fixmybharat canceled.

Name Link
🔨 Latest commit 308df65
🔍 Latest deploy log https://app.netlify.com/projects/fixmybharat/deploys/6987433e935c040007867091

@github-actions
Copy link

github-actions bot commented Feb 7, 2026

🙏 Thank you for your contribution, @RohanExploit!

PR Details:

Quality Checklist:
Please ensure your PR meets the following criteria:

  • Code follows the project's style guidelines
  • Self-review of code completed
  • Code is commented where necessary
  • Documentation updated (if applicable)
  • No new warnings generated
  • Tests added/updated (if applicable)
  • All tests passing locally
  • No breaking changes to existing functionality

Review Process:

  1. Automated checks will run on your code
  2. A maintainer will review your changes
  3. Address any requested changes promptly
  4. Once approved, your PR will be merged! 🎉

Note: The maintainers will monitor code quality and ensure the overall project flow isn't broken.

@coderabbitai
Copy link

coderabbitai bot commented Feb 7, 2026

📝 Walkthrough

Walkthrough

The PR modifies image processing logic in backend/utils.py to preserve original image formats during resize operations, improve EXIF-stripping efficiency by clearing metadata dictionaries instead of creating copies, and apply consistent format handling across both synchronous and asynchronous image processing paths.

Changes

Cohort / File(s) Summary
Image Format Preservation
backend/utils.py
Captures original image format at start of processing; computes save_format (PNG for RGBA images, else JPEG) and applies it during save operations in resize, validation, and processing paths. Replaces inefficient image copy-and-paste EXIF-stripping with direct info dictionary clearing for memory efficiency.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

size/xs

Poem

🐰 A rabbit hops through pixels bright,
Preserving formats, left and right,
EXIF stripped with care so fine,
No copy-paste—just clean design! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: optimizing image processing and fixing an RGBA resize crash, which are the core objectives of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bolt/optimize-image-processing-6671053904282164411

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the backend image upload pipeline to reduce memory/CPU overhead during metadata stripping and to prevent crashes when resizing RGBA (transparent) images by preserving the original image format for re-encoding.

Changes:

  • Preserve the original PIL img.format before resize operations and use it to choose a safe save format (PNG for RGBA when needed).
  • Replace the EXIF-stripping “new image + paste” approach with clearing image metadata via img.info.clear().
  • Adjust save logic to avoid defaulting to JPEG when processing RGBA images.
Comments suppressed due to low confidence (1)

backend/utils.py:115

  • file.size is set from output.tell() immediately after output.seek(0), which will always set the size to 0. If any later logic relies on UploadFile.size, this breaks size checks/limits. Compute the size before rewinding the buffer (or use len(output.getbuffer())) and then seek back to 0.
                img.save(output, format=save_format, quality=85)
                output.seek(0)

                # Replace file content
                file.file = output
                file.size = output.tell()
                output.seek(0)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 185 to +196
# Strip EXIF
img_no_exif = Image.new(img.mode, img.size)
img_no_exif.paste(img)
# Optimization: Clear info dictionary instead of creating new image and pasting (avoids full copy)
if hasattr(img, 'info'):
img.info.clear()

# Save to BytesIO
output = io.BytesIO()
# Preserve format or default to JPEG
fmt = img.format or 'JPEG'
img_no_exif.save(output, format=fmt, quality=85)

# Preserve format or default to JPEG, handling RGBA edge case
save_format = original_format or ('PNG' if img.mode == 'RGBA' else 'JPEG')

img.save(output, format=save_format, quality=85)
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description mentions verifying changes with tests/test_image_optimization.py, but that test file isn’t included in this PR. Since the EXIF-stripping and format-preserving resize behavior is security/behaviorally significant, please add/keep automated tests that cover (1) large RGBA PNG resize path and (2) metadata/EXIF removal, so regressions are caught.

Copilot uses AI. Check for mistakes.

img.save(output, format=save_format, quality=85)
output.seek(0)

Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After resizing, img is a new PIL object whose format is typically None. Even though you preserve original_format for saving back into file.file, you still return the resized img with img.format=None, which can later cause downstream code (e.g., helpers that default to JPEG when image.format is missing) to attempt JPEG encoding and crash again for RGBA images. Consider restoring img.format (and/or returning a reopened image from the resized bytes) so callers receive an image with a correct format.

Suggested change
# Ensure the returned image reports the correct format
img.format = save_format

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/utils.py (1)

110-115: ⚠️ Potential issue | 🟡 Minor

Pre-existing bug: file.size is set to 0 after resize.

Not introduced by this PR, but since this code directly follows the new save logic: after output.seek(0) on line 110, output.tell() on line 114 returns 0. The intent was likely to capture the new file size. Also, line 115 is a redundant seek.

Suggested fix
-                output.seek(0)
-
                 # Replace file content
                 file.file = output
-                file.size = output.tell()
-                output.seek(0)
+                file.size = output.getbuffer().nbytes
+                output.seek(0)
🧹 Nitpick comments (1)
backend/utils.py (1)

262-269: Inconsistent EXIF-stripping strategy: save_file_blocking still uses Image.new() + paste().

process_uploaded_image_sync was updated to use img.info.clear() for efficiency, but this function retains the old Image.new() + paste() approach. If the memory optimization matters here too, consider aligning the strategy. Otherwise, a comment explaining the intentional difference would help future readers.

The format fallback on line 268 is correctly applied — consistent with the other paths.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant