Class: FileStore::BaseStore

Inherits:
Object
  • Object
show all
Defined in:
lib/file_store/base_store.rb

Direct Known Subclasses

LocalStore, S3Store

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.temporary_upload_path(file_name, folder_prefix: "") ⇒ Object



46
47
48
49
50
51
52
# File 'lib/file_store/base_store.rb', line 46

def self.temporary_upload_path(file_name, folder_prefix: "")
  # We don't want to use the original file name as it can contain special
  # characters, which can interfere with external providers operations and
  # introduce other unexpected behaviour.
  file_name_random = "#{SecureRandom.hex}#{File.extname(file_name)}"
  File.join(TEMPORARY_UPLOAD_PREFIX, folder_prefix, SecureRandom.hex, file_name_random)
end

Instance Method Details

#absolute_base_urlObject



66
67
68
# File 'lib/file_store/base_store.rb', line 66

def absolute_base_url
  not_implemented
end

#cache_file(file, filename) ⇒ Object



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/file_store/base_store.rb', line 210

def cache_file(file, filename)
  path = get_cache_path_for(filename)
  dir = File.dirname(path)
  FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
  FileUtils.cp(file.path, path)

  # Remove all but CACHE_MAXIMUM_SIZE most recent files
  files = Dir.glob("#{CACHE_DIR}*")
  files.sort_by! do |f|
    begin
      File.mtime(f)
    rescue Errno::ENOENT
      Time.new(0)
    end
  end
  files.pop(CACHE_MAXIMUM_SIZE)

  FileUtils.rm(files, force: true)
end

#cdn_url(url) ⇒ Object



62
63
64
# File 'lib/file_store/base_store.rb', line 62

def cdn_url(url)
  not_implemented
end

#download(object, max_file_size_kb: nil, print_deprecation: true) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/file_store/base_store.rb', line 107

