Skip to content

fix memory footprint#18993

Open
Taysir-25 wants to merge 1 commit into
Sylius:2.3from
Taysir-25:fix-image-uploader-stream
Open

fix memory footprint#18993
Taysir-25 wants to merge 1 commit into
Sylius:2.3from
Taysir-25:fix-image-uploader-stream

Conversation

@Taysir-25

@Taysir-25 Taysir-25 commented Apr 29, 2026

Copy link
Copy Markdown

Description

Hi!
This PR fixes the issue reported in #XXXX where images fail to upload to AWS S3 when using custom Doctrine Entity Listeners.

The problem comes from upload() using file_get_contents(). With remote Flysystem adapters like S3, loading the entire file content into write() often causes silent upload failures and high memory usage.

I updated the code to use fopen() and writeStream(). This streams the file directly to the cloud provider, which solves the S3 upload bug while remaining fully backward compatible with the local adapter.

Disclaimer

Disclaimer: this contribution is part of work carried out as a student at Université Paris-Cité. Apologies if this is inappropriate or if the contribution does not meet the project’s standards — any feedback is welcome.

Summary by CodeRabbit

  • Bug Fixes
    • Improved image upload efficiency by implementing streaming-based file handling, reducing memory consumption during large file uploads.

@Taysir-25 Taysir-25 requested review from a team as code owners April 29, 2026 10:09
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The upload() method in ImageUploader has been modified to use file streaming instead of loading entire file content into memory. It now opens a read stream from the source file, passes it to writeStream(), and properly closes the stream resource to optimize memory usage.

Changes

Cohort / File(s) Summary
ImageUploader Streaming Optimization
src/Sylius/Component/Core/Uploader/ImageUploader.php
Modified upload() method to use file streaming (fopen + writeStream) instead of loading entire file content into memory, improving memory efficiency for large file uploads.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A stream of bytes flows swift and free,
No memory bloat for you and me,
Open, write, and close with care,
Large files upload through the air! 🌊✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix memory footprint' directly and specifically addresses the main change: optimizing image upload to use streaming instead of loading entire files into memory, reducing memory usage.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Sylius/Component/Core/Uploader/ImageUploader.php`:
- Line 53: The call to $this->filesystem->writeStream($image->getPath(),
$stream) is invalid because FilesystemAdapterInterface lacks a stream-write
method; add a new method signature (e.g. writeStream(string $path, resource
$stream): void or bool) to FilesystemAdapterInterface, implement that method in
all concrete adapters including FlysystemFilesystemAdapter (and any other
classes implementing the interface), and update ImageUploader (and any other
callers) to use the new interface method; ensure the chosen method
name/signature is consistent across the interface and implementations and
preserves existing error/return handling semantics.
- Around line 52-56: The fopen call in ImageUploader (in class ImageUploader,
around the code that opens $stream and calls
$this->filesystem->writeStream($image->getPath(), $stream)) can return false and
the stream must be closed even if writeStream throws; change the logic to first
attempt $stream = fopen(...), check that $stream !== false before calling
writeStream, execute writeStream inside a try block and always close the
resource in a finally block (calling fclose only if is_resource($stream)) and
surface/handle the fopen failure appropriately (throw or return error) so no
resource leaks occur.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4afe3b70-e843-4ddb-a6b6-d23f007595d2

📥 Commits

Reviewing files that changed from the base of the PR and between 32fdbc4 and 6e1a92f.

📒 Files selected for processing (1)
  • src/Sylius/Component/Core/Uploader/ImageUploader.php

Comment on lines +52 to +56
$stream = fopen($file->getPathname(), 'r');
$this->filesystem->writeStream($image->getPath(), $stream);
if (is_resource($stream)) {
fclose($stream);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Harden stream lifecycle handling for failure paths (Lines 52-56).

fopen() can return false, and fclose() should run in a finally block so exceptions from write do not leak the resource.

Suggested patch
-        $stream = fopen($file->getPathname(), 'r');
-        $this->filesystem->writeStream($image->getPath(), $stream);
-        if (is_resource($stream)) {
-            fclose($stream);
-        }
+        $stream = fopen($file->getPathname(), 'rb');
+        if (false === $stream) {
+            throw new \RuntimeException(sprintf('Cannot open file "%s" for reading.', $file->getPathname()));
+        }
+
+        try {
+            $this->filesystem->writeStream($image->getPath(), $stream);
+        } finally {
+            fclose($stream);
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$stream = fopen($file->getPathname(), 'r');
$this->filesystem->writeStream($image->getPath(), $stream);
if (is_resource($stream)) {
fclose($stream);
}
$stream = fopen($file->getPathname(), 'rb');
if (false === $stream) {
throw new \RuntimeException(sprintf('Cannot open file "%s" for reading.', $file->getPathname()));
}
try {
$this->filesystem->writeStream($image->getPath(), $stream);
} finally {
fclose($stream);
}
🧰 Tools
🪛 GitHub Check: Static checks / PHP 8.3, Symfony ~6.4.0

[failure] 53-53:
Call to an undefined method Sylius\Component\Core\Filesystem\Adapter\FilesystemAdapterInterface::writeStream().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Sylius/Component/Core/Uploader/ImageUploader.php` around lines 52 - 56,
The fopen call in ImageUploader (in class ImageUploader, around the code that
opens $stream and calls $this->filesystem->writeStream($image->getPath(),
$stream)) can return false and the stream must be closed even if writeStream
throws; change the logic to first attempt $stream = fopen(...), check that
$stream !== false before calling writeStream, execute writeStream inside a try
block and always close the resource in a finally block (calling fclose only if
is_resource($stream)) and surface/handle the fopen failure appropriately (throw
or return error) so no resource leaks occur.


$this->filesystem->write($image->getPath(), file_get_contents($file->getPathname()));
$stream = fopen($file->getPathname(), 'r');
$this->filesystem->writeStream($image->getPath(), $stream);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

writeStream() is not part of the adapter contract (Line 53).

This call is currently invalid against FilesystemAdapterInterface, and the concrete FlysystemFilesystemAdapter shown in context also lacks writeStream(). This is a release-blocking correctness issue.

You need to extend FilesystemAdapterInterface with a stream-write method and implement it in all adapters before calling it here.

🧰 Tools
🪛 GitHub Check: Static checks / PHP 8.3, Symfony ~6.4.0

[failure] 53-53:
Call to an undefined method Sylius\Component\Core\Filesystem\Adapter\FilesystemAdapterInterface::writeStream().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Sylius/Component/Core/Uploader/ImageUploader.php` at line 53, The call to
$this->filesystem->writeStream($image->getPath(), $stream) is invalid because
FilesystemAdapterInterface lacks a stream-write method; add a new method
signature (e.g. writeStream(string $path, resource $stream): void or bool) to
FilesystemAdapterInterface, implement that method in all concrete adapters
including FlysystemFilesystemAdapter (and any other classes implementing the
interface), and update ImageUploader (and any other callers) to use the new
interface method; ensure the chosen method name/signature is consistent across
the interface and implementations and preserves existing error/return handling
semantics.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant