Ошибка при копировании содержимого в поток.?

#c# #asp.net-mvc #asynchronous #asp.net-web-api

#c# #asp.net-mvc #асинхронный #asp.net-веб-api

Вопрос:

Я экспортирую файл Excel с помощью WebAPI, но он выдает исключение, и я не знаю почему. Я погуглил и не нашел никакого решения.

Исключение:

     <Error>
    <Message>An error has occurred.</Message>
    <ExceptionMessage>Error while copying content to a stream.</ExceptionMessage>
    <ExceptionType>System.Net.Http.HttpRequestException</ExceptionType>
    <StackTrace>
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at Microsoft.AspNet.WebApi.Extensions.Compression.Server.BaseServerCompressionHandler.<HandleCompression>d__17.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() at Microsoft.AspNet.WebApi.Extensions.Compression.Server.BaseServerCompressionHandler.<SendAsync>d__15.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at System.Web.Http.HttpServer.<SendAsync>d__0.MoveNext()
    </StackTrace>
    <InnerException>
    <Message>An error has occurred.</Message>
    <ExceptionMessage>Cannot access a closed Stream.</ExceptionMessage>
    <ExceptionType>System.ObjectDisposedException</ExceptionType>
    <StackTrace>
    at System.IO.__Error.StreamIsClosed() at System.IO.MemoryStream.get_Position() at System.Net.Http.StreamToStreamCopy.StartAsync()
    </StackTrace>
    </InnerException>
    </Error>
  

Мой код :

 [AllowAnonymous]
[HttpGet]
[Route("api/AddPatient/ExportToExcel")]
public HttpResponseMessage ExportToExcel()
{
    try
    {
        MemoryStream dataStream = Utility.GetFile();

        HttpResponseMessage httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK);
        httpResponseMessage.Content = new StreamContent(dataStream);
        httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
        httpResponseMessage.Content.Headers.ContentDisposition.FileName = "Test.xlsx";
        httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

        return httpResponseMessage;
    }
    catch (Exception ex)
    {
        throw;
    }
}  
  

Функция GetFile, как показано ниже, для получения потока памяти Excel
Примечание: используя NPOI, я создаю файл Excel

 public static MemoryStream GetFile()
        {
            MemoryStream stream =null;
            XSSFWorkbook wb = new XSSFWorkbook();
            ISheet sheet = wb.CreateSheet("Test");
            var row = sheet.CreateRow(0);
            var cell = row.CreateCell(0);
            cell.SetCellValue("Test");

            using ( stream = new MemoryStream())
            {
                wb.Write(stream);    
            } 

            return stream;
        }
  

Комментарии:

1. это кажется полезным. В GetFile получена ошибка, пожалуйста, вставьте ее.

2. Нет смысла иметь try catch блок, если вы просто повторно создаете исключение…

3. Вы удаляете, stream а затем возвращаете его? Тогда вы получаете ObjectDisposedException ? Не совсем уверен, в чем здесь вопрос.

4. Что Utility.GetFile() делает? Создает ли это файл в памяти или файл с диска? В любом случае вы можете использовать return File(...) для возврата либо сохраненного файла, либо потока. MemoryStream — это не что иное, как оболочка над byte[] массивом, и его удаление не освобождает никаких ресурсов

Ответ №1:

Вам нужно быть осторожным при работе с объектом stream.

Но я думаю, что ваша функция get dataStream также неверна. Возможно, больше кода?

 try
{
    using(MemoryStream dataStream = Utility.GetFile())
    {
      // read from beginning 
      dataStream.Position = 0;

      HttpResponseMessage httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK);
      httpResponseMessage.Content = new StreamContent(dataStream);
      httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
      httpResponseMessage.Content.Headers.ContentDisposition.FileName = "Test.xlsx";
      httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

      return httpResponseMessage;
    }
}
catch (Exception ex)
{
    throw;
}
  

Вы GetFile не можете обернуть оператор using, который закроет ваш объект потока памяти.

  // try this
 public static MemoryStream GetFile()
    {
        MemoryStream stream = new MemoryStream();
        // do your thing
        XSSFWorkbook wb = new XSSFWorkbook();
        ISheet sheet = wb.CreateSheet("Test");
        var row = sheet.CreateRow(0);
        var cell = row.CreateCell(0);
        cell.SetCellValue("Test");

        wb.Write(stream);    
        // reset position here
        stream.Position = 0;

        return stream;
    }
  

Комментарии:

1. Теперь возникает новая ошибка: не удается получить доступ к закрытому потоку

2. @JasminSolanki обычно это связано с тем, что вы обращаетесь к нему, когда он уже «закрыт», вам нужно обернуть с помощью инструкции using, чтобы ваш объект не удалялся, когда вы захотите его использовать

3. @JasminSolanki вы не можете использовать оператор using в своей функции GetFile, поэтому вы получаете ту же ошибку. Удалите этот оператор using.

4. Да, я изменил его, но поток все еще закрыт

5. Зачем использовать этот код вместо простого return File(stream,..); ? Фактическое исправление заключается в том, чтобы избежать удаления MemoryStream или вместо этого использовать буфер byte[]. MemoryStream — это не что иное, как оболочка над байтом[]