Как в WebView2 загрузить файл в поле типа "file"?

Здравствуйте. В прошлом был найден способ автоматического заполнения полей с типом "file" в webbrowser. В этом контексте я предоставил пример кода, который демонстрирует этот процесс:

```csharp
async Task PopulateInputFile_poster(string file_poster)
{
    ExecuteScript("document.getElementById(\"fln1\").focus();");

    // задержка перед выполнением SendKey для появления диалогового окна выбора файла
    var sendKeyTask = Task.Delay(1000).ContinueWith((_) =>
    {
        // этот код выполняется, когда диалоговое окно видно
        SendKeys.Send(disk + "" + "poster.webp" + "{ENTER}");
    }, TaskScheduler.FromCurrentSynchronizationContext());

    ExecuteScript("document.getElementById(\"fln1\").click();"); // открывает диалоговое окно

    await sendKeyTask;

    // задержка перед продолжением, чтобы диалоговое окно могло закрыться
    await Task.Delay(1000);
}

async Task Populate()
{
    ExecuteScript("document.getElementById(\"fln1\").focus();");
    await PopulateInputFile_poster("fln1");
}
```

Этот метод также работает в webview2. Однако меня интересует, существуют ли какие-либо альтернативные способы, поскольку в приведенном примере я заполняю только одно поле. У меня есть несколько проектов, где таких полей больше десяти, и хотелось бы сократить время на выполнение этой операции, если это возможно.
  • 2 сентября 2024 г. 18:23
Ответы на вопрос 2
В WebView2, как и в других браузерах, загрузка файла через элемент типа `input` с атрибутом `type="file"` может быть затруднена, так как браузеры (включая WebView2) имеют встроенные механизмы безопасности для предотвращения автоматического взаимодействия с диалогами выбора файлов.

Однако есть несколько подходов, которые можно рассмотреть для оптимизации загрузки файлов в несколько полей типа `file`. Вот несколько вариантов:

### 1. Использование JavaScript и Blob
Если вы работаете с файловыми данными, вы можете попробовать использовать JavaScript для создания `File` объектов и программно вставить их в нужные элементы. Например, можно создать Blob-объект с содержимым файла и использовать его для заполнения поля:

```javascript
function populateFileInput(fileInputId, fileName, fileContent) {
    var blob = new Blob([fileContent], { type: 'application/octet-stream' });
    var file = new File([blob], fileName);

    var dataTransfer = new DataTransfer();
    dataTransfer.items.add(file);
    document.getElementById(fileInputId).files = dataTransfer.files;
}
```

В C# вы можете вызвать эту функцию для каждого нужного поле, передавая соответствующее содержимое файла.

### 2. Распараллеливание операций
Если у вас есть множество полей типа `file`, вы можете распараллелить вызовы к JavaScript через `ExecuteScriptAsync()`, чтобы они срабатывали одновременно, вместо последовательного выполнения:

```csharp
private async Task PopulateInputFileAsync(string elementId, string filePath)
{
    // Здесь код для обработки создания DataTransfer, как выше
    await ExecuteScriptAsync($"populateFileInput('{elementId}', '{Path.GetFileName(filePath)}', '{Convert.ToBase64String(File.ReadAllBytes(filePath))}');");
}

public async Task PopulateMultipleFilesAsync(List<(string elementId, string filePath)> fileInputs)
{
    var tasks = fileInputs.Select(fileInput => PopulateInputFileAsync(fileInput.elementId, fileInput.filePath));
    await Task.WhenAll(tasks);
}
```

### 3. Использование `SetValue` с обработкой событий
Вы также можете попробовать установить значения в поле напрямую через JavaScript, но будьте осторожны с обработкой событий, которые могут требоваться для правильной работы:

```javascript
function setValueForFileInput(fileInputId, filePath) {
    var event = new Event('change', { bubbles: true });
    var input = document.getElementById(fileInputId);
    input.value = filePath; // Обратите внимание, что это может не сработать из-за ограничений безопасности
    input.dispatchEvent(event);
}
```

Помните, что этот метод может не сработать, поскольку прямой доступ к свойству `value` предполагает, что данные загружаются из файловой системы, что является нарушением безопасности.

### Заключение
К сожалению, по состоянию на текущую дату, полностью автоматической загрузки файлов через поля `<input type="file">` из-за ограничений безопасности большинства браузеров не существует. Каждый из предложенных методов может помочь сократить время, но также может потребоваться доработка в зависимости от конкретной ситуации.

Обязательно протестируйте каждый метод для вашей конкретной задачи и сценария использования.
В WebView2 есть несколько способов автоматизировать заполнение полей типа file, но основное ограничение связано с безопасностью браузера, так как доступ к файловым полям строго ограничен и браузеры не позволяют напрямую задавать значения для этих полей через JavaScript. Ваш текущий подход с использованием SendKeys имитирует пользовательский ввод, что позволяет обходить это ограничение, но действительно может быть медленным при большом количестве полей. 

Вот несколько возможных альтернатив для ускорения процесса:

Пакетная обработка файлов: Вместо того, чтобы обрабатывать каждое поле по одному, можно попытаться сгенерировать команды для всех полей и запустить их параллельно. Например, можно использовать параллельные задачи для вызова метода PopulateInputFile_poster сразу для нескольких полей:
async Task PopulateMultipleFiles(Dictionary<string, string> fileMappings)
{
    var tasks = fileMappings.Select(async kvp =>
    {
        var fileId = kvp.Key;
        var filePath = kvp.Value;
        ExecuteScript($"document.getElementById(\"{fileId}\").focus();");

        var sendKeyTask = Task.Delay(1000).ContinueWith((_) =>
        {
            SendKeys.Send(filePath + "{ENTER}");
        }, TaskScheduler.FromCurrentSynchronizationContext());

        ExecuteScript($"document.getElementById(\"{fileId}\").click();");

        await sendKeyTask;
        await Task.Delay(1000);
    });

    await Task.WhenAll(tasks);
}

var fileMappings = new Dictionary<string, string>
{
    { "fln1", "poster.webp" },
    { "fln2", "image2.jpg" },
    // Добавьте остальные файлы
};

await PopulateMultipleFiles(fileMappings);

WebDriver / Selenium: Хотя WebView2 более легкий и быстрый, использование Selenium с WebDriver может предоставить более гибкие возможности автоматизации, в том числе возможность работы с множеством полей через драйвер:
IWebDriver driver = new EdgeDriver();
IWebElement fileInput = driver.FindElement(By.Id("fln1"));
fileInput.SendKeys("C:\\path\\to\\poster.webp");

Это может быть быстрее для массового заполнения полей.

Использование JavaScript и локальных API: Если у вас есть контроль над страницей, можно разработать API для загрузки файлов через JavaScript, но это требует модификации самой страницы, что не всегда возможно.

Оптимизация задержек: Поиграйте с настройками задержек в вашем коде. Возможно, в некоторых случаях не нужно ждать целую секунду между шагами.

Если у вас есть возможность контролировать или изменять целевые страницы, другие подходы могут включать в себя создание вспомогательных API для загрузки файлов напрямую.
Похожие вопросы