Skip to content

Commit

Permalink
Truncate long filename before saving tempfile on cache!
Browse files Browse the repository at this point in the history
Reinstates patch by @vrinek that got rejected on upgrade to v0.10
Avoids Errno::ENAMETOOLONG exception that mostly occurs when saving
a remote image with greek letters in the URL
  • Loading branch information
pharlez authored and avgerin0s committed Apr 11, 2016
1 parent 95b861b commit b935afe
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 0 deletions.
21 changes: 21 additions & 0 deletions lib/carrierwave/uploader/download.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ module Download
include CarrierWave::Uploader::Cache

class RemoteFile
# 255 characters is the max size of a filename in modern filesystems
# and 100 characters are allocated for versions
MAX_FILENAME_LENGTH = 255 - 100

def initialize(uri)
@uri = uri
end
Expand All @@ -20,6 +24,23 @@ def original_filename
unless File.extname(filename).present? || mime_type.blank?
filename = "#{filename}.#{mime_type.extensions.first}"
end

if filename.size > MAX_FILENAME_LENGTH
# 32 for MD5 and 2 for the __ separator
split_position = MAX_FILENAME_LENGTH - 32 - 2
extension = File.extname(filename) if File.extname(filename).present?

if extension
split_position -= extension.size
filename = File.basename(filename, extension)
end

hex = Digest::MD5.hexdigest(filename[split_position, filename.size])

filename = filename[0, split_position] + '__' + hex
filename << extension if extension
end

filename
end

Expand Down
39 changes: 39 additions & 0 deletions spec/uploader/download_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,19 @@
end

describe '#download!' do
let(:long_filename) { 'TgFCbMcysSV0v3-JJyvP02lfjh-XzbRxjsNpECoDJEsnoUUro9me195pWTE597xl6p6vDjo5sn5bGMjS40MRwMIsAsbNpqKfqdO19xvFbyPrVeXrkUMDeF_YjMUPXeVkRGdE3nGkK2zgwBCMAMMu2aU06Vod1FvslJaoasIFwqqF_jzolk2ot8nXlwTFvXt82CAV-a6gwqXFFdIfwRlCSF3gLGlfuPqSPzPxamwyDhzcJaf-eSMrsLE1-YA4BUZmEwD9hDKWusnpQ4jqGEbPBP5BKkM-HWPmxkVzkcQahtvQnlA' }
let(:long_url_without_extension) { 'http://www.example.com/' + long_filename }
let(:long_url) { long_url_without_extension + '.jpg' }

before do
allow(CarrierWave).to receive(:generate_cache_id).and_return('1369894322-345-1234-2255')

stub_request(:get, "www.example.com/" + long_filename + ".jpg").
to_return(body: File.read(file_path('test.jpg')), headers: { "Content-Type" => 'image/jpg' })

stub_request(:get, "www.example.com/" + long_filename).
to_return(body: File.read(file_path('test.jpg')), headers: { "Content-Type" => 'image/jpg' })

stub_request(:get, "www.example.com/test.jpg")
.to_return(body: File.read(file_path("test.jpg")))

Expand All @@ -39,6 +49,35 @@
expect(@uploader.file).to be_an_instance_of(CarrierWave::SanitizedFile)
end

context "on a remote file with a long filename" do
context "when the remote filename has no extension" do
it "should only use part of the original filename" do
@uploader.download!(long_url_without_extension)
@uploader.filename.size.should <= 255
@uploader.filename.should =~ /^#{long_url.split("/").last[0,121]}__/

regexp = /#{@uploader.filename.split("__").first}/
long_url_without_extension.split("/").last.should =~ regexp
end
end

context "when the remote filename has a proper extension" do
it "should only use part of the original filename" do
@uploader.download!(long_url)
@uploader.filename.size.should <= 255
@uploader.filename.should =~ /^#{long_url.split("/").last[0,117]}__/

regexp = /#{@uploader.filename.split("__").first}/
long_url.split("/").last.should =~ regexp
end

it "should retain the extension" do
@uploader.download!(long_url)
@uploader.filename.should =~ /\.jpg$/
end
end
end

it "should be cached" do
@uploader.download!('http://www.example.com/test.jpg')
expect(@uploader).to be_cached
Expand Down

0 comments on commit b935afe

Please sign in to comment.