кнопка для сохранения текущей страницы в rails 3.2

Мне нужна кнопка для сохранения текущего веб-сайта (так же, как нажатие «Сохранить как»), я создал метод в контроллере, который отлично работает для любого внешнего сайта (например, http://www.google.com), но не работает для сайтов внутри моего приложения, я получаю сообщение об ошибке тайм-аута!. Это не имеет для меня объяснения :(

Любая подсказка, в чем проблема?

#CONTROLLER FILE
def save_current_page
  # =>  Using MECHANIZE
  agent = Mechanize.new
  page = agent.get request.referer
  send_data(page.content, :filename => "filename.txt")
end

Я также пробовал Open URI, та же проблема!

#CONTROLLER FILE
def save_current_page
 # => USANDO OPEN URI
 send_data(open(request.referer).read, :filename => "filename.txt")
end

Я использую rails 3.2 и ruby ​​1.9, любая помощь приветствуется, я уже потратил около 10 часов, пытаясь заставить это работать!


person Andres Calle    schedule 30.09.2012    source источник


Ответы (2)


Rails может обрабатывать только один запрос за раз. Это бесконечное противостояние между двумя запросами — первый запрос ожидает второго запроса, но второй запрос ожидает первого запроса, и поэтому вы получаете ошибку тайм-аута. Даже если вы используете несколько экземпляров приложения с Passenger или чем-то еще, это плохая идея.

Единственный способ, которым я могу обойти это, - это использовать условные операторы, например:

referer = URI.parse(request.referer)

if Rails.application.config.default_url_options[:host] == referer.host
  content = "via yoursite.com"
else
  agent = Mechanize.new
  page = agent.get request.referer
  content = page.content
end

send_data content, filename: "filename.txt"

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

Гораздо лучшим решением было бы поставить этот код в очередь во что-то вроде Resque или Delayed Job. Затем очередь может сделать запрос и ждать в очереди, чтобы запросить страницу, как обычно. Это также означало бы, что пользователю не придется ждать, пока ваше приложение сделает удаленный запрос, что опасно, потому что кто знает, сколько времени займет ответ страницы.

person bricker    schedule 01.10.2012
comment
Привет, Брикер, я думаю, что это все еще не решает эту проблему, это приложение должно сохранить текущую страницу, которая будет myapp.com/something, но похоже, что предлагаемое решение будет работать только при сохранении внешнего сайта, такого как www.google.com и исключает локальный сайт. Однако я проверил это, и проблема остается (не могу объяснить, почему, если должно работать и избегать цикла в сценарии локального сайта). Эй, чувак, спасибо, что нашел время и помог мне с этим. - person Andres Calle; 02.10.2012
comment
Да, вам нужно решить, как получить локальный сайт. Вы просто не можете заставить приложение делать удаленный запрос самому себе, и даже если вы запускаете несколько экземпляров приложения, оно все равно может зависнуть. Убедитесь, что ваш Rails.application.config.default_url_options[:host] совпадает с referer.host при переходе со страницы в приложении. - person bricker; 02.10.2012
comment
Я вижу кнопку загрузки текущей страницы в нескольких других приложениях (в частности, в одном, сделанном с помощью ASP), я думаю, что это должно быть возможно сделать в рельсах, это даже похоже на то, что это работало для других людей в рельсах, проверьте здесь stackoverflow.com/questions/1080565/ - person Andres Calle; 08.10.2012

После нескольких часов и множества других сообщений я пришел к окончательному решению:

Брикер прав в том, что рельсы не могут выполнять рендеринг более одного раза за вызов, как это взято из http://guides.rubyonrails.org/layouts_and_rendering.html "Может отображать или перенаправлять только один раз для каждого действия"

На сайте также говорится: «Правило состоит в том, что если вы явно не визуализируете что-либо в конце действия контроллера, Rails автоматически ищет шаблон action_name.html.erb в пути представления контроллера и отображает его».

Затем решение, которое отлично сработало для меня, состояло в том, чтобы указать контроллеру отображать в строку, если в :params установлен флаг загрузки (download=true) (я также использую request.url, чтобы он работал из любого представления в моем приложении). )

Вид:

= link_to 'Download', request.url+"&downloadexcel=true", :class => 'btn btn-primary btn-block'

Контроллер:

def acontrolleraction
  #some controller code here
  if params[:downloadexcel]
    save_page_xls
  else
    # render normally
  end
end


def save_page_xls
  #TRESCLOUD - we create a proper name for the file
  path = URI(request.referer).path.gsub(/[^0-9a-z]/i, '')
  query = URI(request.referer).query.gsub(/[^0-9a-z]/i, '')
  filename = @project_data['NOMBRE']+"_"+path+"_"+query+".xls"

  #TRESCLOUD - we render the page into a variable and process it
  page = render_to_string

  #TRESCLOUD - we send the file for download!
  send_data(page, :filename => filename, :type => "application/xls")
end

Спасибо за ваши советы!

person Andres Calle    schedule 16.10.2012