Skip to content

Commit 436f51e

Browse files
authored
Merge pull request #2659 from carrierwaveuploader/use-converted-file-extension
[BREAKING CHANGE] Use the resulting file extension on changing format by :convert
2 parents 6c6e2dc + c4bb30b commit 436f51e

File tree

8 files changed

+435
-100
lines changed

8 files changed

+435
-100
lines changed

README.md

Lines changed: 95 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ gem 'carrierwave', '>= 3.0.0.beta', '< 4.0'
3535

3636
Finally, restart the server to apply the changes.
3737

38+
## Upgrading from 2.x or earlier
39+
40+
CarrierWave 3.0 comes with a change in the way of handling the file extension on conversion. This results in following issues if you use `process convert: :format` to change the file format:
41+
42+
- If you have it on the uploader itself (not within a version), the file extension of the cached file will change. That means if you serve both CarrierWave 2.x and 3.x simultaneously on the same workload (e.g. using blue-green deployment), a cache file stored by 2.x can't be retrieved by 3.x and vice versa.
43+
- If you have it within a version, the file extension of the stored file will change. You need to perform `#recreate_versions!` to make it usable again.
44+
45+
To preserve the 2.x behavior, you can set `force_extension false` right after calling `process convert: :format`. See [#2659](https://github.com/carrierwaveuploader/carrierwave/pull/2659) for the detail.
46+
3847
## Getting Started
3948

4049
Start off by generating an uploader:
@@ -224,6 +233,20 @@ class MyUploader < CarrierWave::Uploader::Base
224233
end
225234
```
226235

236+
## Changing the filename
237+
238+
To change the filename of uploaded files, you can override `#filename` method in the uploader.
239+
240+
```ruby
241+
class MyUploader < CarrierWave::Uploader::Base
242+
def filename
243+
"image.#{file.extension}" # If you upload 'file.jpg', you'll get 'image.jpg'
244+
end
245+
end
246+
```
247+
248+
Some old documentations (like [this](https://stackoverflow.com/a/5865117)) may instruct you to safeguard the filename value with `if original_filename`, but it's no longer necessary with CarrierWave 3.0 or later.
249+
227250
## Securing uploads
228251

229252
Certain files might be dangerous if uploaded to the wrong location, such as PHP
@@ -302,17 +325,8 @@ You no longer need to do this manually.
302325

303326
## Adding versions
304327

305-
Often you'll want to add different versions of the same file. The classic example is image thumbnails. There is built in support for this*:
306-
307-
*Note:* You must have Imagemagick installed to do image resizing.
308-
309-
Some documentation refers to RMagick instead of MiniMagick but MiniMagick is recommended.
310-
311-
To install Imagemagick on OSX with homebrew type the following:
312-
313-
```
314-
$ brew install imagemagick
315-
```
328+
Often you'll want to add different versions of the same file. The classic example is generating image thumbnails while preserving the original file to be used for high-quality representation.
329+
In this section we'll explore how CarrierWave supports working with multiple versions. The image manipulation itself is covered in [another section](#manipulating-images).
316330

317331
```ruby
318332
class MyUploader < CarrierWave::Uploader::Base
@@ -348,17 +362,7 @@ uploader.thumb.url # => '/url/to/thumb_my_file.png' # size: 200x200
348362
One important thing to remember is that process is called *before* versions are
349363
created. This can cut down on processing cost.
350364

351-
### Processing Methods: mini_magick
352-
353-
- `convert` - Changes the image encoding format to the given format, eg. jpg
354-
- `resize_to_limit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. Will only resize the image if it is larger than the specified dimensions. The resulting image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
355-
- `resize_to_fit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. The image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
356-
- `resize_to_fill` - Resize the image to fit within the specified dimensions while retaining the aspect ratio of the original image. If necessary, crop the image in the larger dimension. Optionally, a "gravity" may be specified, for example "Center", or "NorthEast".
357-
- `resize_and_pad` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. If necessary, will pad the remaining area with the given color, which defaults to transparent (for gif and png, white for jpeg). Optionally, a "gravity" may be specified, as above.
358-
359-
See `carrierwave/processing/mini_magick.rb` for details.
360-
361-
### conditional process
365+
### Conditional processing
362366

363367
If you want to use conditional process, you can only use `if` statement.
364368

@@ -442,8 +446,27 @@ class MyUploader < CarrierWave::Uploader::Base
442446
end
443447
```
444448

445-
The option `:from_version` uses the file cached in the `:thumb` version instead
446-
of the original version, potentially resulting in faster processing.
449+
### Customizing version filenames
450+
451+
CarrierWave supports [customization of filename](#changing-the-filename) by overriding an uploader's
452+
#filename method, but this doesn't work for versions because of the limitation on how CarrierWave
453+
re-constructs the filename on retrieval of the stored file.
454+
Instead, you can override `#full_filename` with providing a version-aware name.
455+
456+
```ruby
457+
class MyUploader < CarrierWave::Uploader::Base
458+
version :thumb do
459+
def full_filename(for_file)
460+
'thumb.png'
461+
end
462+
process convert: 'png'
463+
end
464+
end
465+
```
466+
467+
Please note that `#full_filename` mustn't be constructed based on a dynamic value
468+
that can change from the time of store and time of retrieval, since it will result in
469+
being unable to retrieve a file previously stored.
447470

448471
## Making uploads work across form redisplays
449472

@@ -911,67 +934,81 @@ CarrierWave.configure do |config|
911934
end
912935
```
913936

914-
## Using RMagick
937+
## Manipulating images
915938

916939
If you're uploading images, you'll probably want to manipulate them in some way,
917-
you might want to create thumbnail images for example. CarrierWave comes with a
918-
small library to make manipulating images with RMagick easier, you'll need to
919-
include it in your Uploader:
940+
you might want to create thumbnail images for example.
920941

921-
```ruby
922-
class AvatarUploader < CarrierWave::Uploader::Base
923-
include CarrierWave::RMagick
924-
end
942+
### Using MiniMagick
943+
944+
MiniMagick performs all the operations using the 'convert' CLI which is part of the standard ImageMagick kit.
945+
This allows you to have the power of ImageMagick without having to worry about installing
946+
all the RMagick libraries, it often results in higher memory footprint.
947+
948+
See the MiniMagick site for more details:
949+
950+
https://github.com/minimagick/minimagick
951+
952+
To install Imagemagick on OSX with homebrew type the following:
953+
954+
```
955+
$ brew install imagemagick
925956
```
926957

927-
The RMagick module gives you a few methods, like
928-
`CarrierWave::RMagick#resize_to_fill` which manipulate the image file in some
929-
way. You can set a `process` callback, which will call that method any time a
930-
file is uploaded.
931-
There is a demonstration of convert here.
932-
Convert will only work if the file has the same file extension, thus the use of the filename method.
958+
And the ImageMagick command line options for more for what's on offer:
959+
960+
http://www.imagemagick.org/script/command-line-options.php
961+
962+
Currently, the MiniMagick carrierwave processor provides exactly the same methods as
963+
for the RMagick processor.
933964

934965
```ruby
935966
class AvatarUploader < CarrierWave::Uploader::Base
936-
include CarrierWave::RMagick
967+
include CarrierWave::MiniMagick
937968

938969
process resize_to_fill: [200, 200]
939-
process convert: 'png'
940-
941-
def filename
942-
super.chomp(File.extname(super)) + '.png' if original_filename.present?
943-
end
944970
end
945971
```
946972

947-
Check out the manipulate! method, which makes it easy for you to write your own
948-
manipulation methods.
949-
950-
## Using MiniMagick
973+
#### List of available processing methods:
951974

952-
MiniMagick is similar to RMagick but performs all the operations using the 'convert'
953-
CLI which is part of the standard ImageMagick kit. This allows you to have the power
954-
of ImageMagick without having to worry about installing all the RMagick libraries.
975+
- `convert` - Changes the image encoding format to the given format(eg. jpg). This operation is treated specially to trigger the change of the file extension, so it matches with the format of the resulting file.
976+
- `resize_to_limit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. Will only resize the image if it is larger than the specified dimensions. The resulting image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
977+
- `resize_to_fit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. The image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
978+
- `resize_to_fill` - Resize the image to fit within the specified dimensions while retaining the aspect ratio of the original image. If necessary, crop the image in the larger dimension. Optionally, a "gravity" may be specified, for example "Center", or "NorthEast".
979+
- `resize_and_pad` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. If necessary, will pad the remaining area with the given color, which defaults to transparent (for gif and png, white for jpeg). Optionally, a "gravity" may be specified, as above.
955980

956-
See the MiniMagick site for more details:
981+
See `carrierwave/processing/mini_magick.rb` for details.
957982

958-
https://github.com/minimagick/minimagick
983+
### Using RMagick
959984

960-
And the ImageMagick command line options for more for what's on offer:
985+
CarrierWave also comes with support for RMagick, a well-known image processing library.
986+
To use it, you'll need to include this in your Uploader:
961987

962-
http://www.imagemagick.org/script/command-line-options.php
988+
```ruby
989+
class AvatarUploader < CarrierWave::Uploader::Base
990+
include CarrierWave::RMagick
991+
end
992+
```
963993

964-
Currently, the MiniMagick carrierwave processor provides exactly the same methods as
965-
for the RMagick processor.
994+
The RMagick module gives you a few methods, like
995+
`CarrierWave::RMagick#resize_to_fill` which manipulate the image file in some
996+
way. You can set a `process` callback, which will call that method any time a
997+
file is uploaded.
998+
There is a demonstration of convert here.
966999

9671000
```ruby
9681001
class AvatarUploader < CarrierWave::Uploader::Base
969-
include CarrierWave::MiniMagick
1002+
include CarrierWave::RMagick
9701003

9711004
process resize_to_fill: [200, 200]
1005+
process convert: 'png'
9721006
end
9731007
```
9741008

1009+
Check out the manipulate! method, which makes it easy for you to write your own
1010+
manipulation methods.
1011+
9751012
## Migrating from Paperclip
9761013

9771014
If you are using Paperclip, you can use the provided compatibility module:

lib/carrierwave/uploader/cache.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def sanitized_file
103103
# [String] a cache name, in the format TIMEINT-PID-COUNTER-RND/filename.txt
104104
#
105105
def cache_name
106-
File.join(cache_id, full_original_filename) if cache_id && original_filename
106+
File.join(cache_id, original_filename) if cache_id && original_filename
107107
end
108108

109109
##
@@ -166,7 +166,7 @@ def retrieve_from_cache!(cache_name)
166166
self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
167167
@staged = true
168168
@filename = original_filename
169-
@file = cache_storage.retrieve_from_cache!(full_filename(original_filename))
169+
@file = cache_storage.retrieve_from_cache!(full_original_filename)
170170
end
171171
end
172172

@@ -181,7 +181,7 @@ def retrieve_from_cache!(cache_name)
181181
#
182182
# [String] the cache path
183183
#
184-
def cache_path(for_file=full_filename(original_filename))
184+
def cache_path(for_file=full_original_filename)
185185
File.join(*[cache_dir, @cache_id, for_file].compact)
186186
end
187187

@@ -197,9 +197,6 @@ def workfile_path(for_file=original_filename)
197197

198198
attr_reader :original_filename
199199

200-
# We can override the full_original_filename method in other modules
201-
alias_method :full_original_filename, :original_filename
202-
203200
def cache_id=(cache_id)
204201
# Earlier version used 3 part cache_id. Thus we should allow for
205202
# the cache_id to have both 3 part and 4 part formats.
@@ -215,6 +212,11 @@ def original_filename=(filename)
215212
def cache_storage
216213
@cache_storage ||= (self.class.cache_storage || self.class.storage).new(self)
217214
end
215+
216+
# We can override the full_original_filename method in other modules
217+
def full_original_filename
218+
forcing_extension(original_filename)
219+
end
218220
end # Cache
219221
end # Uploader
220222
end # CarrierWave

lib/carrierwave/uploader/configuration.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ module Configuration
2424
add_config :move_to_store
2525
add_config :remove_previously_stored_files_after_update
2626
add_config :downloader
27+
add_config :force_extension
2728

2829
# fog
2930
add_deprecated_config :fog_provider
@@ -202,6 +203,7 @@ def reset_config
202203
config.move_to_store = false
203204
config.remove_previously_stored_files_after_update = true
204205
config.downloader = CarrierWave::Downloader::Base
206+
config.force_extension = false
205207
config.ignore_integrity_errors = true
206208
config.ignore_processing_errors = true
207209
config.ignore_download_errors = true

lib/carrierwave/uploader/processing.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ def process(*args)
6666
condition = new_processors.delete(:if) || new_processors.delete(:unless)
6767
new_processors.each do |processor, processor_args|
6868
self.processors += [[processor, processor_args, condition, condition_type]]
69+
70+
if processor == :convert
71+
# Treat :convert specially, since it should trigger the file extension change
72+
force_extension processor_args
73+
end
6974
end
7075
end
7176
end # ClassMethods
@@ -106,6 +111,15 @@ def process!(new_file=nil)
106111
end
107112
end
108113

114+
private
115+
116+
def forcing_extension(filename)
117+
if force_extension && filename
118+
Pathname.new(filename).sub_ext(".#{force_extension.to_s.delete_prefix('.')}").to_s
119+
else
120+
filename
121+
end
122+
end
109123
end # Processing
110124
end # Uploader
111125
end # CarrierWave

lib/carrierwave/uploader/store.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def retrieve_from_store!(identifier)
9292
private
9393

9494
def full_filename(for_file)
95-
for_file
95+
forcing_extension(for_file)
9696
end
9797

9898
def storage

spec/processing/rmagick_spec.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,4 +348,39 @@ def read
348348
end
349349
end
350350
end
351+
352+
describe "when working with frames" do
353+
before do
354+
def instance.cover
355+
manipulate! { |frame, index| frame if index.zero? }
356+
end
357+
358+
klass.send :include, CarrierWave::RMagick
359+
end
360+
361+
after { instance.instance_eval { undef cover } }
362+
363+
context "with a multi-page PDF" do
364+
before { instance.cache! File.open(file_path("multi_page.pdf")) }
365+
366+
it "successfully processes" do
367+
klass.process :convert => 'jpg'
368+
instance.process!
369+
end
370+
371+
it "supports page specific transformations" do
372+
klass.process :cover
373+
instance.process!
374+
end
375+
end
376+
377+
context "with a simple image" do
378+
before { instance.cache! File.open(file_path("portrait.jpg")) }
379+
380+
it "allows page specific transformations" do
381+
klass.process :cover
382+
instance.process!
383+
end
384+
end
385+
end
351386
end

0 commit comments

Comments
 (0)