added a rake task to clean orphan uploaded files
This commit is contained in:
parent
3e0f47705a
commit
4a17d6dca6
|
@ -36,6 +36,13 @@ class Upload < ActiveRecord::Base
|
||||||
optimized_images << thumbnail if thumbnail
|
optimized_images << thumbnail if thumbnail
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete
|
||||||
|
Upload.transaction do
|
||||||
|
Upload.remove_file url
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.create_for(user_id, file)
|
def self.create_for(user_id, file)
|
||||||
# compute the sha
|
# compute the sha
|
||||||
sha1 = Digest::SHA1.file(file.tempfile).hexdigest
|
sha1 = Digest::SHA1.file(file.tempfile).hexdigest
|
||||||
|
@ -74,6 +81,11 @@ class Upload < ActiveRecord::Base
|
||||||
return LocalStore.store_file(file, sha1, image_info, upload_id)
|
return LocalStore.store_file(file, sha1, image_info, upload_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.remove_file(url)
|
||||||
|
S3.remove_file(url) if SiteSetting.enable_s3_uploads?
|
||||||
|
LocalStore.remove_file(url)
|
||||||
|
end
|
||||||
|
|
||||||
def self.uploaded_regex
|
def self.uploaded_regex
|
||||||
/\/uploads\/#{RailsMultisite::ConnectionManagement.current_db}\/(?<upload_id>\d+)\/[0-9a-f]{16}\.(png|jpg|jpeg|gif|tif|tiff|bmp)/
|
/\/uploads\/#{RailsMultisite::ConnectionManagement.current_db}\/(?<upload_id>\d+)\/[0-9a-f]{16}\.(png|jpg|jpeg|gif|tif|tiff|bmp)/
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,4 +15,9 @@ module LocalStore
|
||||||
return Discourse::base_uri + "#{url_root}/#{clean_name}"
|
return Discourse::base_uri + "#{url_root}/#{clean_name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.remove_file(url)
|
||||||
|
File.delete("#{Rails.root}/public#{url}")
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
48
lib/s3.rb
48
lib/s3.rb
|
@ -1,22 +1,44 @@
|
||||||
module S3
|
module S3
|
||||||
|
|
||||||
def self.store_file(file, sha1, image_info, upload_id)
|
def self.store_file(file, sha1, image_info, upload_id)
|
||||||
raise Discourse::SiteSettingMissing.new("s3_upload_bucket") if SiteSetting.s3_upload_bucket.blank?
|
S3.check_missing_site_settings
|
||||||
raise Discourse::SiteSettingMissing.new("s3_access_key_id") if SiteSetting.s3_access_key_id.blank?
|
|
||||||
raise Discourse::SiteSettingMissing.new("s3_secret_access_key") if SiteSetting.s3_secret_access_key.blank?
|
|
||||||
|
|
||||||
@fog_loaded = require 'fog' unless @fog_loaded
|
directory = S3.get_or_create_directory(SiteSetting.s3_upload_bucket)
|
||||||
|
|
||||||
remote_filename = "#{upload_id}#{sha1}.#{image_info.type}"
|
remote_filename = "#{upload_id}#{sha1}.#{image_info.type}"
|
||||||
|
|
||||||
options = S3.generate_options
|
|
||||||
directory = S3.get_or_create_directory(SiteSetting.s3_upload_bucket, options)
|
|
||||||
# if this fails, it will throw an exception
|
# if this fails, it will throw an exception
|
||||||
file = S3.upload(file, remote_filename, directory)
|
file = S3.upload(file, remote_filename, directory)
|
||||||
|
|
||||||
return "//#{SiteSetting.s3_upload_bucket}.s3.amazonaws.com/#{remote_filename}"
|
return "//#{SiteSetting.s3_upload_bucket}.s3.amazonaws.com/#{remote_filename}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.remove_file(url)
|
||||||
|
S3.check_missing_site_settings
|
||||||
|
|
||||||
|
directory = S3.get_or_create_directory(SiteSetting.s3_upload_bucket)
|
||||||
|
|
||||||
|
file = S3.destroy(url, directory)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.check_missing_site_settings
|
||||||
|
raise Discourse::SiteSettingMissing.new("s3_upload_bucket") if SiteSetting.s3_upload_bucket.blank?
|
||||||
|
raise Discourse::SiteSettingMissing.new("s3_access_key_id") if SiteSetting.s3_access_key_id.blank?
|
||||||
|
raise Discourse::SiteSettingMissing.new("s3_secret_access_key") if SiteSetting.s3_secret_access_key.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_or_create_directory(name)
|
||||||
|
@fog_loaded = require 'fog' unless @fog_loaded
|
||||||
|
|
||||||
|
options = S3.generate_options
|
||||||
|
|
||||||
|
fog = Fog::Storage.new(options)
|
||||||
|
|
||||||
|
directory = fog.directories.get(name)
|
||||||
|
directory = fog.directories.create(key: name) unless directory
|
||||||
|
|
||||||
|
directory
|
||||||
|
end
|
||||||
|
|
||||||
def self.generate_options
|
def self.generate_options
|
||||||
options = {
|
options = {
|
||||||
provider: 'AWS',
|
provider: 'AWS',
|
||||||
|
@ -28,14 +50,6 @@ module S3
|
||||||
options
|
options
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_or_create_directory(name, options)
|
|
||||||
fog = Fog::Storage.new(options)
|
|
||||||
directory = fog.directories.get(name)
|
|
||||||
directory = fog.directories.create(key: name) unless directory
|
|
||||||
|
|
||||||
directory
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.upload(file, name, directory)
|
def self.upload(file, name, directory)
|
||||||
directory.files.create(
|
directory.files.create(
|
||||||
key: name,
|
key: name,
|
||||||
|
@ -45,4 +59,8 @@ module S3
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.destroy(name, directory)
|
||||||
|
directory.files.destroy(key: name)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,3 +30,22 @@ task "images:reindex" => :environment do
|
||||||
end
|
end
|
||||||
puts "\ndone."
|
puts "\ndone."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "clean orphan uploaded files"
|
||||||
|
task "images:clean_orphans" => :environment do
|
||||||
|
RailsMultisite::ConnectionManagement.each_connection do |db|
|
||||||
|
puts "Cleaning up #{db}"
|
||||||
|
# ligthweight safety net to prevent users from wiping all their uploads out
|
||||||
|
if PostUpload.count == 0 && Upload.count > 0
|
||||||
|
puts "The reverse index is empty. Make sure you run the `images:reindex` task"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
Upload.joins("LEFT OUTER JOIN post_uploads ON uploads.id = post_uploads.upload_id")
|
||||||
|
.where("post_uploads.upload_id IS NULL")
|
||||||
|
.find_each do |u|
|
||||||
|
u.delete
|
||||||
|
putc "."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
puts "\ndone."
|
||||||
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ describe LocalStore do
|
||||||
|
|
||||||
let(:image_info) { FastImage.new(file) }
|
let(:image_info) { FastImage.new(file) }
|
||||||
|
|
||||||
it 'returns the url of the S3 upload if successful' do
|
it 'returns the url of the uploaded file if successful' do
|
||||||
# prevent the tests from creating directories & files...
|
# prevent the tests from creating directories & files...
|
||||||
FileUtils.stubs(:mkdir_p)
|
FileUtils.stubs(:mkdir_p)
|
||||||
File.stubs(:open)
|
File.stubs(:open)
|
||||||
|
|
|
@ -62,17 +62,17 @@ describe Upload do
|
||||||
|
|
||||||
it "identifies internal or relatives urls" do
|
it "identifies internal or relatives urls" do
|
||||||
Discourse.expects(:base_url_no_prefix).returns("http://discuss.site.com")
|
Discourse.expects(:base_url_no_prefix).returns("http://discuss.site.com")
|
||||||
Upload.has_been_uploaded?("http://discuss.site.com/upload/1234/42/ABCD.jpg").should == true
|
Upload.has_been_uploaded?("http://discuss.site.com/upload/1234/42/0123456789ABCDEF.jpg").should == true
|
||||||
Upload.has_been_uploaded?("/upload/42/ABCD.jpg").should == true
|
Upload.has_been_uploaded?("/upload/42/0123456789ABCDEF.jpg").should == true
|
||||||
end
|
end
|
||||||
|
|
||||||
it "identifies internal urls when using a CDN" do
|
it "identifies internal urls when using a CDN" do
|
||||||
ActionController::Base.expects(:asset_host).returns("http://my.cdn.com").twice
|
ActionController::Base.expects(:asset_host).returns("http://my.cdn.com").twice
|
||||||
Upload.has_been_uploaded?("http://my.cdn.com/upload/1234/42/ABCD.jpg").should == true
|
Upload.has_been_uploaded?("http://my.cdn.com/upload/1234/42/0123456789ABCDEF.jpg").should == true
|
||||||
end
|
end
|
||||||
|
|
||||||
it "identifies external urls" do
|
it "identifies external urls" do
|
||||||
Upload.has_been_uploaded?("http://domain.com/upload/1234/42/ABCD.jpg").should == false
|
Upload.has_been_uploaded?("http://domain.com/upload/1234/42/0123456789ABCDEF.jpg").should == false
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue