1. Обзор
В этом кратком руководстве мы увидим, как выполнять кодирование и декодирование файла PDF с помощью Base64 с использованием Java 8 и кодека Apache Commons .
Но сначала давайте кратко рассмотрим основы Base64.
2. Основы Base64
При отправке данных по сети нам нужно отправить их в двоичном формате. Но если мы отправим только 0 и 1 , разные протоколы транспортного уровня могут интерпретировать их по-разному, и наши данные могут быть повреждены во время передачи.
Итак, чтобы обеспечить переносимость и общие стандарты при передаче бинарных данных, на сцену вышел Base64 .
Поскольку и отправитель, и получатель понимают и соглашаются использовать стандарт, вероятность того, что наши данные будут потеряны или неверно истолкованы, значительно снижается.
Теперь давайте рассмотрим несколько способов применить это к PDF.
3. Преобразование с использованием Java 8
Начиная с Java 8, у нас есть утилита java.util.Base64
, которая предоставляет кодировщики и декодеры для схемы кодирования Base64. Он поддерживает типы Basic, URL-safe и MIME, как указано в RFC 4648 и RFC 2045 .
3.1. Кодирование
Чтобы преобразовать PDF в Base64 , нам сначала нужно получить его в байтах и передать через метод encode
java.util.Base64.Encoder
: ``
byte[] inFileBytes = Files.readAllBytes(Paths.get(IN_FILE));
byte[] encoded = java.util.Base64.getEncoder().encode(inFileBytes);
Здесь IN_FILE
— это путь к нашему входному PDF-файлу.
3.2. Потоковое кодирование
Для больших файлов или систем с ограниченным объемом памяти гораздо эффективнее выполнять кодирование с помощью потока, а не считывать все данные в памяти . Давайте посмотрим, как это сделать:
try (OutputStream os = java.util.Base64.getEncoder().wrap(new FileOutputStream(OUT_FILE));
FileInputStream fis = new FileInputStream(IN_FILE)) {
byte[] bytes = new byte[1024];
int read;
while ((read = fis.read(bytes)) > -1) {
os.write(bytes, 0, read);
}
}
Здесь IN_FILE
— это путь к нашему входному PDF-файлу, а OUT_FILE
— это путь к файлу, содержащему документ в кодировке Base64. Вместо того, чтобы считывать весь PDF-файл в память, а затем кодировать весь документ в памяти, мы считываем до 1 КБ данных за раз и передаем эти данные через кодировщик в OutputStream
.
3.3. Расшифровка
На принимающей стороне мы получаем закодированный файл.
Итак, теперь нам нужно декодировать его, чтобы вернуть наши исходные байты, и записать их в FileOutputStream
, чтобы получить декодированный PDF :
byte[] decoded = java.util.Base64.getDecoder().decode(encoded);
FileOutputStream fos = new FileOutputStream(OUT_FILE);
fos.write(decoded);
fos.flush();
fos.close();
Здесь OUT_FILE
— это путь к нашему PDF-файлу, который нужно создать.
4. Преобразование с использованием Apache Commons
Далее мы будем использовать пакет кодеков Apache Commons, чтобы добиться того же. Он основан на RFC 2045 и предшествует реализации Java 8, которую мы обсуждали ранее. Итак, когда нам нужно поддерживать несколько версий JDK (включая устаревшие) или поставщиков, это удобно в качестве стороннего API.
4.1. Мавен
Чтобы иметь возможность использовать библиотеку Apache, нам нужно добавить зависимость к нашему pom.xml
:
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
Последнюю версию вышеперечисленного можно найти на Maven Central .
4.2. Кодирование
Шаги такие же, как и для Java 8, за исключением того, что на этот раз мы передаем исходные байты методу encodeBase64 класса
org.apache.commons.codec.binary.Base64
:
byte[] inFileBytes = Files.readAllBytes(Paths.get(IN_FILE));
byte[] encoded = org.apache.commons.codec.binary.Base64.encodeBase64(inFileBytes);
4.3. Потоковое кодирование
Потоковое кодирование не поддерживается этой библиотекой.
4.4. Расшифровка
Опять же, мы просто вызываем метод decodeBase64
и записываем результат в файл:
byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(encoded);
FileOutputStream fos = new FileOutputStream(OUT_FILE);
fos.write(decoded);
fos.flush();
fos.close();
5. Тестирование
Теперь мы проверим нашу кодировку и декодирование с помощью простого теста JUnit:
public class EncodeDecodeUnitTest {
private static final String IN_FILE = // path to file to be encoded from;
private static final String OUT_FILE = // path to file to be decoded into;
private static byte[] inFileBytes;
@BeforeClass
public static void fileToByteArray() throws IOException {
inFileBytes = Files.readAllBytes(Paths.get(IN_FILE));
}
@Test
public void givenJavaBase64_whenEncoded_thenDecodedOK() throws IOException {
byte[] encoded = java.util.Base64.getEncoder().encode(inFileBytes);
byte[] decoded = java.util.Base64.getDecoder().decode(encoded);
writeToFile(OUT_FILE, decoded);
assertNotEquals(encoded.length, decoded.length);
assertEquals(inFileBytes.length, decoded.length);
assertArrayEquals(decoded, inFileBytes);
}
@Test
public void givenJavaBase64_whenEncodedStream_thenDecodedStreamOK() throws IOException {
try (OutputStream os = java.util.Base64.getEncoder().wrap(new FileOutputStream(OUT_FILE));
FileInputStream fis = new FileInputStream(IN_FILE)) {
byte[] bytes = new byte[1024];
int read;
while ((read = fis.read(bytes)) > -1) {
os.write(bytes, 0, read);
}
}
byte[] encoded = java.util.Base64.getEncoder().encode(inFileBytes);
byte[] encodedOnDisk = Files.readAllBytes(Paths.get(OUT_FILE));
assertArrayEquals(encoded, encodedOnDisk);
byte[] decoded = java.util.Base64.getDecoder().decode(encoded);
byte[] decodedOnDisk = java.util.Base64.getDecoder().decode(encodedOnDisk);
assertArrayEquals(decoded, decodedOnDisk);
}
@Test
public void givenApacheCommons_givenJavaBase64_whenEncoded_thenDecodedOK() throws IOException {
byte[] encoded = org.apache.commons.codec.binary.Base64.encodeBase64(inFileBytes);
byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(encoded);
writeToFile(OUT_FILE, decoded);
assertNotEquals(encoded.length, decoded.length);
assertEquals(inFileBytes.length, decoded.length);
assertArrayEquals(decoded, inFileBytes);
}
private void writeToFile(String fileName, byte[] bytes) throws IOException {
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(bytes);
fos.flush();
fos.close();
}
}
Как мы видим, мы сначала прочитали входные байты в методе @BeforeClass
и в обоих наших методах @Test
проверили, что:
закодированные
идекодированные
байтовые массивы имеют разную длинуinFileBytes
идекодированные
байтовые массивы имеют одинаковую длину и одинаковое содержимое.
Конечно, мы также можем открыть декодированный PDF-файл, который мы создали, и убедиться, что его содержимое такое же, как и в файле, который мы предоставили в качестве входных данных.
6. Заключение
В этом кратком руководстве мы узнали больше об утилите Java Base64 .
Мы также видели примеры кода для преобразования PDF в и из Base64 с использованием Java 8 и кодека Apache Commons . Интересно, что реализация JDK намного быстрее, чем у Apache.
Как всегда, исходный код доступен на GitHub .