def download(object, max_file_size_kb: nil, print_deprecation: true)
  Discourse.deprecate(<<~MESSAGE) if print_deprecation
      In a future version `FileStore#download` will no longer raise an error when the
      download fails, and will instead return `nil`. If you need a method that raises
      an error, use `FileStore#download!`, which raises a `FileStore::DownloadError`.
    MESSAGE

  DistributedMutex.synchronize("download_#{object.sha1}", validity: 3.minutes) do
    extension =
      File.extname(
        object.respond_to?(:original_filename) ? object.original_filename : object.url,
      )
    filename = "#{object.sha1}#{extension}"
    file = get_from_cache(filename)

    if !file
      max_file_size_kb ||= [
        SiteSetting.max_image_size_kb,
        SiteSetting.max_attachment_size_kb,
      ].max.kilobytes

      secure = object.respond_to?(:secure) ? object.secure? : object.upload.secure?
      url =
        (
          if secure
            Discourse.store.signed_url_for_path(object.url)
          else
            Discourse.store.cdn_url(object.url)
          end
        )

      url = SiteSetting.scheme + ":" + url if url =~ %r{\A//}
      file =
        FileHelper.download(
          url,
          max_file_size: max_file_size_kb,
          tmp_file_name: "discourse-download",
          follow_redirect: true,
        )

      return nil if file.nil?

      cache_file(file, filename)
      file = get_from_cache(filename)
    end

    file
  end
end

#download!Object



101
102
103
104
105
# File 'lib/file_store/base_store.rb', line 101

def download!(*, **)
  download(*, **, print_deprecation: false)
rescue StandardError
  raise DownloadError
end

#download_safeObject

TODO: Remove when #download becomes the canonical safe version.



95
96
97
98
99
# File 'lib/file_store/base_store.rb', line 95

def download_safe(*, **)
  download(*, **, print_deprecation: false)
rescue StandardError
  nil
end

#download_url(upload) ⇒ Object



58
59
60
# File 'lib/file_store/base_store.rb', line 58

def download_url(upload)
  not_implemented
end

#external?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/file_store/base_store.rb', line 78

def external?
  not_implemented
end

#get_cache_path_for(filename) ⇒ Object



201
202
203
# File 'lib/file_store/base_store.rb', line 201

def get_cache_path_for(filename)
  "#{CACHE_DIR}#{filename}"
end

#get_from_cache(filename) ⇒ Object



205
206
207
208
# File 'lib/file_store/base_store.rb', line 205

def get_from_cache(filename)
  path = get_cache_path_for(filename)
  File.open(path) if File.exist?(path)
end

#get_path_for(type, id, sha, extension) ⇒ Object



160
161
162
163
164
# File 'lib/file_store/base_store.rb', line 160

def get_path_for(type, id, sha, extension)
  depth = get_depth_for(id)
  tree = File.join(*sha[0, depth].chars, "")
  "#{type}/#{depth + 1}X/#{tree}#{sha}#{extension}"
end

#get_path_for_optimized_image(optimized_image) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/file_store/base_store.rb', line 184

def get_path_for_optimized_image(optimized_image)
  # try to extract the path from the URL instead of calculating it,
  # because the calculated path might differ from the actual path
  if optimized_image.url.present? && (path = optimized_image.url[OPTIMIZED_IMAGE_PATH_REGEX, 1])
    return prefix_path(path)
  end

  upload = optimized_image.upload
  version = optimized_image.version || 1
  extension =
    "_#{version}_#{optimized_image.width}x#{optimized_image.height}#{optimized_image.extension}"
  get_path_for("optimized", upload.id, upload.sha1, extension)
end

#get_path_for_upload(upload) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/file_store/base_store.rb', line 166

def get_path_for_upload(upload)
  # try to extract the path from the URL instead of calculating it,
  # because the calculated path might differ from the actual path
  if upload.url.present? && (path = upload.url[UPLOAD_PATH_REGEX, 1])
    return prefix_path(path)
  end

  extension =
    if upload.extension
      ".#{upload.extension}"
    else
      # Maintain backward compatibility before Jobs::MigrateUploadExtensions runs
      File.extname(upload.original_filename)
    end

  get_path_for("original", upload.id, upload.sha1, extension)
end

#has_been_uploaded?(url) ⇒ Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/file_store/base_store.rb', line 54

def has_been_uploaded?(url)
  not_implemented
end

#internal?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'lib/file_store/base_store.rb', line 82

def internal?
  !external?
end

#list_missing_uploads(skip_optimized: false) ⇒ Object



90
91
92
# File 'lib/file_store/base_store.rb', line 90

def list_missing_uploads(skip_optimized: false)
  not_implemented
end

#path_for(upload) ⇒ Object



86
87
88
# File 'lib/file_store/base_store.rb', line 86

def path_for(upload)
  not_implemented
end

#purge_tombstone(grace_period) ⇒ Object



157
158
# File 'lib/file_store/base_store.rb', line 157

def purge_tombstone(grace_period)
end

#relative_base_urlObject



70
71
72
# File 'lib/file_store/base_store.rb', line 70

def relative_base_url
  not_implemented
end

#remove_file(url, path) ⇒ Object



36
37
38
# File 'lib/file_store/base_store.rb', line 36

def remove_file(url, path)
  not_implemented
end

#remove_optimized_image(optimized_image) ⇒ Object



32
33
34
# File 'lib/file_store/base_store.rb', line 32

def remove_optimized_image(optimized_image)
  remove_file(optimized_image.url, get_path_for_optimized_image(optimized_image))
end

#remove_upload(upload) ⇒ Object



28
29
30
# File 'lib/file_store/base_store.rb', line 28

def remove_upload(upload)
  remove_file(upload.url, get_path_for_upload(upload))
end

#s3_upload_hostObject



74
75
76
# File 'lib/file_store/base_store.rb', line 74

def s3_upload_host
  not_implemented
end

#store_file(file, path, opts = {}) ⇒ Object



24
25
26
# File 'lib/file_store/base_store.rb', line 24

def store_file(file, path, opts = {})
  not_implemented
end

#store_optimized_image(file, optimized_image, content_type = nil, secure: false) ⇒ Object



18
19
20
21
22
# File 'lib/file_store/base_store.rb', line 18

def store_optimized_image(file, optimized_image, content_type = nil, secure: false)
  optimized_image.url = nil
  path = get_path_for_optimized_image(optimized_image)
  store_file(file, path)
end

#store_upload(file, upload, content_type = nil) ⇒ Object



12
13
14
15
16
# File 'lib/file_store/base_store.rb', line 12

def store_upload(file, upload, content_type = nil)
  upload.url = nil
  path = get_path_for_upload(upload)
  store_file(file, path)
end

#upload_pathObject



40
41
42
43
44
# File 'lib/file_store/base_store.rb', line 40

def upload_path
  path = File.join("uploads", RailsMultisite::ConnectionManagement.current_db)
  return path if !Rails.env.test?
  File.join(path, "test_#{ENV["TEST_ENV_NUMBER"].presence || "0"}")
end