Как стать автором
Обновить
289.47
TINKOFF
IT’s Tinkoff — просто о сложном

Опенсорс-решение для автоматизации отчетности

Время на прочтение 9 мин
Количество просмотров 34K
Разработчикам сайтов и мобильных приложений часто нужно управлять подготовкой PDF-страниц к выводу на печать или их отправкой клиентам на почту.

У PDF-файлов есть полный контроль над отображением текста и графическими изображениями на странице. К сожалению, библиотеки для генерации динамически заполняющихся PDF-файлов не входят в стандартный инструментарий PHP, JS (Web), Java или Swift (Android и iOS соответственно). В этой статье хочу вам рассказать об опенсорс-решении для генерации PDF-файлов.



JasperReports — это открытая Java-библиотека для генерации динамически заполняющихся файлов. В ней есть много инструментов для создания сложных отчетных форм, в том числе в формате PDF, но также доступны и другие форматы: RTF, DOCX, HTML, XLS, XLS, CSV и XML. Иными словами, достаточно разработать одну форму, сделать одну верстку — и можно будет экспортировать его в любой из вышеперечисленных форматов.

Есть еще хорошие библиотеки, например PDFLib (коммерческая версия) для PHP и ее версия с открытыми исходными кодами PDFLib-Lite. Правда, библиотека довольно дорогая, а облегченная версия распространяется только в исходных кодах, и при ее установке в среде разработки это ограничение может стать проблемой.

PDFbox — еще одна библиотека Java с открытым исходным кодом для работы с документами PDF. Она позволяет создавать новые PDF-документы, управлять существующими документами с возможностью извлекать контент из них. Но у нее нет UI (User Interface), в отличие от JasperReports.

Думаю, JasperReports особо полезен в больших проектах, связанных с отчетностью и не только в PDF-формате. Здесь есть все необходимое, чтобы внедрить ее в ваш проект: простое создание сложных отчетных форм, UI для удобной верстки, серверное приложение и простая интеграция с фронтом.

В статье я раскрою следующие темы:

  • Установка среды разработки и серверного приложения.
  • Создание PDF-файла автоматически заполняющегося из базы данных.
  • Интеграция серверного приложения с фронтендом для получения созданного PDF.

Чтобы начать использовать JasperReports в вашем проекте, необходимо скачать два приложения: JaspersoftStudio — далее будем называть его рабочей средой — и JasperServer — будем называть серверным приложением.

JaspersoftStudio — среда разработки на основе Eclipse со встроенной Java-библиотекой JasperReports, где разрабатываются динамические или статичные PDF-файлы: например, билеты, квитанции, договора, аналитические чарты и другие.

JasperServer — это серверное приложение, куда из JaspersoftStudio деплоятся и хранятся файлы. К ним можно получить доступ из мобильного или веб-приложения. В JasperServer есть UI, с его помощью можно просматривать отчеты, создавать учетные записи для разных пользователей и давать им соответствующие доступы. Также можно настроить рассылку на электронную почту (Scheduler).

Настраиваем рабочую среду и серверное приложение


Скачать и установить приложения можно по ссылкам выше. После сетапа двух приложений необходимо установить соединение из рабочей среды к серверному приложению.

Servers → Create JasperReports Server Connection → Указать предпочитаемое имя сервера, URL, логин и пароль. Нажмите Test Connection, чтобы проверить, что соединение с сервером установлено. Если видите надпись Successful — едем дальше.



Создаем динамически заполняющийся PDF и публикуем его


Мы установили среду разработки, сервер и наладили соединение между ними. Теперь давайте создадим примитивный динамически заполняющийся PDF-файл, который при его запуске (генерации) будет брать данные из PostgreSQL, и задеплоим его на серверное приложение.

В первую очередь следует прикрутить к рабочей среде источник данных (в нашем случае — PostgreSQL), откуда PDF будет брать данные. Затем приступим к разработке нашего первого PDF-файла.

Data Adapters → Create Data Adapter → Database JDBS Connection и указываем данные соединения:

  1. JDBC Driver — PostgreSQL (org.postgresql.Driver). Если нет драйвера вашего СУБД, можно установить нужный драйвер во вкладке Driver Classpath.
  2. JDBC URL — он состоит их хоста, порта и названия БД.
  3. Username и password — логпасс от вашей учетной записи СУБД.



Нажимаем на уже знакомую нам кнопку Test и при успешном соединении (Successful) с БД — Finish.

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

Data Sources → Add Resource → Data Source и повторяем все из пункта выше.

