Главная Библиотека Шаблоны и примерыJava в Domino. Небольшой пример обработки потока
Николай Норкин
г. Киров
notesnet@notesnet.ru
В качестве примера, иллюстрирующего обработку файловых данных на уровне потока, можно рассмотреть манипуляции, связанные с внесением информации в электронную таблицу формата XLSX (Microsoft Excel 2003)
Задача - ежедневно формировать отчёт по данным приложения Notes в электронной таблице Microsoft Excel. Бланк таблицы уже существует, программно необходимо заполнить некоторые ячейки таблицы. В частности, ячейку с датой построения отчёта - ячейку C2 на первом листе книги Microsoft Excel
Требуется оговориться, что данная задача не предусматривает выхода за границы размеченного бланка, заполнения каких-то списковых отчётов. Бланк электронной таблицы можно назвать условно табелем с заранее обозначенным и отформатированным количеством строк и столбцов. Таким образом, отсекается необходимость что-то знать о формате электронной таблицы MS Excel (кроме, разве того факта, что значение даты записывается целым числом). Более близкое знакомство с форматом XLSX откладывается до лучших времён
Подготовка бланка отчёта
Предварительно нужно подготовить файл отчёта Отчёт.xlsx
Требуется открыть файл в приложении Micrisift Excel, установить формат ячейки С2 как формат даты (чтобы не было недоразумений - в ячейку будет записано числовое представление даты), сохранить файл
Затем нужно открыть файл отчёта в программе-архиваторе как архив zip и разделить его на две части, переместив из него файл sheet1.xml
Открыть файл sheet1.xml в текстовом редакторе или редакторе XML-файлов и задать дополнительный атрибут ячейке C2
<row r="2" spans="1:22" ht="29.25" customHeight="1" thickBot="1">
<c r="A2" s="27" t="s"><v>70</v></c>
<c r="C2" s="34" id="today"/>
<c r="G2" s="22"/>
<c r="H2" s="35" t="s"><v>64</v></c>
После этого, и "покоцанный" файл Отчёт.xlsx, и файл sheet1.xml разместить в виде файловых ресурсов в дизайне приложения
Замечание. Из описанного выше критичным является только установка формата ячейки. Парсинг архива можно реализовать и программно. А одну ячейку найти по атрибуту r, но в реальной задаче, требующей заполнения множества значений, соответствие ячейки и данных задать на программном уровне может быть значительно сложнее, чем на уровне шаблона
Программный код, решающий задачу заполнения бланка табеля
Для манипулирования потоками в предлагаемом решении используется библиотека Memo, описанная в этом материале. Для работы с zip-архивом - пакет java.util.zip
Шаг 1. Получить XML-представление файлового ресурса sheet1.xml
org.w3c.dom.Document xml = this.getSheet("sheet1.xml");
protected org.w3c.dom.Document getSheet(String filename) {
org.w3c.dom.Document xml = null;
try {
ru.medkirov.mail.File factory = new ru.medkirov.mail.File(this.session, this.database);
ru.medkirov.mail.FileResource file = factory.getFileResource(filename);
String base64 = file.getResource();
byte[] decodeBase64 = com.ibm.xml.enc.dom.Base64.decode(base64.getBytes());
return this.stringToDocument(decodeBase64);
}
catch (Exception e) {}
return xml;
}
private org.w3c.dom.Document stringToDocument(byte[] xml) {
try {
javax.xml.parsers.DocumentBuilderFactory factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
javax.xml.parsers.DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse( new java.io.ByteArrayInputStream(xml));
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
Шаг 2. Записать в ячейку значение текущей даты
javax.xml.xpath.XPath xpath = javax.xml.xpath.XPathFactory.newInstance().newXPath();
org.w3c.dom.Element cell = (org.w3c.dom.Element)xpath.evaluate("//c[@id='today']", xml, javax.xml.xpath.XPathConstants.NODE);
this.fillCell(xml, cell, Long.toString(this.getDayCount(new java.util.Date())));
protected static void fillCell(org.w3c.dom.Document document, org.w3c.dom.Element cell, String value) {
try {
if (cell == null) return;
org.w3c.dom.Element element = document.createElement("v");
cell.appendChild(element);
element.appendChild(document.createTextNode(value));
}
catch (Exception e) {
// e.printStackTrace();
}
}
public static long getDayCount(java.util.Date dateEnd) { // Дата в MS Excel представлена в виде целого числа, равного количеству дней, прошедших с 31 декабря 1899 года
long diff = -1;
try {
java.util.Calendar calendar = java.util.GregorianCalendar.getInstance();
calendar.set(1899, 11, 31, 0, 0, 0);
java.util.Date dateStart = calendar.getTime();
//time is always 00:00:00 so rounding should help to ignore the missing hour when going from winter to summer time as well as the extra hour in the other direction
diff = Math.round((dateEnd.getTime() - dateStart.getTime()) / (double) 86400000);
} catch (Exception e) {}
return diff;
}
}
Шаг 3. Собрать файл отчёта
String xlsx = this.pack(xml, "Отчёт.xlsx");
protected String pack(org.w3c.dom.Document sheet, String filename) {
try {
ru.medkirov.mail.File factory = new ru.medkirov.mail.File(this.session, this.database);
ru.medkirov.mail.FileResource file = factory.getFileResource(filename);
String base64 = file.getResource();
byte[] decodeBase64 = com.ibm.xml.enc.dom.Base64.decode(base64.getBytes());
java.util.zip.ZipInputStream zip = new java.util.zip.ZipInputStream(new java.io.ByteArrayInputStream(decodeBase64));
java.io.ByteArrayOutputStream copyStream = new java.io.ByteArrayOutputStream();
java.util.zip.ZipOutputStream newZip = new java.util.zip.ZipOutputStream(copyStream);
newZip.setLevel(java.util.zip.ZipOutputStream.DEFLATED);
// копировать
java.util.zip.ZipEntry entry = (java.util.zip.ZipEntry) zip.getNextEntry();
java.util.zip.ZipEntry clone;
while ( entry != null ){
clone = new java.util.zip.ZipEntry(entry.getName());
clone.setComment(entry.getComment());
clone.setExtra(entry.getExtra());
clone.setMethod(entry.getMethod());
newZip.putNextEntry(clone);
if (!entry.isDirectory()) {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(1024);
byte[] buf = new byte[1024];
int len = zip.read(buf);
while (len > 0) {
baos.write(buf, 0, len);
len = zip.read(buf);
}
copy(new java.io.ByteArrayInputStream(baos.toByteArray()), newZip);
}
newZip.closeEntry();
entry = (java.util.zip.ZipEntry) zip.getNextEntry();
}
// добавить sheet
clone = new java.util.zip.ZipEntry("xl/worksheets/sheet1.xml");
newZip.putNextEntry(clone);
String xml = this.xmlToString(sheet);
copy(new java.io.ByteArrayInputStream(xml.getBytes(java.nio.charset.Charset.forName("UTF-8"))), newZip);
newZip.closeEntry();
// закрыть поток
newZip.flush();
newZip.close();
base64 = javax.xml.bind.DatatypeConverter.printBase64Binary(copyStream.toByteArray());
return base64;
}
catch (Exception e) {
e.printStackTrace();
}
return "";
}
Шаг 4. Отправить отчёт по почте (используя библиотеку Memo)
ru.medkirov.mail.Memo memo = this.createMemo();
try { memo.setSubject("Ежедневный отчёт по данным приложения Lotus Notes");} catch (Exception e) {}
if (!xlsx.equals(""))
memo.addFile(xlsx, "Ежедневный отчёт.xlsx");
memo.send();
protected ru.medkirov.mail.Memo createMemo() {
ru.medkirov.mail.Memo memo = new ru.medkirov.mail.Memo(this.session);
try {
memo.createMemo();
memo.setSender("Почта сервисов/Почта", "CN=Почта сервисов/O=Почта@Medical", "Агент интеграции<services-mail@acme.corp>", "services-mail@acme.corp");
memo.setRecipients(this.getRecipients());
memo.println("Получены данные");
}
catch (Exception e) {}
return memo;
}
protected java.util.Vector getRecipients() {
try {
java.util.Vector vector = new java.util.Vector();
vector.add("CN=Отчёты по данным приложения/O=Рассылка");
return vector;
}
catch (Exception e) {}
return null;
}
Ссылки
Java в Domino. Вывод в поток присоединённых файлов
Java в Domino. Получение файловых элементов дизайна приложений Domino в виде потоковых данных
Java в Domino. Сохранение файлового потока в документе Notes
Java в Domino. Библиотека Memo
Код библиотеки Memo
|
Опубликовано — 01/31/2021 |
| |
|
|