Может ли возвращаемый тип WCF MemoryStream записываться в объект Http Response для загрузки в виде файла Excel? - PullRequest
2 голосов
/ 04 января 2011

Я создал приложение для синтаксического анализа, которое читает 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 ]

2 голосов
/ 04 января 2011

Вы, кажется, повторно запускаете поток для запроса частичного обновления страницы с помощью панели «Обновление» (найдите Sys.WebForms.PageRequestManagerParserErrorException, чтобы найти более подробную информацию об исключении).

Убедитесь, что вы перезапускаете поток только для полного запроса страницы (GET / POST выдается самим браузером, а не каким-либо скриптом на странице, который ожидает какой-то определенный тип ответа).

1 голос
/ 04 января 2011

Я нашел решение здесь . Вот мой длинный ответ на случай, если это поможет кому-то в будущем. Во-первых, netTcp не поддерживает потоковую передачу, только буферизацию. В статье говорится, что только basicHttp поддерживает потоковую передачу, но это не так, поскольку я успешно проверил это с помощью wsHttp. В моем файле конфигурации на стороне клиента у меня теперь есть 2 определения привязки; 1 для netTcp, а другой для wsHttp (для всей моей не потоковой связи я все еще хочу использовать netTcp). Если вы используете basicHttp, то вам нужно установить для атрибута 'TransferMode' значение 'Потоковый' - 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>
0 голосов
/ 04 января 2011

Несколько вещей, которые стоит попробовать, если вы используете IE;очевидно, он меньше влияет на данные из типа контента, чем другие браузеры:

  • Укажите общедоступный заголовок Pragma и обязательный повторный валидатор заголовка CacheControl.

  • Попробуйте явно указать кодировку передачи контента: Response.AddHeader("Content-Transfer-Encoding","binary");.

...