|
| 1 | +import java.util.ArrayList; |
| 2 | + |
| 3 | +public class Base64 { |
| 4 | + |
| 5 | + private static int buffer; //здесь я храню символы и разбиваю блоки по 6 бит |
| 6 | + private static int BufferBitCounter = 0; // счетчик бит в буффере |
| 7 | + private static int numOfBits = 0; // число бит в строке которая передается на кодирование. |
| 8 | + public static ArrayList<Byte> ListOfBytes = new ArrayList<>(); |
| 9 | + |
| 10 | + // Это алфавит Base64; |
| 11 | + private final static String abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; |
| 12 | + |
| 13 | + |
| 14 | + public static String StartEncode(byte[] inputArray) { |
| 15 | + String result = ""; |
| 16 | + |
| 17 | + // число бит, которое занимает кодируемое сообщение |
| 18 | + numOfBits = inputArray.length * 8; |
| 19 | + |
| 20 | + // число бит должно быть кратным 6 (так как сообщение разбиваем на 6 бит) |
| 21 | + if (numOfBits % 6 == 0) |
| 22 | + result += Base64Encode(inputArray); |
| 23 | + else if (numOfBits % 6 == 2) { |
| 24 | + result += Base64Encode(inputArray) + "=="; |
| 25 | + } |
| 26 | + |
| 27 | + else if (numOfBits % 6 == 4) { |
| 28 | + result += Base64Encode(inputArray) + "="; |
| 29 | + } |
| 30 | + |
| 31 | + return result; |
| 32 | + } |
| 33 | + |
| 34 | + private static String Base64Encode(byte[] byteArr) { |
| 35 | + |
| 36 | + buffer = 0; |
| 37 | + BufferBitCounter = 0; |
| 38 | + |
| 39 | + String result = ""; // здесь будем хранить результат |
| 40 | + int temp6Bit = 0; // Временная переменная - в ней сохраняем 6-бит из буфера, путем смещения вправо |
| 41 | + |
| 42 | + for (int i = 0; i < byteArr.length; ++i) { |
| 43 | + //for (byte symbol : byteArr) { |
| 44 | + byte symbol = byteArr[i]; |
| 45 | + |
| 46 | + /* т.к. в Java перменная byte знаковая и ее диапазон -127 ... 127 |
| 47 | + будем использовать переменную short. В Java байты могут быть отрицат. величины, |
| 48 | + поэтому приводим их в нормальное значение добавив 256. */ |
| 49 | + |
| 50 | + short symbl = symbol; |
| 51 | + if (symbl < 0 ) symbl += 256; |
| 52 | + |
| 53 | + buffer = (buffer << 8); // сдвигаем влево для добавления битов символа - 8 бит |
| 54 | + // (каждый новый символ занимает 8 бит) |
| 55 | + buffer = (buffer | symbl); // добавляем символ в буфер |
| 56 | + BufferBitCounter += 8; // Прибавляем счетчик бит в буффере на 8 бит (добавили символ - он занимает 8 бит) |
| 57 | + |
| 58 | + /* Если количество бит в передаваеваемом сообщнии не кратно 6-ти (мы кодируем сообщение по 6 бит) |
| 59 | + тогда нам нужно добавить в самый конец цепочки байтов |
| 60 | + (поэтому мы здесь проверяем что i == byteArr.length -1) |
| 61 | + необходимые пустые биты, чтобы длина была кратной 6-ти |
| 62 | + */ |
| 63 | + if (i == byteArr.length -1 && numOfBits % 6 == 2) { |
| 64 | + buffer = buffer << 4; |
| 65 | + BufferBitCounter += 4;} |
| 66 | + else if (i == byteArr.length -1 && numOfBits % 6 == 4 ) { |
| 67 | + buffer = buffer << 2; |
| 68 | + BufferBitCounter += 2;} |
| 69 | + |
| 70 | + // если наш счетчик в буфере больше или равен 6 - разбиваем буфер на блок из 6 бит |
| 71 | + while (BufferBitCounter >= 6) |
| 72 | + { |
| 73 | + /* получаем блок из 6-ти бит сдвигая вправо (Насколько нужно сдвинуть? Мы это определяем с помощью счетчика бит |
| 74 | + * мы сдвигаем столько бит, сколько показывает счетчик BufferBitCounter МИНУС 6, |
| 75 | + * т.к. 6 бит мы оставляем для блока в 6 бит, который мы кодируем в символ MIME64 */ |
| 76 | + temp6Bit = buffer >> (BufferBitCounter - 6); |
| 77 | + BufferBitCounter -= 6; // После того как мы получили блок из 6 бит для MIME64 у нас буфер на 6 бит меньше |
| 78 | + |
| 79 | + // Очищаем буффер, если счетчик использованных бит равен нулю |
| 80 | + if (BufferBitCounter == 0) { |
| 81 | + buffer = 0; |
| 82 | + } |
| 83 | + else { |
| 84 | + // очищаем буфер от выброшенных битов -- буфер имеет тип int - 32 бит |
| 85 | + buffer = (int) (buffer << (32 - BufferBitCounter)); |
| 86 | + buffer = (int) (buffer >>> (32 - BufferBitCounter)); |
| 87 | + } |
| 88 | + |
| 89 | + result += abc.charAt(temp6Bit); // присваиваем с результату закодированный в MIME64 символ |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + return result; // Выводим закодированный в MIME64 символ на экран |
| 94 | + } |
| 95 | + |
| 96 | + |
| 97 | + public static byte[] StartDecode(byte[] bytes) { |
| 98 | + |
| 99 | + // Вначале работы методов startMimeEncode и startMimeDecode инициализируем переменные нулями |
| 100 | + buffer = 0; // здесь я храню символы и разбиваю блоки по 6 бит |
| 101 | + BufferBitCounter = 0; // счетчик бит в буффере |
| 102 | + ListOfBytes.clear(); |
| 103 | + |
| 104 | + byte mimeAbcPosition; // Нам необходимо определить позицию символа в MIME-Алфавите |
| 105 | + char mimeLetter; |
| 106 | + |
| 107 | + |
| 108 | + // Перебираем поисмвольно тест закодированный в MIME64. Напомню, мы его перевели в массив байт, для удобства |
| 109 | + for (int i = 0; i < bytes.length; i++) |
| 110 | + { |
| 111 | + // Нам нужно теперь байт из массива перевести в символ (char) |
| 112 | + mimeLetter = (char)(bytes[i]); |
| 113 | + if (mimeLetter == '=' ) break; |
| 114 | + // и определить позицию символа в массиве алфавите MIME64 |
| 115 | + mimeAbcPosition = (byte)abc.indexOf(mimeLetter); |
| 116 | + // теперь вызываем метод декодирования MIME64 отправляя туда позицию символа в алфавите MIME64 |
| 117 | + Mime64Decode(mimeAbcPosition); |
| 118 | + } |
| 119 | + |
| 120 | + Byte[] byteArray = new Byte[1]; |
| 121 | + byteArray = ListOfBytes.toArray(byteArray); |
| 122 | + byte[] result = new byte[byteArray.length]; |
| 123 | + for (int i = 0; i < byteArray.length; i++) |
| 124 | + result[i] = byteArray[i]; |
| 125 | + |
| 126 | + return result; |
| 127 | + } |
| 128 | + |
| 129 | + /* |
| 130 | + * Метод принимает параметр - byte Symbol, который определяет положение символа в массиве-алфавите MIME64 |
| 131 | + * В процессе работы метода - в буфере накапливаются биты |
| 132 | + */ |
| 133 | + public static void Mime64Decode(byte Symbol) |
| 134 | + { |
| 135 | + byte temp8bit = 0; // Временная переменная, для вырезания из буфера блоков по 8 бит (привычные нам символы в кодировке win-1251 занимают 8 бит) |
| 136 | + |
| 137 | + /* Сдвигаем буфер влево на 6 бит для добавления битов нового числа-позиции в алфавите |
| 138 | + * (каждый новый символ (т.е. позиция в алфавите MIME) занимает 6 бит) */ |
| 139 | + buffer = (buffer << 6); |
| 140 | + buffer = (buffer | Symbol); // добавляем новые биты в буфер в освобожденное место (сдвинули выше, освободив для битов место) |
| 141 | + BufferBitCounter += 6; // Теперь счетчик битов на 6 битов больше |
| 142 | + |
| 143 | + // В случае если счетчик битов >= 8 разбиваем буфер на блоки из 8 битов |
| 144 | + while (BufferBitCounter >= 8) |
| 145 | + { |
| 146 | + /* получаем блок из 8-ти бит сдвигая вправо (Насколько нужно сдвинуть? Мы это определяем с помощью счетчика бит |
| 147 | + * мы сдвигаем столько бит, сколько показывает счетчик BufferBitCounter МИНУС 8, |
| 148 | + * т.к. 8 бит мы оставляем для блока в 8 бит, который мы декодируем в символ windows-1251 */ |
| 149 | + temp8bit = (byte)(buffer >> (BufferBitCounter - 8)); |
| 150 | + BufferBitCounter -= 8; // Теперь счетчик битов в буфере уменьшается на 8 |
| 151 | + buffer = (buffer << (16 - BufferBitCounter)); // очищаем буфер от выброшенных битов -- буфер имеет тип ushort - 16 бит |
| 152 | + buffer = (buffer >> (16 - BufferBitCounter)); |
| 153 | + |
| 154 | + // добавляем декодированные 8битные кусочки в "массив" байтов (List<byte>), который затем будет преобразован в текст в формате UTF-8 |
| 155 | + ListOfBytes.add(temp8bit); |
| 156 | + } |
| 157 | + } |
| 158 | + |
| 159 | +} |
0 commit comments