Работа с электронными таблицами Excel на продвинутом уровне может потребовать анализа и управления метаданными или смежными функциями, такими как безопасность. Java и специальные библиотеки, такие как Apache POI, упрощают и ускоряют извлечение этой информации, которая может быть особенно полезна для целей аудита и отслеживания, а также для ее изменения или работы с ней.
В этом посте я приведу примеры того, как можно использовать Java для упрощения и оптимизации метаданных Excel и управления безопасностью, и как это можно объединить в простом настольном приложении с пользовательским интерфейсом. Пост и фрагменты кода ссылаются на приложение, которое я создал для этой цели: вы можете ознакомиться с полным кодом на Github. Любые отзывы, отчеты об ошибках или дополнения к коду всегда приветствуются.
В следующих разделах представлен обзор приложения, а затем подробно рассмотрены функции резервного копирования, средство просмотра метаданных и код обработки защиты листа, а также способы согласования этих операций в приложении пользовательского интерфейса.
Обзор приложения обработчика Excel
Приложение Java, используемое в примерах в этом посте, имеет пользовательский интерфейс, созданный с помощью JavaFX. Архитектура проекта соответствует шаблону Model-View-Controller:
- Модель данных содержит классы, которые работают непосредственно с файлами Excel.
- Представление состоит из главного окна, внешний вид которого определяется в файле .fxml.
- Контроллер обрабатывает взаимодействие пользователя с элементами пользовательского интерфейса и при необходимости вызывает методы из классов модели данных.
Приложение использует несколько классов из пакета java.io
(Input/Output), которые необходимы для работы с файлами и потоками ввода/вывода данных. Более того, некоторые классы в модели данных основаны на библиотеках Apache POI 5.2.3. Apache POI — очень полезный инструмент, поскольку он позволяет с легкостью создавать, изменять и извлекать данные из файлов Excel.
Основы: резервное копирование файлов
Хотя операции, описанные в этой статье, не обрабатывают данные в базовых файлах напрямую, всегда рекомендуется создавать резервную копию файла на всякий случай. Это делается в модели данных приложения с помощью класса с именем FileBackupHandler
, который содержит метод для создания копии файла и еще один для проверки правильности заданного пути к файлу.
Метод backupFile
создает резервную копию указанного файла. Цель этого метода — гарантировать, что исходный файл не будет перезаписан или потерян, если что-то пойдет не так в процессе модификации. Этот метод принимает исходный путь к файлу в качестве аргумента и создает новый файл с другим именем в том же каталоге, но с добавленной к нему строкой «_backup», чтобы отличить его от оригинала. Таким образом, если в процессе модификации что-то пойдет не так, исходный файл можно будет восстановить из резервной копии.
public static boolean backupFile(String filePath) { if(!isValidFilePath(filePath)) { System.out.println("Please input a correct file path."); return false; } else { try { // Get the file name and directory of the original file File originalFile = new File(filePath); String fileName = originalFile.getName(); String fileDirectory = originalFile.getParent(); // Create the backup file name by adding "_backup" to it int extensionIndex = fileName.lastIndexOf("."); String backupFileName = fileName.substring(0, extensionIndex) + "_backup" + fileName.substring(extensionIndex); String backupFilePath = fileDirectory + File.separator + backupFileName; // Copy the original file to a backup file Files.copy(Paths.get(filePath), Paths.get(backupFilePath), StandardCopyOption.REPLACE_EXISTING); return true; } catch (IOException e){ e.printStackTrace(); return false; } } }
Извлечение метаданных
Извлечение метаданных обрабатывается классом MetaDataHandler
. Он содержит метод displayProperties
, который создает экземпляр класса Apache POI XSSFWorkbook
и вызывает соответствующие методы для извлечения свойств файла Excel. Результат каждого вызова добавляется к экземпляру StringBuilder
, который затем преобразуется в строку и возвращается в качестве вывода. Затем контроллер использует содержимое строки для заполнения нового окна пользовательского интерфейса, отображающего метаданные для пользователя.
Например, в следующем примере показано, как получить информацию об авторе из книги — аналогичный синтаксис можно использовать для большинства других доступных свойств:
XSSFWorkbook workbook = new XSSFWorkbook(inputStream); author = workbook.getProperties().getCoreProperties().getCreator();
Защита рабочего листа
При работе с Excel важно понимать различные уровни безопасности. Чтобы правильно защитить файл Excel, вы должны зашифровать его содержимое, т.е. установить пароль на уровне файла. Это позволит читать и записывать весь файл только тем пользователям, которые знают пароль.
С другой стороны, обычно используемая защита на уровне рабочего листа или рабочей книги не предназначена для обеспечения безопасности, поскольку это просто способ ограничить доступ для записи, но при этом разрешить доступ для чтения даже без пароля. Если вы забудете этот пароль на уровне рабочего листа или окажетесь в ситуации, когда, например, единственный человек, который знал пароль, покинул компанию, вы можете знать, что есть несколько способов обойти это без необходимости иметь дело с алгоритм хеширования. Один из распространенных способов решить эту проблему — создать файл .zip из файла Excel, найти файлы .xml в папках .zip и удалить строку защиты. Однако при этом существует риск внесения нежелательных изменений в файлы .xml, что может поставить под угрозу целостность электронной таблицы и ее данных.
Следующий метод Java является более простым способом выполнения этой операции почти мгновенно. В этом примере он реализован как часть класса ExcelPasswordRemover
в модели данных. Код перебирает листы в файле Excel (представленном экземпляром XSSFWorkbook
) и передает значение null
в метод protectSheet
для каждого листа. При этом существующее значение пароля перезаписывается null
и, следовательно, эффективно удаляется. Аналогичная строка кода делает то же самое на уровне рабочей книги. Очевидно, что вы должны выполнять такие операции только в том случае, если у вас есть на это разрешение. И, как упоминалось выше, это работает только для защиты листов или книг и не будет работать, если сам файл зашифрован, что является правильным способом защиты файла Excel от нежелательного доступа. Используя синтаксис, аналогичный показанному в следующем примере, можно управлять защитой рабочего листа, просто передав требуемое значение в качестве параметра методу protectSheet
.
public static boolean removeExcelPassword(String filePath) throws IOException { if(!isValidFilePath(filePath)) { System.out.println("Please input a correct file path."); return false; } FileInputStream inputStream = new FileInputStream(new File(filePath)); String extension = filePath.substring(filePath.lastIndexOf(".")+1); if(extension.equalsIgnoreCase("xlsx") || extension.equalsIgnoreCase("xls")){ XSSFWorkbook workbook = new XSSFWorkbook(inputStream); // Remove password protection from all sheets in the workbook int numberOfSheets = workbook.getNumberOfSheets(); if (numberOfSheets > 0) { for (int i = 0; i < numberOfSheets; i++) { if(workbook.getSheetAt(i).getProtect()) { workbook.getSheetAt(i).protectSheet(null); } } // Remove password protection from the workbook workbook.setWorkbookPassword(null, HashAlgorithm.sha512); // Save the workbook to the same file path FileOutputStream outputStream = new FileOutputStream(filePath); workbook.write(outputStream); outputStream.close(); workbook.close(); return true; } } else return false; return false; }
Объединяя все вместе: роль контролера
Класс Controller в программе JavaFX действует как посредник между пользовательским интерфейсом (представлением) и базовыми данными (моделью). Роль контроллера заключается в обработке действий пользователя, таких как нажатия кнопок, и координации соответствующих операций в модели.
В этом случае пользовательский интерфейс содержит кнопки для выполнения действий, описанных в разделах выше. При нажатии кнопки в пользовательском интерфейсе событие обнаруживается контроллером и инициирует вызов соответствующего метода в модели. Методы в контроллере дополнительно отображают оповещения для пользователя, чтобы сообщить либо об успешном завершении операции, либо о том, что действие не может быть выполнено из-за ошибок.
Причина выбора этого шаблона проектирования для этого приложения заключается в том, что за счет разделения логики программы на модель данных и контроллер приложение может быть проще в обслуживании и расширении. Классы моделей можно тестировать и модифицировать независимо друг от друга, а пользовательский интерфейс можно обновлять, не влияя на базовые функции. Таким образом, эта модульная конструкция упрощает создание сложных программ и повышает надежность, ремонтопригодность и масштабируемость.