Статьи

Простое предоставление отчетов Excel с Rails и jXLS

Отчетность является одним из наиболее важных требований в любом приложении, и, если мы имеем дело с бизнес-приложением, требования могут стать еще более важными. Однако предоставление отчетов на экране или красивых отчетов в формате HTML или PDF не всегда удовлетворяет пользователя. Иногда пользователям требуются отчеты Excel, чтобы они могли в дальнейшем работать с данными, чтобы получить именно те результаты, которые им нужны.

К счастью, Ruby предоставляет множество библиотек, связанных с Excel, но большинство из них используют программный способ создания Excel, который может быстро усложниться и утомить более сложные отчеты. Но у нас есть решение для этого: jXLS . Это популярная библиотека Java, используемая для генерации файлов Excel из шаблонных файлов Excel. Благодаря JRuby мы можем использовать эту превосходную библиотеку в Rails. Без лишних слов давайте начнем.

Создание базового приложения Rails

Мы создадим базовое приложение Rails с моделью Invoice и действиями CRUD. Приложение будет использовать JRuby, Rails 4.2 и SQLite для упрощения работы. Прежде всего, установите JRuby с помощью RVM или rbenv .

Переключитесь на JRuby и gem install rails для gem install rails чтобы получить последнюю версию Rails. Теперь создайте новое приложение Rails, вот так:

 rails new jxls_rails -T 

После того, как приложение сгенерировано, создайте скаффолд Invoice, который имеет модель и необходимые операции ресурса CRUD:

 cd jxls_rails rails g scaffold Invoice invoice_number:string invoice_date:string customer:string total_value:decimal 

Теперь перенесите базу данных:

 rake db:migrate 

Давайте проверим, как это работает:

 rails s 

Направьте ваш любимый браузер на счета http: // localhost: 3000 / и убедитесь, что все работает правильно. Создайте несколько записей, чтобы мы могли использовать их позже.

Создание отчета Excel

Сначала давайте создадим электронную таблицу Excel, которая будет служить шаблоном отчета. Я использую LibreOffice, так как я нахожусь на Ubuntu. Сохраните его как invoices.xls в каталоге app / reports . Добавьте поля и комментарии, как показано на снимке экрана ниже.

Образец-первенствуйте

Мы определяем шаблон для отчета, добавляя заголовки, заполнители для данных и комментарии для метаданных, связанных с jXLS. Как видите, мы можем отформатировать ячейки так, как нам нравится в самом шаблоне, поэтому нет необходимости программно управлять этим.

Мы в основном установили заполнители для данных, которые выглядят как ${invoice.invoice_number} и будут заменены фактическими данными. Первый комментарий, jx:area(lastCell="D2") , задает рабочую область для отчета, определяя последнюю ячейку в области видимости отчета. Другой комментарий, jx:each(items="jdbc.query(query)" var="invoice" lastCell="D2") , указывает тег итератора, который перемещается по всем записям и заполняет строки, заменяя заполнители. Здесь items — набор строк для отчета. Мы разработали отчет для непосредственного выполнения предоставленного SQL-запроса и заполнения отчета с использованием jdbc.query(query) качестве items . Мы могли бы также передать коллекцию объектов.

Мы создали простую электронную таблицу Excel. Есть несколько расширенных опций, которые можно найти в документации по jXLS .

Интеграция jXLS в Rails

Сначала загрузите файлы jXLS со страницы jXLS SourceForge . Мы используем версию 2.2.9, поэтому скачайте jxls-2.2.9.zip и разархивируйте файл. Скопируйте все файлы из папки dist в lib / jxls нашего приложения.

Также загрузите все зависимости jXLS по этой ссылке . Разархивируйте и скопируйте все файлы из папки lib в lib / jxls в нашем приложении.

Наша окончательная структура каталогов выглядит следующим образом:

 jxls_rails - app ... - lib - jxls - jxls-2.2.9.jar ... 

Теперь у нас есть все необходимые файлы на месте. Давайте создадим файл с именем jxls.rb в каталоге lib со следующим кодом:

 Dir.entries("#{Rails.root}/lib/jxls").each do |lib| require "jxls/#{lib}" if lib =~ /\.jar$/ end require 'java' java_import Java::org.jxls.common.Context java_import Java::org.jxls.util.JxlsHelper java_import Java::org.jxls.jdbc.JdbcHelper java_import Java::java.io.BufferedInputStream java_import Java::java.io.FileInputStream java_import Java::java.io.ByteArrayOutputStream java_import Java::org.jxls.util.TransformerFactory java_import Java::org.jxls.transform.poi.PoiTransformer class Jxls DIR = "#{Rails.root}/app/reports" def initialize(report, query) @filename = report @conn = ActiveRecord::Base.connection.jdbc_connection @query = query end def to_xls beans = {} report_source = BufferedInputStream.new(FileInputStream.new("#{DIR}/#{@filename}.xls")) raise ArgumentError, "#@filename does not exist." unless File.exist?("#{DIR}/#{@filename}.xls") bos = ByteArrayOutputStream.new context = Context.new jdbc_helper = JdbcHelper.new(@conn) context.put_var("jdbc", jdbc_helper) context.put_var("query", @query) JxlsHelper.getInstance().processTemplate(report_source, bos, context) bos.close bytes = bos.toByteArray return String.from_java_bytes(bytes) end end 

