...

Передача и получение файлов с помощью внешних API

Тема в разделе "Примеры решений и дополнительных модулей", создана пользователем vyimova, 28 авг 2023.

  1. vyimova

    vyimova Участник

    Иногда бывает нужно передать в сторонние внешние API на вход не только строки или числа, но и параметры более сложных типов, например, файлы. А также бывает необходимость от API получить файлы из ответа.

    Допустим, у нас есть сервис, который принимает на вход один файл или массив файлов, и возвращает также файл и информацию о нем. Те файлы, которые мы хотим передать, хранятся в ELMA365 в контекстных переменных.

    Для примера будем использовать сервис ConvertApi (https://www.convertapi.com/) и его функции по конвертации docx в pdf и zip. Чтобы можно было пользоваться функциями этого сервиса, нужно создать на его сайте личный кабинет и получить в нем код авторизации (secret).

    Предварительно можно протестировать API-запросы через Postman. Всю информацию по запросам можно найти в документации на сайте нужного сервиса.

    Для нашего сервиса нам нужны два метода:

    Пример POST-запроса через Postman для конвертации docx в pdf и проверки работоспособности API:

    [​IMG]

    Если ввели в «Params» значение «Secret» и указали в «Body - form-data» значения для полей «File» и «StoreFile» корректно, то получим в результате ответ как на скриншоте выше. Отметим, что для этого метода вкладывается один файл. Для других методов могут вкладывается два и более файлов в колонке «Value».

    Попробуем реализовать аналогичный функционал в ELMA365. Создадим модуль «Конвертация файлов». В Настройках модуля создадим переменную «secret» (тип «Строка»), в которую добавим с формы модуля значение secret, которое получили ранее в личном кабинете.

    [​IMG]

    [​IMG]

    Далее создадим два Custom Activity (Действие в БП).

    [​IMG]

    Конвертация docx в pdf

    Создаем первый Custom Activity «Конвертация в pdf». В нем мы напишем функцию для вызова метода ConvertApi по конвертации docx в pdf.

    В контексте создадим переменные:

    • «Входящий docx» с кодом «input_file_docx» и типом «Файлы – Один»,
    • «Итоговый pdf» с кодом «output_file_pdf» и типом «Файлы – Один»,
    • «Лог конвертации в pdf» с кодом «log_converting_pdf» и типом «Строка – Текст».
    Напишем функцию action() в сценарии для отправки файлов в запросе:
    Код:
    
    async function action(): Promise<void> {
        
    // создание объекта типа FormData
        
    let form_data = new FormData();

        if (
    Context.data.input_file_docx) {
            try {
                
    // получение значений для form_data: имени файла, URL, ArrayBuffer
                
    let filename = (await Context.data.input_file_docx.fetch()).data.__name;
                
    let fileurl await Context.data.input_file_docx.getDownloadUrl();
                
    let arrBuffer await (await fetch(fileurl)).arrayBuffer();
                
    // формирование form_data
                // добавление файла
                
    form_data.append("File"arrBufferfilename);
                
    // добавление строки
                
    form_data.append("StoreFile""true");
            }
            catch (
    err_input) {
                throw new 
    Error(`Ошибка при формировании form_data: ${err_input}`);
            }

            
    // вызов метода, в body передаем составленный ранее form_data
            
    let res await fetch(`https://v2.convertapi.com/convert/docx/to/pdf?Secret=${Namespace.params.data.secret}`, {
                
    method'POST',
                
    bodyform_data
            
    });

            if (!
    res.ok) {
                throw new 
    Error(`Ошибка при отправке запроса: ${res.status} ${res.statusText}`);
            }
            else {
                try {
                    
    // обработка JSON-ответа
                    
    let json_result await res.json();
                    
    Context.data.log_converting_pdf JSON.stringify(json_result);
                    
    // формирование файла с помощью createFromLink() и его запись в контекстную переменную типа "Файл"
                    
    Context.data.output_file_pdf await Context.fields.output_file_pdf.createFromLink(json_result.Files[0].FileNamejson_result.Files[0].Url);
                }
                catch (
    err_output) {
                    throw new 
    Error(`Ошибка при формировании ответа: ${err_output}`);
                }
            }
        }
    }


    Наша основная задача - сформировать form_data, который составит body запроса.

    Для этого мы получаем из контекстной переменной «input_file_docx» значения имени файла и URL файла. А также с помощью URL определяем ArrrayBuffer. Затем пользуемся функцией append(), добавляя в form_data нужные нам для API параметры: файл и поле «StoreFile». Для каждого API будут свои поля.

    Выполним запрос, обратившись к значению secret, находящегося в параметрах модуля, через Namespace.params.

    Подсказка: Аналогичным образом как задали secret можно задать и значение URL API и обратиться к нему таким же образом через Namespace.params. А также при необходимости задать и значения других параметров, которые могут потребоваться в запросе и которые было бы удобно настраивать через форму модуля.

    Обязательно укажем form_data как значение body.

    Если запрос выполнен успешно, то получим ответ. Сперва сконвертируем его в JSON. Затем, чтобы сохранить файл в переменную типа «Файл» модуля, воспользуемся функцией createFromLink(). Подробнее про нее можно почитать в tssdk. Для создания файла в системе и его записи в переменную, укажем в параметрах функции имя и URL файлы, полученные из JSON ответа API.

    В случае, если API возвращает не ссылку на файл, а сам файл, можно воспользоваться этим кодом вместо createFromLink():

    Код:
    
    let buff await res.arrayBuffer();
    Context.data.output_file_pdf await Context.fields.output_file_pdf.create(`output-${Date.now()}.pdf`, buff);


    В таком случае нам нужно получить ArrayBuffer из ответа вместо JSON и далее с помощью функции create() создать файл на основе ArrayBuffer.

    Конвертация docx в zip

    Создаем второй Custom Activity «Конвертация в zip». В нем мы напишем функцию для вызова метода ConvertApi по конвертации docx в zip.

    В контексте создадим переменные:

    • «Входящие docx» с кодом «input_files_docx» и типом «Файлы – Несколько»,
    • «Итоговый zip» с кодом «output_file_zip» и типом «Файлы – Один»,
    • «Лог конвертации в zip» с кодом «log_converting_zip» и типом «Строка – Текст».
    Напишем функцию action() в сценарии для отправки файлов в запросе:

    Код:
    
    async function action(): Promise<void> {
        
    // создание объекта типа FormData
        
    let form_data = new FormData();

        if (
    Context.data.input_files_docx) {
            try {
                
    // цикл по всем файлам массива input_files_docx
                
    for (let one_file of Context.data.input_files_docx) {
                    
    // получение значений для form_data: имени файла, URL, ArrayBuffer
                    
    let filename = (await one_file.fetch()).data.__name;
                    
    let fileurl await one_file.getDownloadUrl();
                    
    let arrBuffer await (await fetch(fileurl)).arrayBuffer();
                    
    // формирование form_data
                    // добавление каждого файла
                    
    form_data.append("Files"arrBufferfilename);
                }
                
    // добавление строки
                
    form_data.append("StoreFile""true");
            }
            catch (
    err_input) {
                throw new 
    Error(`Ошибка при формировании массива входных файлов: ${err_input}`);
            }

            
    // вызов метода, в body передаем составленный ранее form_data
            
    let res await fetch(`https://v2.convertapi.com/convert/any/to/zip?Secret=${Namespace.params.data.secret}`, {
                
    method'POST',
                
    bodyform_data
            
    });

            if (!
    res.ok) {
                throw new 
    Error(`Ошибка при отправке запроса: ${res.status} ${res.statusText}`);
            }
            else {
                try {
                    
    // обработка JSON-ответа
                    
    let json_result await res.json();
                    
    Context.data.log_converting_zip JSON.stringify(json_result);
                    
    // формирование файла с помощью createFromLink() и его запись в контекстную переменную типа "Файл"
                    
    Context.data.output_file_zip await Context.fields.output_file_zip.createFromLink(json_result.Files[0].FileNamejson_result.Files[0].Url);
                }
                catch (
    err_output) {
                    throw new 
    Error(`Ошибка при формировании ответа: ${err_output}`);
                }
            }
        }
    }
    Основное отличие от кода предыдущего Custom Activity заключается в том, что сейчас на вход подается массив файлов, а не один файл. Поэтому при формировании form_data необходимо выполнить цикл foreach, чтобы обработать все файлы из массива файлов «input_files_docx» и каждый из них поместить в form_data.

    Для тестирования можно написать простой бизнес-процесс (далее по тексту: БП), где будем отправлять в модуль файлы из переменных и получать сконвертированные файлы на выходе. Исходные файлы можно заполнять, например, на форме стартового события БП.

    Карта БП:

    [​IMG]

    Контекст БП:

    [​IMG]

    Пример настроек блока модуля:

    [​IMG]

    Пример настроек задачи Получения файлов:

    [​IMG]

    Запустим БП, заполним поля с исходными файлами и убедимся, что после этого БП и конвертация выполнятся успешно, и нам придет задача с нужными файлами.

    [​IMG]

    Таким образом можно работать с внешними API, принимающими и отдающими файлы.
    Последнее редактирование: 31 авг 2023