Чтение текста из PDF работает в консоли Rails, но не в приложении Rails

#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 в вашем контроллере.