-
Notifications
You must be signed in to change notification settings - Fork 26
FEATURE: automatically downsize large images #1
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: image-processing-optimization
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -179,7 +179,7 @@ Discourse.Utilities = { | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // check file size | ||||||||||||||||||||||||||||||||||
| var fileSizeKB = file.size / 1024; | ||||||||||||||||||||||||||||||||||
| var maxSizeKB = Discourse.SiteSettings['max_' + type + '_size_kb']; | ||||||||||||||||||||||||||||||||||
| var maxSizeKB = 10 * 1024; // 10MB | ||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoded 10MB bypasses admin-configured limits for non-image uploads.
Suggested change
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoded 10 MB check ignores
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hard-coded 10 MB override ignores per-file-type site settings. Admins'
Suggested change
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoded 10 MB limit replaces per-type server setting. Error message shows wrong limit, breaks client-side validation for non-image types. Use
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [blocking] Hardcoded 10 MB replaces per-type site settings and breaks an existing test The original
This also breaks the existing test Restore the dynamic lookup:
Suggested change
|
||||||||||||||||||||||||||||||||||
| if (fileSizeKB > maxSizeKB) { | ||||||||||||||||||||||||||||||||||
| bootbox.alert(I18n.t('post.errors.file_too_large', { max_size_kb: maxSizeKB })); | ||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||
|
|
@@ -243,7 +243,7 @@ Discourse.Utilities = { | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // entity too large, usually returned from the web server | ||||||||||||||||||||||||||||||||||
| case 413: | ||||||||||||||||||||||||||||||||||
| var maxSizeKB = Discourse.SiteSettings.max_image_size_kb; | ||||||||||||||||||||||||||||||||||
| var maxSizeKB = 10 * 1024; // 10 MB | ||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [medium] 413 error handler shows hardcoded 10 MB instead of actual server limit The 413 (Request Entity Too Large) handler is supposed to tell the user the configured maximum. By hardcoding Suggestion: Restore
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error message shows hardcoded 10MB instead of actual admin-configured limit.
Suggested change
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error message hardcoded to 10240 KB, ignoring actual
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hard-coded error message (10240 KB) hides actual server limit, confusing users if different. Use:
Suggested change
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoded 10 MB limit replaces
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [non-blocking] 413 handler shows a hardcoded 10 MB limit A 413 (Request Entity Too Large) comes from the web server's
Suggested change
|
||||||||||||||||||||||||||||||||||
| bootbox.alert(I18n.t('post.errors.file_too_large', { max_size_kb: maxSizeKB })); | ||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -52,14 +52,23 @@ def create_upload(type, file, url) | |||||||||
| begin | ||||||||||
| # API can provide a URL | ||||||||||
| if file.nil? && url.present? && is_api? | ||||||||||
| tempfile = FileHelper.download(url, SiteSetting.max_image_size_kb.kilobytes, "discourse-upload-#{type}") rescue nil | ||||||||||
| tempfile = FileHelper.download(url, 10.megabytes, "discourse-upload-#{type}") rescue nil | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [high] URL-sourced download limit hardcoded to 10 MB, ignoring The original code used Suggestion: Keep using
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Download cap hardcoded to 10 MB ignores
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [non-blocking] Download cap hardcoded to 10 MB bypasses The original
If the intent is to allow headroom for images that will be downsized, derive the cap from the setting rather than a magic number, e.g. |
||||||||||
| filename = File.basename(URI.parse(url).path) | ||||||||||
| else | ||||||||||
| tempfile = file.tempfile | ||||||||||
| filename = file.original_filename | ||||||||||
| content_type = file.content_type | ||||||||||
| end | ||||||||||
|
|
||||||||||
| # allow users to upload large images that will be automatically reduced to allowed size | ||||||||||
| if tempfile && tempfile.size > 0 && SiteSetting.max_image_size_kb > 0 && FileHelper.is_image?(filename) | ||||||||||
| attempt = 5 | ||||||||||
| while attempt > 0 && tempfile.size > SiteSetting.max_image_size_kb.kilobytes | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Return value ignored: if all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Image re-encoding can increase file size (format overhead). Loop runs 5 attempts regardless of shrinkage, then uploads oversized file. Track previous size and break if unchanged: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [blocking] Downsize loop ignores failures and has no progress detection
Track the previous size, check the return value, and stop early: attempt = 5
prev_size = tempfile.size
while attempt > 0 && tempfile.size > SiteSetting.max_image_size_kb.kilobytes
ok = OptimizedImage.downsize(tempfile.path, tempfile.path, "80%", allow_animation: SiteSetting.allow_animated_thumbnails)
break if !ok || tempfile.size >= prev_size # downsize failed or made no progress
prev_size = tempfile.size
attempt -= 1
end
Rails.logger.warn("Failed to downsize upload below max_image_size_kb after #{5 - attempt} attempts") if tempfile.size > SiteSetting.max_image_size_kb.kilobytes |
||||||||||
| OptimizedImage.downsize(tempfile.path, tempfile.path, "80%", allow_animation: SiteSetting.allow_animated_thumbnails) | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [non-blocking] In-place downsize can corrupt the sole upload copy on a mid-write crash
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [non-blocking]
|
||||||||||
| attempt -= 1 | ||||||||||
| end | ||||||||||
| end | ||||||||||
|
|
||||||||||
| upload = Upload.create_for(current_user.id, tempfile, filename, tempfile.size, content_type: content_type, image_type: type) | ||||||||||
|
|
||||||||||
| if upload.errors.empty? && current_user.admin? | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -139,25 +139,24 @@ def self.downsize_instructions_animated(from, to, dimensions, opts={}) | |
| end | ||
|
|
||
| def self.resize(from, to, width, height, opts={}) | ||
| optimize("resize", from, to, width, height, opts) | ||
| optimize("resize", from, to, "#{width}x#{height}", opts) | ||
| end | ||
|
|
||
| def self.downsize(from, to, max_width, max_height, opts={}) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dead-code method—unreachable since line 148 redefines |
||
| optimize("downsize", from, to, max_width, max_height, opts) | ||
| optimize("downsize", from, to, "#{max_width}x#{max_height}", opts) | ||
| end | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [critical] Duplicate Ruby uses the last definition when the same method name appears twice. The diff adds a new 3-arg Suggestion: Remove the duplicate 4-arg There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two |
||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ruby method overloading: second Remove the old definition entirely; keep only the new 3-argument version. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate |
||
| def self.optimize(operation, from, to, width, height, opts={}) | ||
| dim = dimensions(width, height) | ||
| def self.downsize(from, to, dimensions, opts={}) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Method There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [blocking] Duplicate Ruby has no method overloading by arity — the second The existing caller Fix: keep a single method that accepts either form, e.g. def self.downsize(from, to, *args)
opts = args.last.is_a?(Hash) ? args.pop : {}
dims = args.size == 2 ? "#{args[0]}x#{args[1]}" : args[0]
optimize("downsize", from, to, dims, opts)
end…or update |
||
| optimize("downsize", from, to, dimensions, opts) | ||
| end | ||
|
|
||
| def self.optimize(operation, from, to, dimensions, opts={}) | ||
| method_name = "#{operation}_instructions" | ||
| method_name += "_animated" if !!opts[:allow_animation] && from =~ /\.GIF$/i | ||
| instructions = self.send(method_name.to_sym, from, to, dim, opts) | ||
| instructions = self.send(method_name.to_sym, from, to, dimensions, opts) | ||
| convert_with(instructions, to) | ||
| end | ||
|
|
||
| def self.dimensions(width, height) | ||
| "#{width}x#{height}" | ||
| end | ||
|
|
||
| def self.convert_with(instructions, to) | ||
| `#{instructions.join(" ")} &> /dev/null` | ||
| return false if $?.exitstatus != 0 | ||
|
|
||
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.
[high] Client-side size check hardcoded to 10 MB, bypassing per-type site settings
The original code used
Discourse.SiteSettings['max_' + type + '_size_kb']to enforce per-type limits (image vs. attachment). The diff replaces this with a hardcoded10 * 1024(10 MB) for all types. This means: (1) non-image file types (attachments) that have a site setting smaller than 10 MB are no longer client-side validated — users see no error until the server rejects them; (2) themaxSizeKBvalue passed to thefile_too_largei18n message will always show "10240 KB" regardless of the actual configured limit, confusing users.Suggestion: Restore the dynamic lookup:
var maxSizeKB = Discourse.SiteSettings['max_' + type + '_size_kb'];— or, if the intent is to allow large image uploads for server-side downsize, special-case only image types.