You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
1.4 KiB
77 lines
1.4 KiB
6 years ago
|
class Base64ImageConverter
|
||
|
BASE64_HEAD = 'data:image/jpeg;base64,'.freeze
|
||
|
|
||
|
Error = Class.new(StandardError)
|
||
|
OutLimit = Class.new(Error)
|
||
|
InvalidData = Class.new(Error)
|
||
|
InvalidFormat = Class.new(Error)
|
||
|
|
||
|
attr_reader :opts
|
||
|
|
||
|
def initialize(**opts)
|
||
|
@opts = opts
|
||
|
end
|
||
|
|
||
|
def convert(data)
|
||
|
raise InvalidData, '不合法的Base64数据' unless valid_base64?(data)
|
||
|
|
||
|
io = StringIO.new(Base64.decode64(image_data data))
|
||
|
|
||
|
raise OutLimit, '文件大小超过限制' if opts[:max_size].present? && io.size > opts[:max_size]
|
||
|
|
||
|
raise InvalidFormat, '无效的格式' unless Image.new(io).image?
|
||
|
|
||
|
io
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def valid_base64?(data)
|
||
|
data&.start_with?(BASE64_HEAD)
|
||
|
end
|
||
|
|
||
|
def image_data(data)
|
||
|
data[BASE64_HEAD.size..-1]
|
||
|
end
|
||
|
|
||
|
def size_limit
|
||
|
EduSetting.find_by_name('upload_avatar_max_size')&.value
|
||
|
end
|
||
|
|
||
|
class Image
|
||
|
attr_reader :io
|
||
|
|
||
|
def initialize(io)
|
||
|
raise ArgumentError unless io.respond_to?(:read)
|
||
|
@io = io
|
||
|
end
|
||
|
|
||
|
def data
|
||
|
@_data ||= begin
|
||
|
data = io.read(9)
|
||
|
io.rewind
|
||
|
data
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def image?
|
||
|
bitmap? || gif? || jpeg? || png?
|
||
|
end
|
||
|
|
||
|
def bitmap?
|
||
|
data[0,2] == 66.chr + 77.chr
|
||
|
end
|
||
|
|
||
|
def gif?
|
||
|
data[0,4] == 71.chr + 73.chr + 70.chr + 56.chr
|
||
|
end
|
||
|
|
||
|
def jpeg?
|
||
|
data[0,3] == 0xff.chr + 0xd8.chr + 0xff.chr
|
||
|
end
|
||
|
|
||
|
def png?
|
||
|
data[0,2] == 0x89.chr + 80.chr
|
||
|
end
|
||
|
end
|
||
|
end
|