#ruby-on-rails #ruby #pdf #rails-activestorage #pdf-reader
#ruby-on-rails #ruby #PDF #rails-activestorage #pdf-reader
Вопрос:
У меня есть простой одностраничный PDF-файл с возможностью поиска, который загружается в модель приложения Rails 6 (Car) с использованием Active Storage. Я могу извлечь текст из PDF-файла, используя драгоценные камни ‘tempfile’ и ‘pdf-reader’ в консоли Rails:
> @car.creport.attached?
=> true
> f = Tempfile.new(['file', '.pdf'])
> f.binmode
> f.write(@car.creport.blob.download)
> r = PDF::Reader.new(f.path.to_s)
> r.pages[1].text
=> "Welcome to the ABC Car Report for January 16, 20...
Но, если я попробую то же самое в методе create моего cars_controller.rb, это не сработает:
# cars_controller.rb
...
def create
@car = Car.new(car_params)
@car.filetext = ""
f = Tempfile.new(['file', '.pdf'])
f.binmode
f.write(@car.creport.blob.download)
r = PDF::Reader.new(f.path.to_s)
@car.filetext = r.pages[1].text
...
end
Когда я запускаю приложение Rails, я могу создать новый Car и выбрать файл PDF для вложения. Но когда я нажимаю «Отправить», я получаю ошибку FileNotFoundError в cars_controller.rb в строке f.write().
Мой внутренний инстинкт заключается в том, что контроллер пытается прочитать большой двоичный объект, чтобы записать его во временный файл слишком рано (т. Е. До того, как большой двоичный объект был даже записан). Я попытался вставить a sleep(2)
, чтобы дать ему время, но я получаю тот же FileNotFoundError .
Есть идеи?
Спасибо!
Ответ №1:
Я не понимаю, почему вы прыгаете через столько обручей. И использование .download
без блока загружает весь файл в память (yikes). Если @car.creport
это вложение ActiveStorage, вы можете просто использовать метод open вместо этого:
@car.creport.blob.open do |file|
file.binmode
r = PDF::Reader.new(file) # just pass the IO object
@car.filetext = r.pages[1].text
end if @car.creport
Вместо этого файл отправляется на диск (как временный файл).
Если вы просто вводите файл с помощью обычного старого ввода файла, вы получите ActionDispatch::Http::UploadedFile в параметрах, которые также очень легко открыть:
params[:file].open do |file|
file.binmode
r = PDF::Reader.new(file) # just pass the IO object
@car.filetext = r.pages[1].text
end if params[:file].respond_to?(:open)
Комментарии:
1. Я все еще n00b, и я не совсем уверен, что я делаю, поэтому у меня было так много шагов… Я пытался понять, где он ломается. Я не смог заставить работать верхнюю часть кода. Он сломался в том же месте с тем же ‘FileNotFoundError’. Чтобы было понятно, у меня есть форма, которую я использую для загрузки вложения PDF в запись Car. Прямо над вашим блоком кода у меня есть: « def create @car = Car.new(review_params) « (Я не уверен, почему этот комментарий неправильно форматируется?)
2. Оказывается, дьявол кроется в деталях с ActiveStorage. Вложение фактически недоступно до тех пор, пока родительская модель не будет сохранена, это запускает обновление таблицы больших двоичных объектов ActiveStorage, в которой задаются столбцы resource_id и type. Это «требует» загрузки.
Ответ №2:
Разница, похоже, связана с вашей @car
переменной.
В консоли у вас есть прикрепленный большой двоичный объект ( @car.creport.attached? => true
). В вашем контроллере вы инициализируете новый экземпляр класса Car, поэтому, если у вас не выполняется какая-либо инициализация, которая подключает что-то в фоновом режиме, это будет равно нулю.
Почему это вернет ошибку «файл не найден», я не уверен, но из того, что я вижу, это единственное различие между примерами кода. Вы пытаетесь написать @car.creport.blob.download
, который присутствует @car
в консоли, но nil в вашем контроллере.