Разархивируйте БОЛЬШОЙ zip-файл в хранилище файлов Azure без исключения «Недостаточно памяти» - PullRequest
1 голос
/ 27 марта 2019

Вот с чем я имею дело ...

Некоторые процессы (вне нашего контроля) иногда помещают zip-файл в каталог в хранилище файлов Azure. Это имя каталога InBound. Допустим, файл с именем bigbook.zip помещен в папку InBound.

Мне нужно создать приложение-функцию Azure, которое запускается каждые 5 минут и ищет zip-файлы в каталоге InBound. Если что-то существует, то один за другим мы создаем новый каталог с тем же именем, что и zip-файл в другом каталоге (называемый InProcess). Так что в нашем примере я бы создал InProcess/bigbook.

Теперь внутри InProcess/bigbook, мне нужно распаковать bigbook.zip. Таким образом, к моменту завершения процесса InProcess/bigbook будет содержать все содержимое bigbook.zip.

Обратите внимание: эта функция, которую я создаю, является консольным приложением, которое будет работать как приложение-функция Azure. Таким образом, не будет доступа к файловой системе (по крайней мере, насколько мне известно, в любом случае.) Нет возможности скачать zip-файл, распаковать его, а затем переместить содержимое.

У меня дьявол времени, выясняющий, как сделать это только в памяти. Независимо от того, что я пытаюсь, я продолжаю сталкиваться с исключением Out Of Memory. Сейчас я просто делаю это на моем локальном хосте, работающем в режиме отладки в Visual Studio 2017, .NET 4.7. В этом случае я не могу преобразовать тестовый zip-файл размером 515 069 КБ.

Это была моя первая попытка:

    private async Task<MemoryStream> GetMemoryStreamAsync(CloudFile inBoundfile)
    {
        MemoryStream memstream = new MemoryStream();
        await inBoundfile.DownloadToStreamAsync(memstream).ConfigureAwait(false);
        return memstream;
    }

И это (с большими надеждами) была моя вторая попытка, думать, что DownloadRangeToStream будет работать лучше, чем просто DownloadToStream.

    private MemoryStream GetMemoryStreamByRange(CloudFile inBoundfile)
    {
        MemoryStream outPutStream = new MemoryStream();
        inBoundfile.FetchAttributes();
        int bufferLength = 1 * 1024 * 1024;//1 MB chunk
        long blobRemainingLength = inBoundfile.Properties.Length;
        long offset = 0;
        while (blobRemainingLength > 0)
        {
            long chunkLength = (long)Math.Min(bufferLength, blobRemainingLength);

            using (var ms = new MemoryStream())
            {
                inBoundfile.DownloadRangeToStream(ms, offset, chunkLength);
                lock (outPutStream)
                {
                    outPutStream.Position = offset;
                    var bytes = ms.ToArray();
                    outPutStream.Write(bytes, 0, bytes.Length);
                }
            }

            offset += chunkLength;
            blobRemainingLength -= chunkLength;
        }
        return outPutStream;
    }

Но в любом случае, у меня проблемы с памятью. Я полагаю, это потому, что MemoryStream, который я пытаюсь создать, становится слишком большим?

Как еще я могу заняться этим? И снова загрузка файла zip не является вариантом, поскольку в конечном итоге это будет приложение-функция Azure. Я также уверен, что использование FileStream также не вариант, так как для этого требуется локальный путь к файлу, которого у меня нет. (У меня есть только удаленный URL Azure)

Могу ли я как-нибудь создать временный файл в той же учетной записи хранилища Azure, в которой находится файл zip, и направить файл zip в этот временный файл, а не в поток памяти? (Мысли вслух.)

Цель состоит в том, чтобы получить поток в ZipArchive, используя:

ZipArchive archive = new ZipArchive(stream)

И оттуда я могу извлечь все содержимое. Но достижение этой точки без ошибок памяти доказывает настоящий провал.

Есть идеи?

Ответы [ 2 ]

1 голос
/ 31 мая 2019

Использование общего хранилища файлов Azure - для меня это единственный способ без загрузки всего ZIP в память.Я протестировал ZIP-файл объемом 3 ГБ (с тысячами файлов или большим файлом внутри), а память / ЦП была низкой и стабильной.Надеюсь, это поможет!

var zipFiles = _directory.ListFilesAndDirectories()
    .OfType<CloudFile>()
    .Where(x => x.Name.ToLower().Contains(".zip"))
    .ToList();

foreach (var zipFile in zipFiles)
{
    using (var zipArchive = new ZipArchive(zipFile.OpenRead()))
    {
        foreach (var entry in zipArchive.Entries)
        {
            if (entry.Length > 0)
            {
                CloudFile extractedFile = _directory.GetFileReference(entry.Name);

                using (var entryStream = entry.Open())
                {
                    byte[] buffer = new byte[16 * 1024];
                    using (var ms = extractedFile.OpenWrite(entry.Length))
                    {
                        int read;
                        while ((read = entryStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            ms.Write(buffer, 0, read);
                        }
                    }
                }
            }
        }
    }               
}
0 голосов
/ 29 марта 2019

Я бы посоветовал вам использовать снимки памяти, чтобы понять, почему у вас не хватает памяти в Visual Studio. Вы можете использовать учебник в этой статье , чтобы найти виновника. Локальная разработка с меньшим файлом может помочь вам продолжить работу, если на вашей машине просто не хватает памяти.

Когда это делается в Azure, размер узла в плане потребления составляет , ограниченный 1,5 ГБ общей памяти . Если вы ожидаете получать файлы большего размера, вам следует обратиться к одному из других планов обслуживания приложений, которые дают вам больше памяти для работы.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...