uploadcolumn 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/CHANGELOG +123 -0
- data/LICENSE +22 -0
- data/README.rdoc +206 -0
- data/Rakefile +90 -0
- data/VERSION +1 -0
- data/init.rb +15 -0
- data/lib/upload_column/active_record_extension.rb +154 -0
- data/lib/upload_column/configuration.rb +49 -0
- data/lib/upload_column/magic_columns.rb +50 -0
- data/lib/upload_column/manipulators/image_science.rb +86 -0
- data/lib/upload_column/manipulators/rmagick.rb +75 -0
- data/lib/upload_column/rails/action_controller_extension.rb +61 -0
- data/lib/upload_column/rails/asset_tag_extension.rb +17 -0
- data/lib/upload_column/rails/upload_column_helper.rb +45 -0
- data/lib/upload_column/sanitized_file.rb +176 -0
- data/lib/upload_column/uploaded_file.rb +299 -0
- data/lib/upload_column.rb +12 -0
- data/spec/active_record_extension_spec.rb +514 -0
- data/spec/custom_matchers.rb +148 -0
- data/spec/fixtures/animated.gif +0 -0
- data/spec/fixtures/animated_solarized.gif +0 -0
- data/spec/fixtures/invalid-image.jpg +1 -0
- data/spec/fixtures/kerb.jpg +0 -0
- data/spec/fixtures/kerb_solarized.jpg +0 -0
- data/spec/fixtures/netscape.gif +0 -0
- data/spec/fixtures/skanthak.png +0 -0
- data/spec/image_science_manipulator_spec.rb +195 -0
- data/spec/integration_spec.rb +668 -0
- data/spec/magic_columns_spec.rb +120 -0
- data/spec/rmagick_manipulator_spec.rb +186 -0
- data/spec/sanitized_file_spec.rb +496 -0
- data/spec/spec_helper.rb +90 -0
- data/spec/upload_column_spec.rb +65 -0
- data/spec/uploaded_file_spec.rb +1053 -0
- metadata +108 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module UploadColumn
|
2
|
+
module MagicColumns
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
super
|
6
|
+
base.send :alias_method_chain, :set_upload_column, :magic_columns
|
7
|
+
base.send :alias_method_chain, :set_upload_column_temp, :magic_columns
|
8
|
+
base.send :alias_method_chain, :save_uploaded_files, :magic_columns
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_upload_column_with_magic_columns(name, file)
|
12
|
+
set_upload_column_without_magic_columns(name, file)
|
13
|
+
evaluate_magic_columns_for_upload_column(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_upload_column_temp_with_magic_columns(name, path)
|
17
|
+
set_upload_column_temp_without_magic_columns(name, path)
|
18
|
+
evaluate_magic_columns_for_upload_column(name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def save_uploaded_files_with_magic_columns
|
22
|
+
save_uploaded_files_without_magic_columns
|
23
|
+
self.class.reflect_on_upload_columns.each do |name, column|
|
24
|
+
evaluate_magic_columns_for_upload_column(name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def evaluate_magic_columns_for_upload_column(name)
|
31
|
+
|
32
|
+
self.class.column_names.each do |column_name|
|
33
|
+
|
34
|
+
statement, predicate = column_name.split('_', 2)
|
35
|
+
|
36
|
+
if statement and predicate and name.to_s == statement and not self.read_attribute(column_name.to_sym)
|
37
|
+
uploaded_file = self.send(:get_upload_column, name.to_sym)
|
38
|
+
|
39
|
+
self.write_attribute(column_name.to_sym, handle_predicate(uploaded_file, predicate))
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def handle_predicate(uploaded_file, predicate)
|
46
|
+
return uploaded_file.send(predicate.to_sym) if uploaded_file.respond_to?(predicate.to_sym)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module UploadColumn
|
2
|
+
module Manipulators
|
3
|
+
|
4
|
+
module ImageScience
|
5
|
+
|
6
|
+
attr_reader :width, :height
|
7
|
+
|
8
|
+
def load_manipulator_dependencies #:nodoc:
|
9
|
+
require 'image_science'
|
10
|
+
end
|
11
|
+
|
12
|
+
def process!(instruction)
|
13
|
+
if instruction.to_s =~ /^c(\d+x\d+)/
|
14
|
+
crop_resized!($1)
|
15
|
+
elsif instruction.to_s =~ /\d+x\d+/
|
16
|
+
resize!(instruction)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Resize the image so that it will not exceed the dimensions passed
|
21
|
+
# via geometry, geometry should be a string, formatted like '200x100' where
|
22
|
+
# the first number is the height and the second is the width
|
23
|
+
def resize!( geometry )
|
24
|
+
::ImageScience.with_image(self.path) do |img|
|
25
|
+
width, height = extract_dimensions(img.width, img.height, geometry)
|
26
|
+
img.resize( width, height ) do |file|
|
27
|
+
file.save( self.path )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Resize and crop the image so that it will have the exact dimensions passed
|
33
|
+
# via geometry, geometry should be a string, formatted like '200x100' where
|
34
|
+
# the first number is the height and the second is the width
|
35
|
+
def crop_resized!( geometry )
|
36
|
+
::ImageScience.with_image(self.path) do |img|
|
37
|
+
new_width, new_height = geometry.split('x').map{|i| i.to_i }
|
38
|
+
|
39
|
+
width, height = extract_dimensions_for_crop(img.width, img.height, geometry)
|
40
|
+
x_offset, y_offset = extract_placement_for_crop(width, height, geometry)
|
41
|
+
|
42
|
+
img.resize( width, height ) do |i2|
|
43
|
+
|
44
|
+
i2.with_crop( x_offset, y_offset, new_width + x_offset, new_height + y_offset) do |file|
|
45
|
+
file.save( self.path )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def extract_dimensions(width, height, new_geometry, type = :resize)
|
54
|
+
new_width, new_height = convert_geometry(new_geometry)
|
55
|
+
|
56
|
+
aspect_ratio = width.to_f / height.to_f
|
57
|
+
new_aspect_ratio = new_width / new_height
|
58
|
+
|
59
|
+
if (new_aspect_ratio > aspect_ratio) ^ ( type == :crop ) # Image is too wide, the caret is the XOR operator
|
60
|
+
new_width, new_height = [ (new_height * aspect_ratio), new_height]
|
61
|
+
else #Image is too narrow
|
62
|
+
new_width, new_height = [ new_width, (new_width / aspect_ratio)]
|
63
|
+
end
|
64
|
+
|
65
|
+
[new_width, new_height].collect! { |v| v.round }
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_dimensions_for_crop(width, height, new_geometry)
|
69
|
+
extract_dimensions(width, height, new_geometry, :crop)
|
70
|
+
end
|
71
|
+
|
72
|
+
def extract_placement_for_crop(width, height, new_geometry)
|
73
|
+
new_width, new_height = convert_geometry(new_geometry)
|
74
|
+
x_offset = (width / 2.0) - (new_width / 2.0)
|
75
|
+
y_offset = (height / 2.0) - (new_height / 2.0)
|
76
|
+
[x_offset, y_offset].collect! { |v| v.round }
|
77
|
+
end
|
78
|
+
|
79
|
+
def convert_geometry(geometry)
|
80
|
+
geometry.split('x').map{|i| i.to_f }
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module UploadColumn
|
2
|
+
|
3
|
+
UploadError = Class.new(StandardError) unless defined?(UploadError)
|
4
|
+
ManipulationError = Class.new(UploadError) unless defined?(ManipulationError)
|
5
|
+
|
6
|
+
module Manipulators
|
7
|
+
|
8
|
+
module RMagick
|
9
|
+
|
10
|
+
def load_manipulator_dependencies #:nodoc:
|
11
|
+
require 'RMagick'
|
12
|
+
end
|
13
|
+
|
14
|
+
def process!(instruction = nil, &block)
|
15
|
+
if instruction.is_a?(Proc)
|
16
|
+
manipulate!(&instruction)
|
17
|
+
elsif instruction.to_s =~ /^c(\d+x\d+)$/
|
18
|
+
crop_resized!($1)
|
19
|
+
elsif instruction.to_s =~ /^(\d+x\d+)$/
|
20
|
+
resize!($1)
|
21
|
+
end
|
22
|
+
manipulate!(&block) if block
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convert the image to format
|
26
|
+
def convert!(format)
|
27
|
+
manipulate! do |img|
|
28
|
+
img.format = format.to_s.upcase
|
29
|
+
img
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Resize the image so that it will not exceed the dimensions passed
|
34
|
+
# via geometry, geometry should be a string, formatted like '200x100' where
|
35
|
+
# the first number is the height and the second is the width
|
36
|
+
def resize!( geometry )
|
37
|
+
manipulate! do |img|
|
38
|
+
img.change_geometry( geometry ) do |c, r, i|
|
39
|
+
i.resize(c,r)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Resize and crop the image so that it will have the exact dimensions passed
|
45
|
+
# via geometry, geometry should be a string, formatted like '200x100' where
|
46
|
+
# the first number is the height and the second is the width
|
47
|
+
def crop_resized!( geometry )
|
48
|
+
manipulate! do |img|
|
49
|
+
h, w = geometry.split('x')
|
50
|
+
img.crop_resized(h.to_i,w.to_i)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def manipulate!
|
55
|
+
image = ::Magick::Image.read(self.path)
|
56
|
+
|
57
|
+
if image.size > 1
|
58
|
+
list = ::Magick::ImageList.new
|
59
|
+
image.each do |frame|
|
60
|
+
list << yield( frame )
|
61
|
+
end
|
62
|
+
list.write(self.path)
|
63
|
+
else
|
64
|
+
yield( image.first ).write(self.path)
|
65
|
+
end
|
66
|
+
rescue ::Magick::ImageMagickError => e
|
67
|
+
# this is a more meaningful error message, which we could catch later
|
68
|
+
raise ManipulationError.new("Failed to manipulate with rmagick, maybe it is not an image? Original Error: #{e}")
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module UploadColumn::ActionControllerExtension
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.send :alias_method_chain, :url_for, :uploaded_file_check
|
5
|
+
base.helper_method :url_for_path
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
def url_for_with_uploaded_file_check(options = {}, *parameters_for_method_reference)
|
11
|
+
if(options.respond_to?(:public_path))
|
12
|
+
options.public_path
|
13
|
+
else
|
14
|
+
url_for_without_uploaded_file_check(options || {}, *parameters_for_method_reference)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def url_for_path(path)
|
19
|
+
request.protocol + request.host_with_port + path
|
20
|
+
end
|
21
|
+
|
22
|
+
# You can use +render_image+ in your controllers to render an image
|
23
|
+
# def picture
|
24
|
+
# @user = User.find(params[:id])
|
25
|
+
# render_image @user.picture
|
26
|
+
# end
|
27
|
+
# This of course, is not very useful at all (you could simply have linked to the image itself),
|
28
|
+
# However it is even possible to pass a block to render_image that allows manipulation using
|
29
|
+
# RMagick, here the fun begins:
|
30
|
+
# def solarize_picture
|
31
|
+
# @user = User.find(params[:id])
|
32
|
+
# render_image @user.picture do |img|
|
33
|
+
# img = img.segment
|
34
|
+
# img.solarize
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
# Note that like in UploadColumn::BaseUploadedFile.process you will need to 'carry' the image
|
38
|
+
# since most Rmagick methods do not modify the image itself but rather return the result of the
|
39
|
+
# transformation.
|
40
|
+
#
|
41
|
+
# Instead of passing an upload_column object to +render_image+ you can even pass a path String,
|
42
|
+
# if you do you will have to pass a :mime-type option as well though.
|
43
|
+
def render_image( file, options = {} )
|
44
|
+
format = if options.is_a?(Hash) then options[:force_format] else nil end
|
45
|
+
mime_type = if options.is_a?(String) then options else options[:mime_type] end
|
46
|
+
mime_type ||= file.mime_type
|
47
|
+
path = if file.is_a?( String ) then file else file.path end
|
48
|
+
headers["Content-Type"] = mime_type unless format
|
49
|
+
|
50
|
+
if block_given? or format
|
51
|
+
img = ::Magick::Image::read(path).first
|
52
|
+
img = yield( img ) if block_given?
|
53
|
+
img.format = format.to_s.upcase if format
|
54
|
+
render :text => img.to_blob, :layout => false
|
55
|
+
else
|
56
|
+
send_file( path )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
ActionController::Base.send(:include, UploadColumn::ActionControllerExtension)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module UploadColumn::AssetTagExtension
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.send :alias_method_chain, :image_tag, :uploaded_file_check
|
5
|
+
end
|
6
|
+
|
7
|
+
def image_tag_with_uploaded_file_check(source, options = {})
|
8
|
+
if(source.respond_to?(:public_path))
|
9
|
+
image_tag_without_uploaded_file_check(source.public_path, options)
|
10
|
+
else
|
11
|
+
image_tag_without_uploaded_file_check(source, options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
ActionView::Helpers::AssetTagHelper.send(:include, UploadColumn::AssetTagExtension)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module UploadColumn::UploadColumnHelper
|
2
|
+
|
3
|
+
# Returns an input tag of the "file" type tailored for accessing an upload_column field
|
4
|
+
# (identified by method) on an object assigned to the template (identified by object).
|
5
|
+
# Additional options on the input tag can be passed as a hash with options.
|
6
|
+
#
|
7
|
+
# Example (call, result)
|
8
|
+
# upload_column_field( :user, :picture )
|
9
|
+
# <input id="user_picture_temp" name="user[picture_temp]" type="hidden" />
|
10
|
+
# <input id="user_picture" name="user[picture]" size="30" type="file" />
|
11
|
+
#
|
12
|
+
# Note: if you use file_field instead of upload_column_field, the file will not be
|
13
|
+
# stored across form redisplays.
|
14
|
+
def upload_column_field(object, method, options={})
|
15
|
+
file_field(object, method, options) + hidden_field(object, method.to_s + '_temp')
|
16
|
+
end
|
17
|
+
|
18
|
+
# A helper method for creating a form tag to use with uploadng files,
|
19
|
+
# it works exactly like Rails' form_tag, except that :multipart is always true
|
20
|
+
def upload_form_tag(url_for_options = {}, options = {}, *parameters_for_url, &proc)
|
21
|
+
options[:multipart] = true
|
22
|
+
form_tag( url_for_options, options, *parameters_for_url, &proc )
|
23
|
+
end
|
24
|
+
|
25
|
+
# A helper method for creating a form tag to use with uploadng files,
|
26
|
+
# it works exactly like Rails' form_for, except that :multipart is always true
|
27
|
+
def upload_form_for(*args, &block)
|
28
|
+
options = args.extract_options!
|
29
|
+
options[:html] ||= {}
|
30
|
+
options[:html][:multipart] = true
|
31
|
+
args.push(options)
|
32
|
+
|
33
|
+
form_for(*args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class ActionView::Helpers::FormBuilder #:nodoc:
|
39
|
+
self.field_helpers += ['upload_column_field']
|
40
|
+
def upload_column_field(method, options = {})
|
41
|
+
@template.send(:upload_column_field, @object_name, method, options.merge(:object => @object))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
ActionView::Base.send(:include, UploadColumn::UploadColumnHelper)
|
@@ -0,0 +1,176 @@
|
|
1
|
+
begin; require 'mime/types'; rescue Exception; end
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module UploadColumn
|
6
|
+
# Sanitize is a base class that takes care of all the dirtywork when dealing with file uploads.
|
7
|
+
# it is subclassed as UploadedFile in UploadColumn, which does most of the upload magic, but if
|
8
|
+
# you want to roll you own uploading system, SanitizedFile might be for you since it takes care
|
9
|
+
# of a lot of the unfun stuff.
|
10
|
+
#
|
11
|
+
# Usage is pretty simple, just do SanitizedFile.new(some_uploaded_file) and you're good to go
|
12
|
+
# you can now use #copy_to and #move_to to place the file wherever you want, whether it is a StringIO
|
13
|
+
# or a TempFile.
|
14
|
+
#
|
15
|
+
# SanitizedFile also deals with content type detection, which it does either through the 'file' *nix exec
|
16
|
+
# or (if you are stuck on Windows) through the MIME::Types library (not to be confused with Rails' Mime class!).
|
17
|
+
class SanitizedFile
|
18
|
+
|
19
|
+
attr_reader :basename, :extension
|
20
|
+
|
21
|
+
def initialize(file, options = {})
|
22
|
+
@options = options
|
23
|
+
if file && file.instance_of?(String) && !file.empty?
|
24
|
+
@path = file
|
25
|
+
self.filename = File.basename(file)
|
26
|
+
else
|
27
|
+
@file = file
|
28
|
+
self.filename = self.original_filename unless self.empty?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the filename before sanitation took place
|
33
|
+
def original_filename
|
34
|
+
@original_filename ||= if @file and @file.respond_to?(:original_filename)
|
35
|
+
@file.original_filename
|
36
|
+
elsif self.path
|
37
|
+
File.basename(self.path)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the files properly sanitized filename.
|
42
|
+
def filename
|
43
|
+
@filename ||= (self.extension && !self.extension.empty?) ? "#{self.basename}.#{self.extension}" : self.basename
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the file's size
|
47
|
+
def size
|
48
|
+
return @file.size if @file.respond_to?(:size)
|
49
|
+
File.size(self.path) rescue nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the full path to the file
|
53
|
+
def path
|
54
|
+
@path ||= File.expand_path(@file.path) rescue nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# Checks if the file is empty.
|
58
|
+
def empty?
|
59
|
+
(@file.nil? && @path.nil?) || self.size.nil? || self.size.zero?
|
60
|
+
end
|
61
|
+
|
62
|
+
# Checks if the file exists
|
63
|
+
def exists?
|
64
|
+
File.exists?(self.path) if self.path
|
65
|
+
end
|
66
|
+
|
67
|
+
# Moves the file to 'path'
|
68
|
+
def move_to(path)
|
69
|
+
if copy_file(path)
|
70
|
+
# FIXME: This gets pretty broken in UploadedFile. E.g. moving avatar-thumb.jpg will change the filename
|
71
|
+
# to avatar-thumb-thumb.jpg
|
72
|
+
@basename, @extension = split_extension(File.basename(path))
|
73
|
+
@file = nil
|
74
|
+
@filename = nil
|
75
|
+
@path = path
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Copies the file to 'path' and returns a new SanitizedFile that points to the copy.
|
80
|
+
def copy_to(path)
|
81
|
+
copy = self.clone
|
82
|
+
copy.move_to(path)
|
83
|
+
return copy
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the content_type of the file as determined through the MIME::Types library or through a *nix exec.
|
87
|
+
def content_type
|
88
|
+
unless content_type = get_content_type_from_exec || get_content_type_from_mime_types
|
89
|
+
content_type ||= @file.content_type.chomp if @file.respond_to?(:content_type) and @file.content_type
|
90
|
+
end
|
91
|
+
return content_type
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def copy_file(path)
|
97
|
+
unless self.empty?
|
98
|
+
# create the directory if it doesn't exist
|
99
|
+
FileUtils.mkdir_p(File.dirname(path)) unless File.exists?(File.dirname(path))
|
100
|
+
# stringios don't have a path and can't be copied
|
101
|
+
if not self.path and @file.respond_to?(:read)
|
102
|
+
@file.rewind # Make sure we are at the beginning of the buffer
|
103
|
+
File.open(path, "wb") { |f| f.write(@file.read) }
|
104
|
+
else
|
105
|
+
begin
|
106
|
+
FileUtils.cp(self.path, path)
|
107
|
+
rescue ArgumentError
|
108
|
+
end
|
109
|
+
end
|
110
|
+
File.chmod(@options[:permissions], path) if @options[:permissions]
|
111
|
+
return true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def filename=(filename)
|
116
|
+
basename, extension = split_extension(filename)
|
117
|
+
@basename = sanitize(basename)
|
118
|
+
@extension = correct_file_extension(extension)
|
119
|
+
end
|
120
|
+
|
121
|
+
# tries to identify the mime-type of file and correct self's extension
|
122
|
+
# based on the found mime-type
|
123
|
+
def correct_file_extension(ext)
|
124
|
+
if @options[:fix_file_extensions] && defined?(MIME::Types)
|
125
|
+
if mimes = MIME::Types[self.content_type]
|
126
|
+
return mimes.first.extensions.first unless mimes.first.extensions.empty?
|
127
|
+
end
|
128
|
+
end
|
129
|
+
return ext.downcase
|
130
|
+
end
|
131
|
+
|
132
|
+
# Try to use *nix exec to fetch content type
|
133
|
+
def get_content_type_from_exec
|
134
|
+
if @options[:get_content_type_from_file_exec] and not self.path.empty?
|
135
|
+
return system_call(%(file -bi "#{self.path}")).chomp.scan(/^[a-z0-9\-_]+\/[a-z0-9\-_]+/).first
|
136
|
+
end
|
137
|
+
rescue
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
|
141
|
+
def system_call(command)
|
142
|
+
`#{command}`
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_content_type_from_mime_types
|
146
|
+
if @extension and defined?(MIME::Types)
|
147
|
+
mimes = MIME::Types.of(@extension)
|
148
|
+
return mimes.first.content_type rescue nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def sanitize(name)
|
153
|
+
# Sanitize the filename, to prevent hacking
|
154
|
+
name = File.basename(name.gsub("\\", "/")) # work-around for IE
|
155
|
+
name.gsub!(/[^a-zA-Z0-9\.\-\+_]/,"_")
|
156
|
+
name = "_#{name}" if name =~ /^\.+$/
|
157
|
+
name = "unnamed" if name.size == 0
|
158
|
+
return name.downcase
|
159
|
+
end
|
160
|
+
|
161
|
+
def split_extension(fn)
|
162
|
+
# regular expressions to try for identifying extensions
|
163
|
+
ext_regexps = [
|
164
|
+
/^(.+)\.([^\.]{1,3}\.[^\.]{1,4})$/, # matches "something.tar.gz"
|
165
|
+
/^(.+)\.([^\.]+)$/ # matches "something.jpg"
|
166
|
+
]
|
167
|
+
ext_regexps.each do |regexp|
|
168
|
+
if fn =~ regexp
|
169
|
+
return $1, $2
|
170
|
+
end
|
171
|
+
end
|
172
|
+
return fn, "" # In case we weren't able to split the extension
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|