Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WinForms, .Net Framework Новый топик    Ответить
 избавиться от MemoryStream  [new]
vb_sub
Member

Откуда:
Сообщений: 983
Всем привет, у меня есть метод, который тянет из MS SQL несколько pdf потоком в виде VarBinary, с помощью PDFSharp все склеивается в один pdf.
public async Task<Stream> GetFileByStream(int id)
        {
            try
            {       
                PdfDocument outputPDFDocument = new PdfDocument();

                await foreach (var stream in getStreams())
                {
                    using PdfDocument inputPDFDocument = PdfReader.Open(stream, PdfDocumentOpenMode.Import);
                    outputPDFDocument.Version = inputPDFDocument.Version;

                    foreach (PdfPage page in inputPDFDocument.Pages)
                        outputPDFDocument.AddPage(page);
                }

                Stream pdfStream = new MemoryStream();
                    outputPDFDocument.Save(pdfStream);
                return pdfStream;
            }
            catch (Exception ex)
            {
                throw;
            }
                     
            async  IAsyncEnumerable<Stream> getStreams()
            {  
                using (SqlConnection connection = new SqlConnection(_connectionString))
                {
                    await connection.OpenAsync();
                    using (SqlCommand command = new SqlCommand(@" select pdffile.fileContent from pdffile
                    where insertedTaskId = @id", connection))
                    {
                        command.Parameters.AddWithValue("id", id);
                        using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
                        {                            
                            while (await reader.ReadAsync())
                            {
                                if (!(await reader.IsDBNullAsync(0)))
                                {
                                    using (Stream data = reader.GetStream(0))
                                    {
                                        using var memoryStream = new MemoryStream();
                                        await data.CopyToAsync(memoryStream);
                                        yield return memoryStream;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } 

    public async Task<IActionResult> GetPdfByTaskFromStreamAsync([FromQuery] int id)
    {
       var result= await _PdfFileRepository.GetFileByStream(id);
       return new FileStreamResult(result, "application/octet-stream") { FileDownloadName = "file.pdf" };
    }

Проблема в том, что мне приходится использовать оберточный MemoryStream, который нормально так отжирает память-хотелось бы избавиться от него. Причем объем потребления памяти увеличивается непропорционально размеру файлов. То есть если нужно из базы получить 10 файлов по 1 Мб и склеить его в один файл 10 мб, то метод отъедает +150 мб Ram.
Если не использовать MemoryStream, а сразу возвращать
Stream data = reader.GetStream(0)

, то при инициализации Pdf-документа
using PdfDocument inputPDFDocument = PdfReader.Open(stream, PdfDocumentOpenMode.Import);
, получаю Exception "Specified method is not supported". Это происходит при попытке получить PdfReader SqlSequentialStream.GetLenght(). Это из-за того, что SqlSequentialStream Not Seekable и не имеет длины.
Вот собственно вопрос- как можно зарефактрить?
Спасибо
18 окт 21, 16:32    [22385112]     Ответить | Цитировать Сообщить модератору
 Re: избавиться от MemoryStream  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6257
vb_sub,

можно заменить MemoryStream на RecyclableMemoryStream - авторы (то бишь майкрософт) обещают от этого кучу профитов:

  • Eliminate Large Object Heap allocations by using pooled buffers
  • Incur far fewer gen 2 GCs, and spend far less time paused due to GC
  • Avoid memory leaks by having a bounded pool size
  • Avoid memory fragmentation
  • Allow for multiple ways to read and write data that will avoid extraneous allocations
  • Provide excellent debuggability and logging
  • Provide metrics for performance tracking

Не проверял, действительно ли это так, но тот же EPPlus в одной из своих версий стал использовать именно эту библиотеку (т.к. у них были многим известные проблемы с памятью при больших объёмах документов).
18 окт 21, 18:24    [22385200]     Ответить | Цитировать Сообщить модератору
 Re: избавиться от MemoryStream  [new]
vb_sub
Member

Откуда:
Сообщений: 983
Сон Веры Павловны,
пробовал эту тему- на мое удивление с ним стало только хуже.
19 окт 21, 08:16    [22385320]     Ответить | Цитировать Сообщить модератору
 Re: избавиться от MemoryStream  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6257
vb_sub
Сон Веры Павловны,
пробовал эту тему- на мое удивление с ним стало только хуже.

Ну тогда, если для PdfReader нужен только размер данных потока, получать этот размер в запросе (функция datalength), и написать свою обёртку, реализующую Stream, и читающую данные мелкими блоками.
А если нужен действительно полноценный seekable stream, то тут от хранения всего содержимого бинарного поля на клиенте не отвертеться никак.
19 окт 21, 08:28    [22385322]     Ответить | Цитировать Сообщить модератору
 Re: избавиться от MemoryStream  [new]
vb_sub
Member

Откуда:
Сообщений: 983
Печально
19 окт 21, 11:40    [22385402]     Ответить | Цитировать Сообщить модератору
 Re: избавиться от MemoryStream  [new]
pation
Member

Откуда: Москва
Сообщений: 4512
vb_sub,

using
2 ноя 21, 03:57    [22391230]     Ответить | Цитировать Сообщить модератору
Все форумы / WinForms, .Net Framework Ответить