Может ли возвращаемый тип WCF MemoryStream записываться в объект Http Response для загрузки в виде файла Excel?
Я создал приложение для синтаксического анализа, которое читает XML-файлы и заполняет книгу Excel с использованием библиотеки NPOI. Первоначально я имел это как часть моего веб-приложения.net и получал MemoryStream от NPOI и записывал это в объект Response, чтобы браузер загружал его. С тех пор я переместил анализ в службу WCF netTcp, размещенную в службе Windows. Связь прекрасно работает, но когда я возвращаю MemoryStream из службы WCF и записываю его в ответ, я получаю следующую ошибку:
Ошибка выполнения Microsoft JScript: Sys.WebForms.PageRequestManagerParserErrorException: не удалось проанализировать сообщение, полученное с сервера.
Мой вопрос: что происходит с потоком, когда он передается от службы wcf моему клиенту? Поток (теоретически) - это тот же поток из NPOI, который я изначально писал в ответ. Есть ли какая-либо специальная обработка, которую я должен сделать на клиенте, чтобы сделать эту работу?
Вот мой код клиента: (исключение выдается в Response.End()
string filename = "test.xls";
ms = client.GetExportedFile();
byte[] b = ms.ToArray();
ms.Flush();
ms.Close();
Response.Clear();
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader( "Content-Disposition", string.Format( "attachment;filename={0}", filename ) );
Response.AddHeader( "Content-Length", b.Length.ToString() );
Response.BinaryWrite( b );
Response.End();
3 ответа
Похоже, вы повторно запускаете поток, чтобы запросить частичное обновление страницы с помощью панели "Обновление" (найдите Sys.WebForms.PageRequestManagerParserErrorException, чтобы найти более подробную информацию об исключении).
Убедитесь, что вы перезапускаете поток только к полностраничному запросу (GET/POST выдается самим браузером, а не каким-либо скриптом на странице, который ожидает какой-то определенный тип ответа).
Я нашел решение здесь. Вот мой длинный ответ на случай, если это поможет кому-то в будущем. Во-первых, netTcp не поддерживает потоковую передачу, только буферизацию. В статье говорится, что только basicHttp поддерживает потоковую передачу, но это не так, поскольку я успешно проверил это с помощью wsHttp. В моем файле конфигурации на стороне клиента у меня теперь есть 2 определения привязки; 1 для netTcp и другой для wsHttp (для всех моих потоковых сообщений я все еще хочу использовать netTcp). Если вы используете basicHttp, то вам нужно установить для атрибута 'TransferMode' значение 'Streamed' - wsHttp не разрешает этот атрибут.
Затем мне пришлось определить новый DataContract в моем сервисе, который определил член, который я определил как MessageBodyMember. Этот член имеет тип MemoryStream:
[MessageContract]
public class FileDownloadMessage
{
[MessageBodyMember( Order = 1 )]
public System.IO.MemoryStream FileByteStream;
}
Затем для OperationContract, который первоначально возвращал MemoryStream, я изменил так, чтобы он возвращал новый контракт данных FileDownloadMessage:
[OperationContract]
FileDownloadMessage GetExportedFile();
Реализация этого контракта:
public FileDownloadMessage GetExportedFile()
{
FileDownloadMessage f = new FileDownloadMessage();
f.FileByteStream = new MemoryStream();
if ( ProgressMonitor.Status == ProcessStatus.CompletedReadyForExport )
{
f.FileByteStream = ProgressMonitor.ExportedFileData;
ProgressMonitor.Status = ProcessStatus.Ready;
}
else
{
f.FileByteStream = null;
}
return f;
}
Теперь служба WCF возвращает поток без каких-либо сопутствующих метаданных, поэтому объект Response моей веб-страницы может правильно проанализировать поток:
MemoryStream ms = new MemoryStream();
string filename = "test.xls";
// the code file from the wcf service includes a get method to get the
// MessageBodyMember directly
ms = streamingClient.GetExportedFile();
byte[] b = ms.ToArray();
ms.Flush();
ms.Close();
Response.Clear();
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader( "Content-Disposition", string.Format( "attachment;filename={0}", filename ) );
Response.AddHeader( "Content-Length", b.Length.ToString() );
Response.BinaryWrite( b );
Response.End();
Мой конфиг выглядит так:
<wsHttpBinding>
<binding name="WSHttpBindingEndPoint" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="655360"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="1638400"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
<netTcpBinding>
<binding name="NetTcpBindingEndPoint" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="524288"
maxBufferSize="655360" maxConnections="10" maxReceivedMessageSize="655360">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="1638400"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
Несколько вещей, которые нужно попробовать, если вы используете IE; по-видимому, он меньше влияет на данные типа контента, чем другие браузеры:
Укажите общедоступный заголовок Pragma и обязательный повторный проверку заголовка CacheControl.
Попробуйте явно указать кодировку передачи контента:
Response.AddHeader("Content-Transfer-Encoding","binary");
,