Теперь можно приступать к созданию PDF. Исходники в JasperReports хранятся в формате JRXML — это XML с зашитыми тегами и атрибутами, с которыми работает API JasperReports.

Нажимаем File → New → Jasper Report → Blank A4 → Указываем имя JRXML-файла → Finish.



После создания нового проекта вы увидите следующую картину:



Семь разных блоков — для каждого блока прописано свое поведение, отличающееся от других. Более подробно об этом можно почитать в документации. Нажав на Source можно увидеть структуру этих блоков:

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport
	xmlns="http://jasperreports.sourceforge.net/jasperreports"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Example" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="ae9517f6-ff0b-41bb-a8dc-82196190e940">
	<queryString>
		<![CDATA[]]>
	</queryString>
	<background>
		<band splitType="Stretch"/>
	</background>
	<title>
		<band height="79" splitType="Stretch"/>
	</title>
	<pageHeader>
		<band height="35" splitType="Stretch"/>
	</pageHeader>
	<columnHeader>
		<band height="61" splitType="Stretch"/>
	</columnHeader>
	<detail>
		<band height="125" splitType="Stretch"/>
	</detail>
	<columnFooter>
		<band height="45" splitType="Stretch"/>
	</columnFooter>
	<pageFooter>
		<band height="54" splitType="Stretch"/>
	</pageFooter>
	<summary>
		<band height="42" splitType="Stretch"/>
	</summary>
</jasperReport>

Итак, давайте удалим пять лишних блоков и оставим только два: Title и Detail. В этом нам поможет кнопка Delete (Windows) или Backspace (OS X).

Теперь добавим два элемента. Добавить новый элемент можно двумя способами: прописать контейнер в XML-структуре (кнопка Source) либо перетащить нужный элемент из правого верхнего окна Pallette — Static Text, где будут названия полей и Text Field, внутрь которого зашьем переменные поля, вытащенные из БД:

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport
	xmlns="http://jasperreports.sourceforge.net/jasperreports"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Example" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="ae9517f6-ff0b-41bb-a8dc-82196190e940">
	<queryString>
		<![CDATA[]]>
	</queryString>
	<background>
		<band splitType="Stretch"/>
	</background>
	<title>
		<band height="30" splitType="Stretch">
			<staticText>
				<reportElement x="0" y="0" width="100" height="30" uuid="7b697ed9-f52a-483c-965e-f0b2dc6130c1"/>
				<text>
					<![CDATA[Static Text]]>
				</text>
			</staticText>
		</band>
	</title>
	<detail>
		<band height="169" splitType="Stretch">
			<textField>
				<reportElement x="0" y="0" width="100" height="20" uuid="41002e0b-ddb2-4e4b-a049-10810ab51208"/>
				<textFieldExpression>
					<![CDATA["Text Field"]]>
				</textFieldExpression>
			</textField>
		</band>
	</detail>
</jasperReport>

Запрос в БД можно написать в теге queryString либо нажать на кнопку DataSet and Query editor dialog. После этого откроется новое окно, где необходимо выбрать источник данных (1), написать запрос (2) и объявить переменные поля. Кнопка Read Fields (3) прочитает все поля автоматически при валидном запросе. Чтобы просмотреть данные, нужно нажать на Data preview (4).



Отлично! Мы получили четыре поля с типом String, теперь можем совершать с ними практически любые манипуляции. Для примера мы просто выведем их списком и пропишем небольшую логику.

Напечатаем названия нужных полей в Static Text элементах и поместим их в контейнер Title. Переменные поля укажем в Text Field элементах в контейнере Detail, так как они будут множиться. Наш PDF будет выводить имя, город и адрес электронной почты. Чтобы не было совсем скучно, давайте в Text Field элементе пропишем простую логику, используя четвертое поле — пол клиента, Sex.

Сделаем следующее: если клиент — женщина, то перед именем будем добавлять Mrs., если мужчина — Mr. Для этого используем тернарный оператор Java:

<textFieldExpression>
      <![CDATA[$F{sex}.equals( "male" )?"Mr. "+$F{name}:"Mrs. "+$F{name}]]>
</textFieldExpression> 

Нажав на Preview рядом с кнопкой Source, можно увидеть результат:



Как видно на скриншоте, PDF успешно собрался: забрал все значения и применил логику, правильно проставив Mrs. и Mr.

Еще заведем входной параметр City, чтобы была возможность фильтровать данные по городам. Это можно сделать либо нажав Parameters → Create Parameter в окне Outline, либо добавив новый тег parameter с атрибутами name и class:

<parameter name="City" class="java.lang.String"/>

Осталось только добавить параметр в SQL-запрос:

