diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2012-12-13 12:07:19 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2012-12-13 12:07:19 +0000 |
commit | c99b638d61cc5dd6b9ffcf4212dfaca1973f7500 (patch) | |
tree | 13f809bad3d9ce0c8583b857d5030b55c54bb1f1 /app/models/attachment.rb | |
parent | d0bbaef308ae6183445a8697b08f8eae5ed9ccf8 (diff) | |
download | redmine-c99b638d61cc5dd6b9ffcf4212dfaca1973f7500.tar.gz redmine-c99b638d61cc5dd6b9ffcf4212dfaca1973f7500.zip |
Store attachments in subdirectories (#5298).
Existing files can be moved to their target subdirectories using rake redmine:attachments:move_to_subdirectories.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10990 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'app/models/attachment.rb')
-rw-r--r-- | app/models/attachment.rb | 45 |
1 files changed, 38 insertions, 7 deletions
diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 6fa079c91..c288d588b 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -16,6 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. require "digest/md5" +require "fileutils" class Attachment < ActiveRecord::Base belongs_to :container, :polymorphic => true @@ -92,9 +93,6 @@ class Attachment < ActiveRecord::Base def filename=(arg) write_attribute :filename, sanitize_filename(arg.to_s) - if new_record? && disk_filename.blank? - self.disk_filename = Attachment.disk_filename(filename) - end filename end @@ -102,7 +100,13 @@ class Attachment < ActiveRecord::Base # and computes its MD5 hash def files_to_final_location if @temp_file && (@temp_file.size > 0) + self.disk_directory = target_directory + self.disk_filename = Attachment.disk_filename(filename, disk_directory) logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)") + path = File.dirname(diskfile) + unless File.directory?(path) + FileUtils.mkdir_p(path) + end md5 = Digest::MD5.new File.open(diskfile, "wb") do |f| if @temp_file.respond_to?(:read) @@ -134,7 +138,7 @@ class Attachment < ActiveRecord::Base # Returns file's location on disk def diskfile - File.join(self.class.storage_path, disk_filename.to_s) + File.join(self.class.storage_path, disk_directory.to_s, disk_filename.to_s) end def title @@ -259,6 +263,26 @@ class Attachment < ActiveRecord::Base Attachment.where("created_on < ? AND (container_type IS NULL OR container_type = '')", Time.now - age).destroy_all end + # Moves an existing attachment to its target directory + def move_to_target_directory! + if !new_record? & readable? + src = diskfile + self.disk_directory = target_directory + dest = diskfile + if src != dest && FileUtils.mkdir_p(File.dirname(dest)) && FileUtils.mv(src, dest) + update_column :disk_directory, disk_directory + end + end + end + + # Moves existing attachments that are stored at the root of the files + # directory (ie. created before Redmine 2.3) to their target subdirectories + def self.move_from_root_to_target_directory + Attachment.where("disk_directory IS NULL OR disk_directory = ''").find_each do |attachment| + attachment.move_to_target_directory! + end + end + private # Physically deletes the file from the file system @@ -276,8 +300,15 @@ class Attachment < ActiveRecord::Base @filename = just_filename.gsub(/[\/\?\%\*\:\|\"\'<>]+/, '_') end - # Returns an ASCII or hashed filename - def self.disk_filename(filename) + # Returns the subdirectory in which the attachment will be saved + def target_directory + time = created_on || DateTime.now + time.strftime("%Y/%m") + end + + # Returns an ASCII or hashed filename that do not + # exists yet in the given subdirectory + def self.disk_filename(filename, directory=nil) timestamp = DateTime.now.strftime("%y%m%d%H%M%S") ascii = '' if filename =~ %r{^[a-zA-Z0-9_\.\-]*$} @@ -287,7 +318,7 @@ class Attachment < ActiveRecord::Base # keep the extension if any ascii << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$} end - while File.exist?(File.join(@@storage_path, "#{timestamp}_#{ascii}")) + while File.exist?(File.join(storage_path, directory.to_s, "#{timestamp}_#{ascii}")) timestamp.succ! end "#{timestamp}_#{ascii}" |