Разработчикам часто приходится решать задачу отправки больших файлов (отчетов, выгрузок, архивных копий) клиентам или контрагентам. Обычная отправка почтой здесь не подходит, так как почтовые серверы имеют жесткие лимиты на размер вложений. Оптимальное решение — выгрузить файл в облачное хранилище и отправить в письме красивую ссылку на скачивание. Для автоматизации этого процесса можно использовать готовое решение для автоматической выгрузки отчетов в облако по расписанию.
В этой статье мы подробно разберем, как реализовать интеграцию 1С с Яндекс Диском через современный REST API. Мы научимся создавать папки, загружать файлы, публиковать их и программно получать готовую публичную ссылку для скачивания.
В более ранних разработках часто использовался протокол WebDAV (адрес webdav.yandex.ru). Однако в настоящее время крайне не рекомендуется использовать его для автоматизации систем интеграции. Яндекс официально ввел жесткие ограничения скорости (throttling) для WebDAV-соединений сторонних приложений. При передаче больших файлов через WebDAV вы гарантированно столкнетесь с падением скорости до нескольких сотен килобайт в секунду и постоянными ошибками таймаута соединения. Использование REST API (через хост cloud-api.yandex.net) — единственный стабильный и поддерживаемый вендором вариант.
Для взаимодействия с API Яндекс Диска вам потребуется авторизационный токен (OAuth-токен). Получить его можно, зарегистрировав свое приложение на портале Яндекс OAuth.
Важный совет по безопасности: При регистрации приложения запрашивайте доступ только к «Папке приложения» на Диске (права вида yadisk:navigation и yadisk:write к выделенной директории). В этом случае ваше приложение получит доступ не ко всему Диску пользователя, а только к изолированной папке Приложения/<Имя_Вашего_Приложения>. Это гарантирует безопасность личных файлов пользователя при возможных сбоях в программном коде 1С.
Одной из частых ошибок разработчиков при формировании GET-параметров запроса является прямая склейка строк путей, содержащих пробелы, русские буквы или спецсимволы. Если отправить на сервер путь вида Папка/Мой Файл.xml, Яндекс Диск вернет ошибку 400 Bad Request.
Чтобы избежать подобных проблем, все передаваемые параметры путей необходимо преобразовывать в формат URL-кодирования. В платформе 1С для этого используется встроенная функция КодироватьСтроку со специальным режимом кодирования.
Рассмотрим пример правильной подготовки пути к файлу:
ИмяФайлаНаДиске = "Каталог Выгрузки/Файл отчета.xlsx";
КодированныйПуть = КодироватьСтроку(ИмяФайлаНаДиске, СпособКодированияСтроки.КодировкаИменURL);
REST API Яндекс Диска не умеет автоматически создавать вложенные папки «на лету» при загрузке файлов. Если вы попытаетесь загрузить файл в несуществующую папку, сервер вернет ошибку. Поэтому структуру папок необходимо создавать последовательно, уровень за уровнем, используя HTTP-метод PUT. Для тестирования и отладки таких HTTP-запросов к API Яндекс Диска из среды 1С пригодится удобная консоль HTTP-запросов для управляемых форм.
Разберем вспомогательную функцию для создания каталога на Яндекс Диске:
&НаСервере
Функция СоздатьПапкуНаЯндексДиске(Знач ПутьКПапке, Знач Токен) Экспорт
ИмяСервера = "cloud-api.yandex.net";
КодированныйПуть = КодироватьСтроку(ПутьКПапке, СпособКодированияСтроки.КодировкаИменURL);
ОтносительныйURL = "/v1/disk/resources?path=" + КодированныйПуть;
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/json");
Заголовки.Вставить("Authorization", "OAuth " + Токен);
Запрос = Новый HTTPЗапрос(ОтносительныйURL, Заголовки);
Соединение = Новый HTTPСоединение(ИмяСервера, 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
Попытка
// Метод PUT в 1С вызывается через "Записать"
Ответ = Соединение.Записать(Запрос);
// Код 201 означает успешное создание папки
// Код 409 означает, что папка уже существует (это не ошибка для нашего процесса)
Если Ответ.КодСостояния = 201 Или Ответ.КодСостояния = 409 Тогда
Возврат Истина;
Иначе
ЗаписьЖурналаРегистрации("Яндекс.Диск", УровеньЖурналаРегистрации.Предупреждение,,,
"Не удалось создать папку: " + ПутьКПапке + ". Код состояния: " + Ответ.КодСостояния);
Возврат Ложь;
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("Яндекс.Диск", УровеньЖурналаРегистрации.Ошибка,,,
"Ошибка связи с сервером при создании папки: " + ОписаниеОшибки());
Возврат Ложь;
КонецПопытки;
КонецФункции
Процесс загрузки файлов через REST API устроен асинхронно и состоит из двух последовательных этапов:
href полученного JSON-ответа), на который непосредственно нужно физически отправить сам файл.Проанализируем вспомогательную функцию для разбора возвращаемого URL. Поскольку адрес загрузки ведет на специальные распределенные CDN-серверы Яндекса, нам необходимо парсить полученную строку, выделяя имя сервера, путь запроса и протокол.
&НаСервере
Функция РазделитьURL(Знач URL)
ИмяСервера = "";
Путь = "";
Протокол = "";
Порт = 443;
URL = СокрЛП(URL);
Если Лев(URL, 7) = "http://" Тогда
URL = Прав(URL, СтрДлина(URL) - 7);
Протокол = "http";
Порт = 80;
ИначеЕсли Лев(URL, 8) = "https://" Тогда
URL = Прав(URL, СтрДлина(URL) - 8);
Протокол = "https";
Порт = 443;
КонецЕсли;
Индекс = СтрНайти(URL, "/");
Если Индекс Тогда
ИмяСервера = Лев(URL, Индекс - 1);
Путь = Сред(URL, Index);
КонецЕсли;
Индекс = СтрНайти(ИмяСервера, ":");
Если Индекс Тогда
Порт = Число(Сред(ИмяСервера, Индекс + 1));
ИмяСервера = Лев(ИмяСервера, Индекс - 1);
КонецЕсли;
Результат = Новый Структура;
Результат.Вставить("ИмяСервера", ИмяСервера);
Результат.Вставить("Путь", Путь);
Результат.Вставить("Протокол", Протокол);
Результат.Вставить("Порт", Порт);
Возврат Результат;
КонецФункции
Ниже представлен готовый оптимизированный модуль, реализующий полную цепочку взаимодействия: получение адреса загрузки, отправка файла в облако, публикация файла и возврат готовой публичной ссылки. Код оптимизирован под современные стандарты платформы 1С v8.3 (используется прямой метод чтения JSON ПрочитатьJSON без инициализации Фабрики XDTO, а также гарантированно используется защищенное SSL-соединение).
&НаСервере
Функция ВыгрузитьФайлНаЯндексДискИПолучитьСсылку(Знач ПутьКЛокальномуФайлу, Знач Токен) Экспорт
ФайлИсточник = Новый Файл(ПутьКЛокальномуФайлу);
Если Не ФайлИсточник.Существует() Тогда
ВызватьИсключение "Локальный файл не найден на диске: " + ПутьКЛокальномуФайлу;
КонецЕсли;
ИмяФайлаНаДиске = ФайлИсточник.Имя;
ИмяСервераAPI = "cloud-api.yandex.net";
// Подготовка заголовков авторизации
Заголовки = Новый Соответствие;
Заголовки.Вставить("Content-Type", "application/json");
Заголовки.Вставить("Authorization", "OAuth " + Токен);
// Шаг 1. Запрос адреса для загрузки
КодированноеИмя = КодироватьСтроку(ИмяФайлаНаДиске, СпособКодированияСтроки.КодировкаИменURL);
ОтносительныйURL = "/v1/disk/resources/upload?path=" + КодированноеИмя + "&overwrite=true";
HTTPЗапрос = Новый HTTPЗапрос(ОтносительныйURL, Заголовки);
// Обязательно используем защищенное соединение OpenSSL
HTTPСоединение = Новый HTTPСоединение(ИмяСервераAPI, 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
Попытка
HTTPОтвет = HTTPСоединение.Получить(HTTPЗапрос);
Исключение
ВызватьИсключение "Ошибка при запросе адреса загрузки: " + ОписаниеОшибки();
КонецПопытки;
Если HTTPОтвет.КодСостояния <> 200 Тогда
ВызватьИсключение "Яндекс Диск вернул ошибку при запросе адреса загрузки. Код: " + HTTPОтвет.КодСостояния;
КонецЕсли;
// Парсинг ответа JSON
ТелоОтвета = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТелоОтвета);
ДанныеОтвета = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
АдресЗагрузки = ДанныеОтвета.href;
// Шаг 2. Физическая передача файла на полученный URL
СтруктураАдреса = РазделитьURL(АдресЗагрузки);
// Создаем новое соединение с сервером загрузки (CDN)
ЗащитаЗагрузки = ?(СтруктураАдреса.Протокол = "https", Новый ЗащищенноеСоединениеOpenSSL(), Неопределено);
HTTPСоединениеЗагрузки = Новый HTTPСоединение(СтруктураАдреса.ИмяСервера, СтруктураАдреса.Порт,,,,, ЗащитаЗагрузки);
HTTPЗапросЗагрузки = Новый HTTPЗапрос(СтруктураАдреса.Путь, Заголовки);
// Важно: для очень больших файлов загрузка в ОЗУ через ДвоичныеДанные может вызвать нехватку памяти на сервере.
// Рекомендуется загружать файл в поток непосредственно с диска.
HTTPЗапросЗагрузки.УстановитьТелоИзФайла(ПутьКЛокальномуФайлу);
Попытка
// Метод PUT реализуется через метод Записать() в 1С
HTTPОтветЗагрузки = HTTPСоединениеЗагрузки.Записать(HTTPЗапросЗагрузки);
Исключение
ВызватьИсключение "Ошибка при передаче данных файла на сервер Яндекса: " + ОписаниеОшибки();
КонецПопытки;
// Код успешной записи файла на Яндекс.Диск - 201 Created
Если HTTPОтветЗагрузки.КодСостояния <> 201 Тогда
ВызватьИсключение "Не удалось записать файл на Яндекс Диск. Код ответа сервера: " + HTTPОтветЗагрузки.КодСостояния;
КонецЕсли;
// Шаг 3. Публикация загруженного файла
ОтносительныйURLПубликации = "/v1/disk/resources/publish?path=" + КодированноеИмя;
HTTPЗапросПубликации = Новый HTTPЗапрос(ОтносительныйURLПубликации, Заголовки);
Попытка
HTTPОтветПубликации = HTTPСоединение.Записать(HTTPЗапросПубликации);
Исключение
ВызватьИсключение "Ошибка при отправке запроса на публикацию файла: " + ОписаниеОшибки();
КонецПопытки;
Если HTTPОтветПубликации.КодСостояния <> 200 Тогда
ВызватьИсключение "Не удалось опубликовать файл. Код состояния: " + HTTPОтветПубликации.КодСостояния;
КонецЕсли;
// Шаг 4. Получение метаданных файла для извлечения готовой публичной ссылки
ОтносительныйURLМетаданных = "/v1/disk/resources?path=" + КодированноеИмя;
HTTPЗапросМетаданных = Новый HTTPЗапрос(ОтносительныйURLМетаданных, Заголовки);
Попытка
HTTPОтветМетаданных = HTTPСоединение.Получить(HTTPЗапросМетаданных);
Исключение
ВызватьИсключение "Ошибка при получении метаданных опубликованного ресурса: " + ОписаниеОшибки();
КонецПопытки;
Если HTTPОтветМетаданных.КодСостояния <> 200 Тогда
ВызватьИсключение "Не удалось получить метаданные. Код состояния: " + HTTPОтветМетаданных.КодСостояния;
КонецЕсли;
ТелоМетаданных = HTTPОтветМетаданных.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
ЧтениеМетаданных = Новый ЧтениеJSON;
ЧтениеМетаданных.УстановитьСтроку(ТелоМетаданных);
ДанныеМетаданных = ПрочитатьJSON(ЧтениеМетаданных);
ЧтениеМетаданных.Закрыть();
// Извлекаем публичную ссылку из свойства public_url
Если ДанныеМетаданных.Свойство("public_url") Тогда
Возврат ДанныеМетаданных.public_url;
Иначе
ВызватьИсключение "Публичная ссылка на файл не была сформирована Яндексом.";
КонецЕсли;
КонецФункции
При использовании этого алгоритма важно помнить об ограничениях самого Яндекс Диска:
HTTPЗапрос.УстановитьТелоИзФайла() вместо считывания в ДвоичныеДанные. Это критически важное архитектурное решение. Метод считывания данных в память целиком (через объект ДвоичныеДанные) при работе с большими архивами вызовет утечку или переполнение оперативной памяти рабочего процесса rphost на сервере 1С. Метод УстановитьТелоИзФайла() передает данные потоком прямо с жесткого диска, минимизируя нагрузку на оперативную память сервера.Рассмотренный нами подход позволяет полностью автоматизировать обмен файлами с облачным диском силами встроенного языка 1С без применения сторонних утилит и COM-объектов, что обеспечивает высокую кроссплатформенность решения (код будет одинаково стабильно работать как на Windows, так и на Linux-серверах 1С).