-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Code Quality: Use temporary files to extract zip entries #17461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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 addresses a critical memory issue in zip file operations by replacing in-memory extraction with temporary file-based extraction. The change prevents OutOfMemoryException when working with large zip archives by avoiding loading entire files into RAM.
- Replaces MemoryStream usage with temporary file streams for zip entry extraction
- Implements automatic cleanup of temporary files using FileOptions.DeleteOnClose and explicit cleanup in error scenarios
- Applies the fix consistently across all zip operation methods (OpenAsync, OpenReadAsync, OpenSequentialReadAsync, CopyAsync, RenameAsync, DeleteAsync)
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
{ | ||
DisposeCallback = () => | ||
{ | ||
fileStream.Dispose(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling fileStream.Dispose() in the DisposeCallback is redundant and potentially harmful. The NonSeekableRandomAccessStreamForRead should handle disposal of its underlying stream, and calling Dispose() twice could cause ObjectDisposedException.
fileStream.Dispose(); |
Copilot uses AI. Check for mistakes.
{ | ||
DisposeCallback = () => | ||
{ | ||
fileStream.Dispose(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling fileStream.Dispose() in the DisposeCallback is redundant and potentially harmful. The NonSeekableRandomAccessStreamForRead should handle disposal of its underlying stream, and calling Dispose() twice could cause ObjectDisposedException.
fileStream.Dispose(); |
Copilot uses AI. Check for mistakes.
{ | ||
DisposeCallback = () => | ||
{ | ||
fileStream.Dispose(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling fileStream.Dispose() in the DisposeCallback is redundant and potentially harmful. The NonSeekableRandomAccessStreamForRead should handle disposal of its underlying stream, and calling Dispose() twice could cause ObjectDisposedException.
fileStream.Dispose(); |
Copilot uses AI. Check for mistakes.
await zipFile.ExtractFileAsync(entry.Index, tempStream); | ||
} | ||
|
||
using var fileStream = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.DeleteOnClose); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using FileOptions.DeleteOnClose with a 'using' statement creates a race condition. The file will be deleted when the FileStream is disposed at the end of the using block, but the NonSeekableRandomAccessStreamForRead may still need access to it after this method returns.
Copilot uses AI. Check for mistakes.
catch | ||
{ | ||
// Clean up temp file if extraction failed | ||
try { IO.File.Delete(tempFile); } catch { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The empty catch block silently swallows all exceptions when trying to delete the temporary file. Consider logging the exception or at least catching specific exceptions like FileNotFoundException or UnauthorizedAccessException to avoid hiding unexpected errors.
try { IO.File.Delete(tempFile); } catch { } | |
try { IO.File.Delete(tempFile); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Failed to delete temp file '{tempFile}': {ex}"); } |
Copilot uses AI. Check for mistakes.
Resolved / Related Issues
To prevent extra work, all changes to the Files codebase must link to an approved issue marked as
Ready to build
. Please insert the issue number following the hashtag with the issue number that this Pull Request resolves.Steps used to test these changes
Stability is a top priority for Files and all changes are required to go through testing before being merged into the repo. Please include a list of steps that you used to test this PR.
Fixes FILES-APP-1Z5. The issue was that: Zip file operations load entire files/archives into memory, exhausting RAM and causing OutOfMemoryException.
FileOptions.DeleteOnClose
to automatically delete the temporary file when the stream is closed.This fix was generated by Seer in Sentry, triggered by Yair. 👁️ Run ID: 852921
Not quite right? Click here to continue debugging with Seer.