Загрузить файл
  1. base64hub
  2. Base64 в коде
  3. Java
Код

Base64 в Java

Начиная с Java 8, java.util.Base64 умеет стандартный, URL-safe и MIME Base64, в том числе потоком. Все результаты ниже получены на Java 21.

Закодировать и декодировать строку

java.util.Base64 работает с байтами. Преобразуйте строки с явно указанной кодировкой: getBytes() без аргументов берёт кодировку платформы по умолчанию, а до Java 18 это не всегда был UTF-8.

import java.nio.charset.StandardCharsets;
import java.util.Base64;

String enc = Base64.getEncoder()
        .encodeToString("héllo wörld".getBytes(StandardCharsets.UTF_8));
System.out.println(enc);   // aMOpbGxvIHfDtnJsZA==

String dec = new String(Base64.getDecoder().decode(enc), StandardCharsets.UTF_8);
System.out.println(dec);   // héllo wörld

Экземпляры Base64.Encoder и Base64.Decoder потокобезопасны, их можно хранить в поле static final.

URL-safe Base64 с дополнением и без

getUrlEncoder() использует - и _. Дополнение включено по умолчанию; withoutPadding() возвращает новый кодировщик, который его не пишет:

byte[] d = "<<???>>".getBytes(StandardCharsets.UTF_8);

Base64.getUrlEncoder().encodeToString(d);                   // PDw_Pz8-Pg==
Base64.getUrlEncoder().withoutPadding().encodeToString(d);  // PDw_Pz8-Pg

new String(Base64.getUrlDecoder().decode("PDw_Pz8-Pg"), StandardCharsets.UTF_8);
// <<???>>

Декодеры принимают данные и с дополнением, и без, поэтому getDecoder().decode("aGk") тоже сработает и вернёт hi. Сегменты JWT – это Base64URL без дополнения; что и когда использовать, объясняет статья Base64 и Base64URL.

MIME-кодировщик и декодер

getMimeEncoder() пишет строки по 76 символов, разделённые \r\n, как требует RFC 2045. После последней строки перевод строки не добавляется.

String mime = Base64.getMimeEncoder().encodeToString("x".repeat(60).getBytes());
// eHh4eHh4...eHh4  (76 chars) + "\r\n" + "eHh4"

Base64.getMimeEncoder(64, "\n".getBytes());   // PEM-style 64-char lines

Длина строки округляется вниз до кратной 4. getMimeDecoder() – самый снисходительный: он игнорирует разделители строк и любые другие символы вне алфавита.

Base64.getMimeDecoder().decode("aGVs\r\nbG8K!");   // bytes of "hello\n"

Кодировщик MIME Base64 выдаёт такой же формат в браузере.

Закодировать и декодировать файл

Небольшие файлы:

import java.nio.file.*;

byte[] file = Files.readAllBytes(Path.of("hello.txt"));
System.out.println(Base64.getEncoder().encodeToString(file));   // aGVsbG8K

Files.write(Path.of("copy.txt"), Base64.getDecoder().decode("aGVsbG8K"));

Большие файлы потоком через wrap()

Encoder.wrap(OutputStream) и Decoder.wrap(InputStream) кодируют и декодируют на лету, так что расход памяти не зависит от размера файла:

try (InputStream in = new FileInputStream("big.bin");
     OutputStream out = Base64.getEncoder().wrap(new FileOutputStream("big.b64"))) {
    in.transferTo(out);
}

try (InputStream in = Base64.getDecoder().wrap(new FileInputStream("big.b64"));
     OutputStream out = new FileOutputStream("big.out")) {
    in.transferTo(out);
}

Закрыть оборачивающий поток обязательно: последнюю группу и дополнение кодировщик пишет в close(). Забудете – и пропадёт последняя группа: до четырёх символов вместе с дополнением. На тестовом файле в 300 КБ результат в точности совпал с encodeToString, а после кодирования и декодирования байты остались идентичными. InputStream.transferTo требует Java 9; в Java 8 копируйте циклом с буфером.

Проверка и исключения

Базовый и URL-декодер строгие и бросают java.lang.IllegalArgumentException. Сообщение говорит, что пошло не так, а символы выводятся в hex:

Вход для getDecoder()Сообщение
PDw_Pz8-Pg==Illegal base64 character 5f
"aGVs\nbG8K"Illegal base64 character a
aGk=!!Input byte array has incorrect ending byte at 4
aGk==Input byte array has incorrect ending byte at 4
aInput byte[] should at least have 2 bytes for base64 bytes

Так что проверка сводится к try/catch:

static boolean isBase64(String s) {
    try {
        Base64.getDecoder().decode(s);
        return true;
    } catch (IllegalArgumentException e) {
        return false;
    }
}

Одна брешь: декодер принимает ненулевые биты дополнения, поэтому aGl= без возражений декодируется в hi. Если нужны только канонические строки, закодируйте результат заново и сравните. Валидатор Base64 показывает, какой символ мешает, а в статье частые ошибки Base64 разобран каждый тип сбоев.

Частые подводные камни

  • Кодировка платформы. "text".getBytes() и new String(bytes) используют кодировку по умолчанию. В Windows до Java 17 включительно это обычно была Windows-1252, которая ломает текст не в ASCII. С Java 18 по умолчанию UTF-8 (JEP 400), но явный StandardCharsets.UTF_8 ничего не стоит.
  • Не тот декодер для входных данных. URL-safe строка в базовом декодере даёт Illegal base64 character 5f или 2d. Строка в формате MIME – Illegal base64 character a или d.
  • encode() и encodeToString(). encode() возвращает byte[] из ASCII-символов. Если напечатать такой массив, увидите что-то вроде [B@1b6d3586, а не Base64.
  • Незакрытый обёрнутый поток. Без close() вывод кодировщика обрезан.
  • Надежда, что Base64 скрывает данные. Не скрывает; см. кодирование и шифрование.

Версии

  • Java 8: появился java.util.Base64 с базовым, URL и MIME вариантами, withoutPadding() и wrap().
  • Java 9: InputStream.transferTo, которым мы копируем потоки выше.
  • Java 11: javax.xml.bind.DatatypeConverter.printBase64Binary пропал вместе с удалением JAXB из JDK.
  • Java 18: UTF-8 стал кодировкой по умолчанию.

В Kotlin доступно всё перечисленное, а кроме того, там есть собственный мультиплатформенный API, о нём рассказано на странице Base64 в Kotlin.

Вопросы и ответы

Что значит «Illegal base64 character 5f»?

Число – это проблемный символ в hex. 5f – это _, а 2d – -: значит, вы декодируете URL-safe Base64 через getDecoder(). Возьмите getUrlDecoder(). a – перевод строки, 20 – пробел; используйте getMimeDecoder() или удалите пробельные символы.

Стоит ли ещё пользоваться sun.misc.BASE64Encoder или javax.xml.bind.DatatypeConverter?

Нет. sun.misc.BASE64Encoder – внутренний API, которого нет в современных JDK, а javax.xml.bind убрали из JDK в Java 11. java.util.Base64 заменяет оба.

Принимает ли декодер Java Base64 без дополнения?

Да. Base64.getDecoder().decode("aGk") возвращает байты hi. Декодеры допускают отсутствие дополнения, но отвергают неправильное дополнение.

Как закодировать в Base64 на Android?

С API level 26 доступен java.util.Base64. Код постарше использует android.util.Base64.encodeToString(bytes, Base64.NO_WRAP); без NO_WRAP он добавляет переносы строк.

Читайте также