support arbitrary attachments
This commit is contained in:
parent
e8c1a81e87
commit
27ab5f471c
|
@ -4,8 +4,7 @@ class UploadsController < ApplicationController
|
||||||
def create
|
def create
|
||||||
file = params[:file] || params[:files].first
|
file = params[:file] || params[:files].first
|
||||||
|
|
||||||
# only supports images for now
|
return render status: 415, json: failed_json unless SiteSetting.authorized_file?(file)
|
||||||
return render status: 415, json: failed_json unless file.content_type =~ /^image\/.+/
|
|
||||||
|
|
||||||
upload = Upload.create_for(current_user.id, file)
|
upload = Upload.create_for(current_user.id, file)
|
||||||
|
|
||||||
|
|
|
@ -274,6 +274,23 @@ class SiteSetting < ActiveRecord::Base
|
||||||
top_menu_items.map { |item| item.name }.select{ |item| list.include?(item) }.first
|
top_menu_items.map { |item| item.name }.select{ |item| list.include?(item) }.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.authorized_file?(file)
|
||||||
|
file.original_filename =~ /\.(#{authorized_extensions.tr(". ", "")})$/i
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.images
|
||||||
|
@images ||= ["jpg", "jpeg", "png", "gif", "tif", "tiff", "bmp"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.authorized_image?(file)
|
||||||
|
authorized_images = authorized_extensions
|
||||||
|
.tr(". ", "")
|
||||||
|
.split("|")
|
||||||
|
.select { |extension| images.include?(extension) }
|
||||||
|
.join("|")
|
||||||
|
file.original_filename =~ /\.(#{authorized_images})$/i
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
require 'digest/sha1'
|
require 'digest/sha1'
|
||||||
require 'image_sizer'
|
require 'image_sizer'
|
||||||
require 's3'
|
|
||||||
require 'local_store'
|
|
||||||
require 'tempfile'
|
require 'tempfile'
|
||||||
require 'pathname'
|
require 'pathname'
|
||||||
|
require_dependency 's3'
|
||||||
|
require_dependency 'local_store'
|
||||||
|
|
||||||
class Upload < ActiveRecord::Base
|
class Upload < ActiveRecord::Base
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
@ -48,24 +48,25 @@ class Upload < ActiveRecord::Base
|
||||||
sha1 = Digest::SHA1.file(file.tempfile).hexdigest
|
sha1 = Digest::SHA1.file(file.tempfile).hexdigest
|
||||||
# check if the file has already been uploaded
|
# check if the file has already been uploaded
|
||||||
unless upload = Upload.where(sha1: sha1).first
|
unless upload = Upload.where(sha1: sha1).first
|
||||||
# retrieve image info
|
|
||||||
image_info = FastImage.new(file.tempfile, raise_on_failure: true)
|
|
||||||
# compute image aspect ratio
|
|
||||||
width, height = ImageSizer.resize(*image_info.size)
|
|
||||||
# create a db record (so we can use the id)
|
# create a db record (so we can use the id)
|
||||||
upload = Upload.create!({
|
upload = Upload.create!({
|
||||||
user_id: user_id,
|
user_id: user_id,
|
||||||
original_filename: file.original_filename,
|
original_filename: file.original_filename,
|
||||||
filesize: File.size(file.tempfile),
|
filesize: File.size(file.tempfile),
|
||||||
sha1: sha1,
|
sha1: sha1,
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
url: ""
|
url: ""
|
||||||
})
|
})
|
||||||
# make sure we're at the beginning of the file (FastImage is moving the pointer)
|
# deal with width & heights for images
|
||||||
file.rewind
|
if SiteSetting.authorized_image?(file)
|
||||||
|
# retrieve image info
|
||||||
|
image_info = FastImage.new(file.tempfile, raise_on_failure: true)
|
||||||
|
# compute image aspect ratio
|
||||||
|
upload.width, upload.height = ImageSizer.resize(*image_info.size)
|
||||||
|
# make sure we're at the beginning of the file (FastImage is moving the pointer)
|
||||||
|
file.rewind
|
||||||
|
end
|
||||||
# store the file and update its url
|
# store the file and update its url
|
||||||
upload.url = Upload.store_file(file, sha1, image_info, upload.id)
|
upload.url = Upload.store_file(file, sha1, upload.id)
|
||||||
# save the url
|
# save the url
|
||||||
upload.save
|
upload.save
|
||||||
end
|
end
|
||||||
|
@ -73,9 +74,9 @@ class Upload < ActiveRecord::Base
|
||||||
upload
|
upload
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.store_file(file, sha1, image_info, upload_id)
|
def self.store_file(file, sha1, upload_id)
|
||||||
return S3.store_file(file, sha1, image_info, upload_id) if SiteSetting.enable_s3_uploads?
|
return S3.store_file(file, sha1, upload_id) if SiteSetting.enable_s3_uploads?
|
||||||
return LocalStore.store_file(file, sha1, image_info, upload_id)
|
return LocalStore.store_file(file, sha1, upload_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.remove_file(url)
|
def self.remove_file(url)
|
||||||
|
@ -92,29 +93,15 @@ class Upload < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.is_local?(url)
|
def self.is_local?(url)
|
||||||
url.start_with?(base_url)
|
!SiteSetting.enable_s3_uploads? && url.start_with?(LocalStore.base_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.is_on_s3?(url)
|
def self.is_on_s3?(url)
|
||||||
SiteSetting.enable_s3_uploads? && url.start_with?(S3.base_url)
|
SiteSetting.enable_s3_uploads? && url.start_with?(S3.base_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.base_url
|
|
||||||
asset_host.present? ? asset_host : Discourse.base_url_no_prefix
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.asset_host
|
|
||||||
ActionController::Base.asset_host
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.get_from_url(url)
|
def self.get_from_url(url)
|
||||||
if has_been_uploaded?(url)
|
Upload.where(url: url).first if has_been_uploaded?(url)
|
||||||
if m = LocalStore.uploaded_regex.match(url)
|
|
||||||
Upload.where(id: m[:upload_id]).first
|
|
||||||
elsif is_on_s3?(url)
|
|
||||||
Upload.where(url: url).first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
module LocalStore
|
module LocalStore
|
||||||
|
|
||||||
def self.store_file(file, sha1, image_info, upload_id)
|
def self.store_file(file, sha1, upload_id)
|
||||||
clean_name = Digest::SHA1.hexdigest("#{Time.now.to_s}#{file.original_filename}")[0,16] + ".#{image_info.type}"
|
unique_sha1 = Digest::SHA1.hexdigest("#{Time.now.to_s}#{file.original_filename}")[0,16]
|
||||||
|
extension = File.extname(file.original_filename)
|
||||||
|
clean_name = "#{unique_sha1}#{extension}"
|
||||||
url_root = "/uploads/#{RailsMultisite::ConnectionManagement.current_db}/#{upload_id}"
|
url_root = "/uploads/#{RailsMultisite::ConnectionManagement.current_db}/#{upload_id}"
|
||||||
path = "#{Rails.root}/public#{url_root}"
|
path = "#{Rails.root}/public#{url_root}"
|
||||||
|
|
||||||
FileUtils.mkdir_p path
|
FileUtils.mkdir_p path
|
||||||
|
|
||||||
# not using cause mv, cause permissions are no good on move
|
# not using cause mv, cause permissions are no good on move
|
||||||
File.open("#{path}/#{clean_name}", "wb") do |f|
|
File.open("#{path}/#{clean_name}", "wb") do |f|
|
||||||
f.write File.read(file.tempfile)
|
f.write File.read(file.tempfile)
|
||||||
end
|
end
|
||||||
|
|
||||||
# url
|
# url
|
||||||
return Discourse::base_uri + "#{url_root}/#{clean_name}"
|
Discourse::base_uri + "#{url_root}/#{clean_name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.remove_file(url)
|
def self.remove_file(url)
|
||||||
|
@ -24,4 +27,21 @@ module LocalStore
|
||||||
/\/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
|
||||||
|
|
||||||
|
def self.base_url
|
||||||
|
url = asset_host.present? ? asset_host : Discourse.base_url_no_prefix
|
||||||
|
"#{url}#{directory}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.base_path
|
||||||
|
"#{Rails.root}/public#{directory}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.directory
|
||||||
|
"/uploads/#{RailsMultisite::ConnectionManagement.current_db}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.asset_host
|
||||||
|
ActionController::Base.asset_host
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
module S3
|
module S3
|
||||||
|
|
||||||
def self.store_file(file, sha1, image_info, upload_id)
|
def self.store_file(file, sha1, upload_id)
|
||||||
S3.check_missing_site_settings
|
S3.check_missing_site_settings
|
||||||
|
|
||||||
directory = S3.get_or_create_directory(SiteSetting.s3_upload_bucket)
|
directory = S3.get_or_create_directory(SiteSetting.s3_upload_bucket)
|
||||||
|
extension = File.extname(file.original_filename)
|
||||||
remote_filename = "#{upload_id}#{sha1}.#{image_info.type}"
|
remote_filename = "#{upload_id}#{sha1}#{extension}"
|
||||||
|
|
||||||
# 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)
|
||||||
|
|
Loading…
Reference in New Issue