О чём эта страница
- Что такое «сырые байты» (raw bytes) и чем они отличаются от текста
- Как Oracle хранит бинарные данные (RAW, BLOB)
- Примеры преобразований: STRING ↔ RAW, HEX, Base64
- Пример отправки BLOB в HTTP API через UTL_HTTP (поблочно)
- Что такое Base64 при передаче в API и в чем разница?
Что такое «сырые байты» (raw bytes)
«Сырые байты» — это последовательность восьмибитных значений (octets). У них нет смысла, пока вы явно не укажете, как их интерпретировать (кодировка текста, формат изображения, структура числа и т.д.).
Ключевые моменты:
- Это не текст: текст — это байты + кодировка (UTF‑8, CP1251 и т.п.).
- Без знания кодировки результат интерпретации байтов как текста непредсказуем.
- В Oracle: RAW/RAW(n) — для небольших бинарных фрагментов, BLOB — для больших потоков.
- Для отображения или передачи бинарных данных часто используют HEX или Base64.
- Преобразования выполняются явными функциями: HEX↔RAW, STRING↔RAW (с указанием charset), Base64 encode/decode.
Примеры: строка → байты → HEX / Base64
SQL/PLSQL примеры для Oracle.
1) Получение RAW (UTF‑8 байты) и его HEX-представления
SELECT UTL_I18N.STRING_TO_RAW('Привет, мир!', 'AL32UTF8') FROM dual;
-- В SQL-клиентах RAW обычно показывают в виде HEX.
SELECT RAWTOHEX(UTL_I18N.STRING_TO_RAW('Привет, мир!', 'AL32UTF8')) AS hex_bytes FROM dual;
-- -> D09FD180D0B8D0B2D0B5D1822C20D0BCD0B8D18021
Это шестнадцатеричное представление байтов, которое SQL Developer или sqlplus отображают для RAW.
2) Декодирование HEX → строка (UTF‑8)
SELECT UTL_I18N.RAW_TO_CHAR(HEXTORAW('D09FD180D0B8D0B2D0B5D1822C20D0BCD0B8D18021'), 'AL32UTF8') AS text
FROM dual;
-- -> Привет, мир!
3) Base64 — удобный формат для передачи бинарных данных в JSON/XML
-- Base64 текстовую строку нужно получить из RAW как VARCHAR2
SELECT UTL_RAW.CAST_TO_VARCHAR2(
UTL_ENCODE.BASE64_ENCODE(
UTL_I18N.STRING_TO_RAW('Привет, мир!', 'AL32UTF8')
)
) AS b64_text
FROM dual;
-- -> 0J/RgNC40LLQtdGCLCDQvNC40YAh
-- Если посмотреть без CAST, вы увидите HEX-репрезентацию RAW с base64-байтами
SELECT RAWTOHEX(UTL_ENCODE.BASE64_ENCODE(UTL_I18N.STRING_TO_RAW('Привет, мир!', 'AL32UTF8')))
FROM dual;
Короткое сравнение размеров (пример):
SELECT
LENGTH(UTL_I18N.STRING_TO_RAW('Привет, мир!', 'AL32UTF8')) AS bytes_len, -- байтовая длина
LENGTH(RAWTOHEX(UTL_I18N.STRING_TO_RAW('Привет, мир!', 'AL32UTF8'))) AS hex_len, -- HEX длина
LENGTH(UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_I18N.STRING_TO_RAW('Привет, мир!', 'AL32UTF8')))) AS b64_len
FROM dual;
-- HEX = 2 * bytes, Base64 ≈ 4/3 * bytes
Немного о кодировках в Oracle
- AL32UTF8 — имя кодировки UTF‑8 в Oracle.
- Windows‑1251 обычно обозначается как
CL8MSWIN1251
в Oracle.
Пример конвертации строк:
SELECT CONVERT('Привет', 'CL8MSWIN1251', 'AL32UTF8') FROM dual;
При преобразовании строк ↔ RAW используйте UTL_I18N.STRING_TO_RAW
и UTL_I18N.RAW_TO_CHAR
с явным указанием кодировки.
Разбор HEX-строки D09FD180D0B8D0B2D0B5D1822C20D0BCD0B8D18021
Это UTF‑8 в hex. Разбиение по байтам и соответствие:
- D0 9F → U+041F → П
- D1 80 → U+0440 → р
- D0 B8 → U+0438 → и
- D0 B2 → U+0432 → в
- D0 B5 → U+0435 → е
- D1 82 → U+0442 → т
- 2C → ,
- 20 → пробел
- D0 BC → м
- D0 B8 → и
- D1 80 → р
- 21 → !
Результат: Привет, мир!
Декодировать в Oracle:
SELECT UTL_I18N.RAW_TO_CHAR(HEXTORAW('D09FD180D0B8D0B2D0B5D1822C20D0BCD0B8D18021'), 'AL32UTF8') FROM dual;
Пример: отправка BLOB в HTTP API (поблочно) — пояснения
Ниже — рабочий пример PL/SQL, который собирает CLOB в BLOB, отправляет его поблочно через UTL_HTTP.write_raw и читает ответ как RAW‑чанки.
DECLARE
p_source_data clob := 'some_source_data';
l_auth_header varchar2(2000) := 'your diadoc Auth header data...';
v_resp_clob CLOB;
l_api_base_url varchar2(2000) := 'http://api.diadoc.ru/v4/endpoint';
l_blob BLOB;
-- переменные для LOB/HTTP
l_req UTL_HTTP.req;
l_resp UTL_HTTP.resp;
l_buffer RAW(32767); -- буфер для чтения BLOB
l_amount BINARY_INTEGER;
l_offset INTEGER := 1;
l_buf_len BINARY_INTEGER;
BEGIN
-- Создаём временный BLOB и заполняем его данными из p_source_data
DBMS_LOB.createtemporary(l_blob, TRUE);
DBMS_LOB.OPEN(l_blob, DBMS_LOB.LOB_READWRITE);
DECLARE
v_pos INTEGER := 1;
v_chunk VARCHAR2(32767 CHAR);
v_raw RAW(32767);
v_chunk_len INTEGER := 32767;
BEGIN
LOOP
v_chunk := DBMS_LOB.SUBSTR(p_source_data, v_chunk_len, v_pos);
EXIT WHEN v_chunk IS NULL OR LENGTH(v_chunk) = 0;
-- Если нужна определённая кодировка для байтов — заменить UTL_RAW.CAST_TO_RAW на UTL_I18N.STRING_TO_RAW(v_chunk, 'AL32UTF8'/'CL8MSWIN1251')
v_raw := UTL_RAW.CAST_TO_RAW(v_chunk);
DBMS_LOB.WRITEAPPEND(l_blob, UTL_RAW.LENGTH(v_raw), v_raw);
v_pos := v_pos + v_chunk_len;
END LOOP;
DBMS_LOB.CLOSE(l_blob);
END;
-- Начинаем HTTP-запрос
l_req := UTL_HTTP.begin_request(l_api_base_url, 'POST', 'HTTP/1.1');
UTL_HTTP.set_header(l_req, 'Content-Type', 'application/octet-stream'); -- отправляем сырые байты
UTL_HTTP.set_header(l_req, 'Accept', 'application/json');
UTL_HTTP.set_header(l_req, 'Authorization', l_auth_header);
UTL_HTTP.set_header(l_req, 'Transfer-Encoding', 'chunked'); -- учесть поддержку на стороне сервера
-- Пишем BLOB по частям
l_amount := DBMS_LOB.getlength(l_blob);
l_offset := 1;
l_buf_len := 2000; -- безопасный размер для write_raw; можно увеличить до 32767 при тестировании
WHILE l_offset <= l_amount LOOP
IF l_offset + l_buf_len - 1 > l_amount THEN
l_buf_len := l_amount - l_offset + 1;
END IF;
DBMS_LOB.READ(l_blob, l_buf_len, l_offset, l_buffer);
UTL_HTTP.write_raw(l_req, l_buffer);
l_offset := l_offset + l_buf_len;
END LOOP;
-- Получаем ответ
l_resp := UTL_HTTP.get_response(l_req);
-- Чтение ответа как RAW‑чанки и сбор в CLOB с корректной кодировкой
DECLARE
v_resp_chunk_raw RAW(32767);
v_resp_chunk_clob CLOB;
v_resp_chunk_len INTEGER;
v_charset VARCHAR2(50) := 'CL8MSWIN1251'; -- установить согласно ожидаемой кодировке ответа
BEGIN
DBMS_LOB.createtemporary(v_resp_clob, TRUE);
LOOP
UTL_HTTP.read_raw(l_resp, v_resp_chunk_raw, 32767);
v_resp_chunk_len := UTL_RAW.LENGTH(v_resp_chunk_raw);
EXIT WHEN v_resp_chunk_len IS NULL OR v_resp_chunk_len = 0;
-- Корректное преобразование: RAW -> VARCHAR2 (используя DB charset) и конвертация в AL32UTF8
v_resp_chunk_clob := CONVERT(UTL_RAW.CAST_TO_VARCHAR2(v_resp_chunk_raw), v_charset, 'AL32UTF8');
DBMS_LOB.writeappend(v_resp_clob, LENGTH(v_resp_chunk_clob), v_resp_chunk_clob);
END LOOP;
-- здесь можно обработать v_resp_clob (парсинг JSON и т.д.)
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
UTL_HTTP.end_response(l_resp);
WHEN OTHERS THEN
dbms_output.put_line(sqlerrm);
dbms_output.put_line(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE());
END;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(sqlerrm);
dbms_output.put_line(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE());
BEGIN
UTL_HTTP.end_response(l_resp);
EXCEPTION WHEN OTHERS THEN NULL; END;
-- освободить временные LOB
BEGIN IF DBMS_LOB.ISTEMPORARY(l_blob) = 1 THEN DBMS_LOB.FREETEMPORARY(l_blob); END IF; END;
END;
Что такое Base64 при передаче в API и в чем разница?
Base64 — это текстовое представление бинарных данных, удобное для включения в JSON, XML или другие текстовые протоколы. Когда вы передаёте файл, изображение или произвольный BLOB через API, чаще всего вам нужно преобразовать байты в строку, потому что JSON и XML ожидают текст, а не «сырые» байты.
Кроме того, это необходимо, поскольку передаваемые данные могут содержать байты, которые не являются допустимыми символами в текстовых форматах (например, амперсанды или специальные символы в XML и JSON). Base64 помогает избежать проблем с кодировкой и интерпретацией таких данных.
Ключевые различия и заметки:
- Формат и размер
- HEX кодирует каждый байт двумя шестнадцатеричными символами → длина строки = 2 × bytes.
- Base64 кодирует каждые 3 байта в 4 символа → длина ≈ 4/3 × bytes (с возможным паддингом '=').
-
Base64 обычно экономичнее по размеру по сравнению с HEX и поэтому предпочтительнее для передачи больших бинарных фрагментов.
-
Семантика и совместимость
- Base64 — де-факто стандарт для встраивания бинарных данных в JSON и XML (MIME, data URI, многие SDK и сервисы ожидают именно base64).
-
HEX чаще используется для представления хешей (sha/sha1/md5) и отладки, но реже для встраивания файлов в JSON.
-
Безопасность
-
Base64 — это кодирование, а не шифрование. Данные остаются читаемыми после декодирования любым, кто имеет доступ. Для защиты используйте HTTPS и/или шифрование до кодирования.
-
URL и URL-safe
-
Стандартный Base64 использует символы "+" и "/" и паддинг "="; для URL иногда используют variant (base64-url), где "+"→"-" и "/"→"_".
-
Практические нюансы при потоковой обработке
- При поблочном кодировании BLOB в Base64 полезно читать BLOB кусками, размер которых кратен 3, чтобы не вводить лишние паддинги в промежуточных частях; UTL_ENCODE.BASE64_ENCODE работает с RAW-чанками и возвращает RAW.
Примеры в Oracle:
1) Получить Base64-строку из текста (UTF-8):
SELECT UTL_RAW.CAST_TO_VARCHAR2(
UTL_ENCODE.BASE64_ENCODE(
UTL_I18N.STRING_TO_RAW('Привет, мир!', 'AL32UTF8')
)
) AS b64_text
FROM dual;
-- -> 0J/RgNC40LLQtdGCLCDQvNC40YAh
Практика отправки через JSON: формируем объект с base64-полем и отправляем Content-Type: application/json. Не используйте заголовок Content-Transfer-Encoding: base64
для обычного HTTP/REST — этот заголовок исторически используется в MIME.
{
"fileName": "image.png",
"content": "iVBORw0KGgoAAAANS..."
}