Хорошо, давайте пройдемся по этому коду и выясним, что он на самом деле делает. Во-первых, нам нужны все файлы jXLS в наш класс, вот так:

 Dir.entries("#{Rails.root}/lib/jxls").each do |lib| require "jxls/#{lib}" if lib =~ /\.jar$/ end 

Добавьте объявления импорта Java для jXLS:

 require 'java' java_import Java::org.jxls.common.Context java_import Java::org.jxls.util.JxlsHelper java_import Java::org.jxls.jdbc.JdbcHelper java_import Java::java.io.BufferedInputStream java_import Java::java.io.FileInputStream java_import Java::java.io.ByteArrayOutputStream java_import Java::org.jxls.util.TransformerFactory java_import Java::org.jxls.transform.poi.PoiTransformer 

Определите место, где будут храниться все отчеты:

 DIR = "#{Rails.root}/app/reports" 

Добавьте код инициализации в конструктор класса:

 def initialize(report, query) @filename = report @query = query @conn = ActiveRecord::Base.connection.jdbc_connection end 

В качестве первого шага мы инициализировали все необходимые переменные:

  • Имя файла reportreport
  • SQL-запрос отчета — query

@conn — это соединение JDBC из пула соединений ActiveRecord, поскольку jXLS потребуется соединение JDBC для выполнения запроса, указанного в отчете. Если мы собираемся передать коллекцию объектов, то это соединение может не понадобиться.

Наконец, мы добавили метод для заполнения и экспорта отчета в формат Excel:

 def to_xls report_source = BufferedInputStream.new(FileInputStream.new("#{DIR}/#{@filename}.xls")) raise ArgumentError, "#@filename does not exist." unless File.exist?("#{DIR}/#{@filename}.xls") bos = ByteArrayOutputStream.new context = Context.new jdbc_helper = JdbcHelper.new(@conn) context.put_var("jdbc", jdbc_helper) context.put_var("query", @query) JxlsHelper.getInstance().processTemplate(report_source, bos, context) bos.close bytes = bos.toByteArray return String.from_java_bytes(bytes) end 

Файл отчета ( invoices.xls ), созданный ранее, устанавливается как источник report_source . Затем мы добавляем jdbc_helper и @query в объект context для передачи в отчет. Наконец, мы вызываем JxlsHelper.getInstance().processTemplate для заполнения и экспорта отчета в поток данных Excel, который возвращается с использованием String.from_java_bytes .

Теперь у нас есть отчет в виде байтового потока Excel, но мы пока не можем отправить его пользователю. Итак, добавьте небольшой вспомогательный метод в application_controller.rb следующим образом:

 def respond_to_report(name, query, filename, download = false) @report = Jxls.new(name, query) disposition = (download.nil? || download == false) ? 'inline' : 'attachment' send_data @report.to_pdf, :filename => filename, :type => :xls, :disposition => disposition end 

Этот вспомогательный метод упрощает вызов отчета и отправляет ответ обратно пользователю. Также добавлены параметры для предоставления имени файла, наряду с возможностью disposition чтобы открыть файл в браузере или загрузить его как вложение.

Еще одна вещь, которую нам нужно сделать, это добавить инициализатор для загрузки нашего класса JXLS в Rails. Создайте файл с именем config / initializers / jxls.rb со следующим кодом —

 require 'jxls' 

Теперь мы добавим действие в InvoicesController который вызывает отчет. Добавьте следующий код в app / controllers / invoices_controller.rb :

 def report respond_to_report('invoices', 'select * from invoices', 'invoices.xls') end 

Поскольку у нас нет параметров для передачи и мы хотим открыть отчет в браузере, мы исключили download и report_params из вызова метода.

Обновите route.rb, чтобы добавить новое действие:

 resources :invoices do get :report, on: :collection end 

Добавьте ссылку на отчет в представлении invoices / index.html.erb :

 ... <h1>Listing Invoices</h1> <%= link_to 'Download as Excel', report_invoices_path %> ... 

Запустите сервер и перейдите по адресу http: // localhost: 3000 / invoices . Нажмите «Скачать как Excel». Теперь вы должны увидеть файл Excel со всеми счетами в списке.

Окончательный-первенствуйте

Завершение

Сегодня мы получили краткий обзор того, как использовать jXLS с приложением Rails для быстрой генерации отчетов Excel. jXLS предоставляет более сложные опции для сложных отчетов, таких как формулы, графики, макросы и т. д., но это на другой день.

Ваши комментарии и идеи всегда приветствуются.