Хранение изображений в файловой системе нарушает работу файлов. Есть ли лучший способ написать это? Почему я получаю испорченные файлы? - PullRequest
1 голос
/ 20 марта 2012
post '/upload' do
  unless params[:file] && (tmpfile = params[:file][:tempfile]) && (name = params[:file][:filename])
    return haml(:upload)
  end
  time = Time.now.to_s
  time.gsub!(/\s/, '')
  name = time + name
  while blk = tmpfile.read(65536)
    File.open(File.join(Dir.pwd,"public/uploads", name), "wb") { |f| f.write(tmpfile.read) }
  end
  'success'
end

Все идет туда, где и ожидалось, что файлы просто повредятся.

Ответы [ 2 ]

5 голосов
/ 20 марта 2012

Этот бит выглядит действительно прикольным:

while blk = tmpfile.read(65536)
    File.open(File.join(Dir.pwd,"public/uploads", name), "wb") { |f| f.write(tmpfile.read) }
end

Я предполагаю, что вы пытаетесь прочитать свой временный файл по 65536-байтовому блоку за раз, а затем последовательно записать эти блоки в файл назначения. Но вы никогда не пишете blk, что является первым прочитанным блоком; вместо этого вы пишете остальную часть файла (tempfile.read). И даже если этот цикл записывал блоки так, как должен, он заново открывает файл для каждого блока, перезаписывая старое содержимое! Во всяком случае, я подозреваю, что вы имели в виду что-то вроде этого:

File.open(File.join(Dir.pwd,"public/uploads", name), "wb") do |f|
    while(blk = tempfile.read(65536))
        f.write(blk)
    end
end

Тем не менее, если у вас есть файл как временный файл (предположительно, уже в вашей локальной файловой системе), возможно, все, что вам нужно сделать, это переместить этот файл ? В этом случае все будет намного быстрее - если источник и место назначения находятся на одном диске, нужно просто поменять местами некоторые указатели файловой системы, а не копировать все эти данные.

Надеюсь, это поможет!

3 голосов
/ 20 марта 2012

Код открывается и заменяет файл во время каждой итерации цикла, что вызывает часть проблемы.Код также считывает tmpfile в blk, а затем выбрасывает эти данные.Time.now.to_s содержит двоеточия, которые являются разделителем пути в Mac OS X и могут вызвать проблемы в OS X. Предоставленное пользователем имя файла может содержать некоторые плохие вещи, такие как .., которые могут позволить пользователям перезаписывать файлы.Попробуйте вместо этого:

require 'pathname'
require 'zaru'

post '/upload' do
  unless tmpfile = params[:file].try(:[], :tempfile)
    return haml(:upload)
  end

  name = Zaru.sanitize!("#{Time.now.to_i}#{params[:file][:filename]}")
  Pathname.pwd.join("public/uploads", name).open("wb") do |f|
    while blk = tmpfile.read(65536)
      f.write(blk)
    end
  end

  'success'
end

Вы также должны убедиться, что имя файла не заканчивается чем-то нечистым, например .js или .css, которые могут быть использованы.

...