Какова наилучшая стратегия для загрузки большого файла с использованием HttpClient в телефонном аппарате с низкой памятью?

Я пытаюсь загрузить файлы с использованием аналогичного подхода HttpClient: как загрузить сразу несколько файлов  в телефоне Windows.

using (var content = new MultipartFormDataContent())
{
    content.Add(CreateFileContent(imageStream, "image.jpg", "image/jpeg"));
    content.Add(CreateFileContent(signatureStream, "image.jpg.sig", "application/octet-stream"));

    var response = await httpClient.PostAsync(_profileImageUploadUri, content);
    response.EnsureSuccessStatusCode();
}

private StreamContent CreateFileContent(Stream stream, string fileName, string contentType)
{
    var fileContent = new StreamContent(stream);
    fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") 
    { 
        Name = "\"files\"", 
        FileName = "\"" + fileName + "\""
    }; // the extra quotes are key here
    fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);            
    return fileContent;
}

Это прекрасно работает при загрузке небольших файлов. Если я попытался загрузить более крупный файл (скажем, > 50 МБ) в устройстве с низким уровнем (512 Мб памяти) он выдает System.OutOfMemoryException. Я использовал диагностические инструменты для мониторинга потребления памяти и заметил, что память экспоненциально растет во время вызова PostAsync. Кажется, он копирует весь контент в память. Прямо сейчас у нас нет поддержки каналов в апи.

Какова наилучшая стратегия для загрузки большого файла с использованием HttpClient в телефонном аппарате с низкой памятью?

Ответ 1

Сделайте многопользовательский POST вручную - без помощи MultipartFormDataContent

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

Вам не обязательно делать это с помощью методов async. Решение представляет собой "ручное управление буферизацией 4k". Но async был бы идеальным, будучи наиболее эффективным потоком/ЦП.

Здесь другая рекомендуемая ссылка, чтобы понять, как кодовое многостраничное сообщение. И еще один для понимания протокола, вот пример того, что отправляется по потоку, иллюстрирующий маркеры границ

Кроме того, в архитектуре я предпочитаю загружать файлы отдельно для любых (форм) данных. Это позволяет полностью отказаться от многопользовательской проводки, делая ваши API-интерфейсы атомарными и простыми. У вас может быть служба, которая просто хранит загруженный файл и возвращает URL-адрес или идентификатор. Затем этот URL-адрес или идентификатор можно будет ссылаться на ваши данные и опубликовать впоследствии.

Ответ 2

Я не являюсь экспертом в MultipartFormDataContent (и он может разделить контент под водой), но подсказка может заключаться в том, что вы делите данные, которые хотите отправить.

Затем отправьте другие блоки и восстановите их на принимающей стороне.

например. разделите изображения в меньших блоках (например, 10 Мб или меньше в зависимости от использования памяти) и отправьте эти

Таким образом, это может привести к тому, что цикл for будет перемещаться по блокам.

foreach (byte[] block in dividedContent)
{
    using (var content = new MultipartFormDataContent())
    {
        content.Add(block);

        var response = await httpClient.PostAsync(_profileImageUploadUri, content);
        response.EnsureSuccessStatusCode();
    }
}

возможно, что-то подобное решит вашу проблему:)