Я хотел бы пожертвовать свое решение «безопасного перемещения», которое не зависит от какого-либо частного API и защищает от потери данных из-за сбоя сети:
Сначала мы получаем старые и новые пути для каждого стиля:
styles = file.styles.keys+[:original]
old_style2key = Hash[ styles.collect{|s| [s,file.path(s).sub(%r{\A/},'')]} ]
self.file_file_name = new_filename
new_style2key = Hash[ styles.collect{|s| [s,file.path(s).sub(%r{\A/},'')]} ]
Затем мы копируем каждый файл на новый путь. Поскольку путь по умолчанию включает в себя как идентификатор объекта, так и имя файла, он никогда не может конфликтовать с путем для другого файла. Но это не удастся, если мы попытаемся переименовать без изменения имени:
styles.each do |style|
raise "same key" if old_style2key[style] == new_style2key[style]
file.s3_bucket.objects[old_style2key[style]].copy_to(new_style2key[style])
end
Теперь мы применяем обновленную модель к БД:
save!
Важно сделать это после того, как мы создадим новые объекты S3, но перед тем, как удалить старые объекты S3. Большинство других решений в этом потоке могут привести к потере данных в случае сбоя обновления базы данных (например, разделение сети с неправильной синхронизацией), поскольку тогда файл будет находиться в новом местоположении S3, но БД все еще будет указывать на старое местоположение. Вот почему мое решение не удаляет старые объекты S3 до тех пор, пока не удастся обновить БД:
styles.each do |style|
file.s3_bucket.objects[old_style2key[style]].delete
end
Как и в случае с копией, мы не можем случайно удалить данные другого объекта базы данных, потому что идентификатор объекта включен в путь. Поэтому, если вы не переименуете один и тот же объект базы данных A-> B и B-> A в одно и то же время (например, 2 потока), это удаление всегда будет безопасным.