SELECT 
Id, name, sex, city, email
FROM users WHERE city = $P{City}

Передадим в параметр City значение San Francisco (о том, как это сделать, расскажу в следующем пункте) и нажмем Data Preview для просмотра результата.



PDF собрался, успешно отфильтровав данные. Едем дальше

Так как у нас уже есть динамически заполняющийся PDF-файл, можем задеплоить его на сервер для дальнейшей интеграции с нашими фронтенд-приложениями. Для этого нажимаем кнопку Publish Report to JasperReports Server → двойным кликом открываем сервер → Выбираем серверную папку, куда нужно загрузить PDF (в нашем случае — reports) → Next → Data Source from Repository → выбираем источник данных, который создали ранее на серверном приложении → Finish.

Интеграция с фронтендом


В JasperReports API входит своя RESTful-реализация для взаимодействия клиента с сервером — REST v2. Если вам она не подходит, можете использовать простой протокол доступа к объектам — SOAP.

Мы рассмотрим REST v2.

Доступны основные четыре метода для действий CRUD (Create-Read-Update-Delete): GET (получить), POST (добавить, изменить, удалить), PUT (добавить, заменить), DELETE (удалить). Вся подробная информация имеется в документации по ссылкам выше.

Мы рассмотрим более распространенный и актуальный для этой статьи метод GET.

http://<host>:<port>/jasperserver[pro]/rest_v2/reports/path/to/report.<format>?<arguments> 

Выше — синхронный запрос, с помощью которого можно получить выходные данные файла (готовый PDF) в одном запросе-ответе (с асинхронным вызовом можно ознакомиться здесь).

Думаю, с хостом и портом все понятно, а /reports/path/to/report — это URI того файла, который вызывается. Так как мы задеплоили исходник PDF-файла (Example.jrxml) в серверную папку reports, заполненный вариант URI будет таким: /reports/reports/Example.

format — это формат (в нашем случае PDF).
arguments — параметры.

Выше мы добавили параметр City, его и передадим в запросе со значением San Francisco для фильтрации данных по этому городу.

Если вызов делается не с авторизованной зоны, нужно добавить еще два параметра/атрибута: j_username и j_password (логпасс для авторизации). По умолчанию логин и пароль на сервере — jasperadmin.

Таким образом, получаем следующий URL:

http://localhost:8080/jasperserver/rest_v2/reports/reports/Example.PDF?city=San Francisco&j_username=jasperadmin&j_password=jasperadmin

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

Может возникнуть необходимость отобразить PDF картинкой. Например, если клиент хочет просто просмотреть файл, можно показать документ в PNG-формате, если же хочет скачать его, то в PDF.

На примере Java с использованием библиотеки PDFbox рассмотрим, как можно из внешнего приложения сгенерировать и забрать PDF-файл, а затем сконвертировать его в PNG.

Ниже — класс PullPDF с одним методом, принимающим в качестве аргумента URL-адрес.

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.tools.imageio.ImageIOUtil;

public class PullPDF {
 public String convertPDF2PNG(String valuefromParam) throws IOException {
  BufferedInputStream input_file = new BufferedInputStream(new URL(valuefromParam).openStream());
  ByteArrayOutputStream baos = new ByteArrayOutputStream();

  try {
   PDDocument document = PDDocument.load(input_file);
   PDFRenderer pdfRenderer = new PDFRenderer(document);
   for (int page = 0; page < document.getNumberOfPages(); ++page) {
    BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
    ImageIOUtil.writeImage(bim, "png", baos);
    baos.flush();
    byte[] encodedBytes = Base64.getEncoder().encode(baos.toByteArray());
    valuefromParam = new String(encodedBytes);
   }
  } catch (Exception e) {

  }
  return valuefromParam;
 }
}

Можно получить тот же результат, используя, например, Spring Framework. Но я постарался показать универсальный способ, который можно применить и в Android, и в вебе при работе с Java.

Заключение


Если вы хотите автоматизировать генерацию простого чека для интернет-магазина и у вас ограничено время на его создание, то я рекомендую использовать нативный инструментарий вашего проекта или знакомый вам фреймворк. Поскольку затраты времени на установку JasperReports, ознакомление с документацией для разработки более комплексных отчетных форм, могут не оправдать себя. В остальных случаях JasperReports — хорошее опенсорс-решение для автоматизации отчетности.
Теги:
Хабы:
+9
Комментарии 23
Комментарии Комментарии 23

Публикации

Информация

Сайт
www.tinkoff.ru
Дата регистрации
Дата основания
Численность
свыше 10 000 человек
Местоположение
Россия