none
Close stream returned from message RRS feed

  • Question

  • Hi everydoby,

    Hope you're fine.

    Quick question: I have a WCF service that returns a FileStream. Everything works fine but only once as the Stream is not closed after being read from client :

    string headerInfo = String.Format("inline; filename={0}", fileName);
    WebOperationContext.Current.OutgoingResponse.Headers["Content-Disposition"] = headerInfo;
    var fileStream = new FileStream(filePath, FileMode.Open);
    return WebOperationContext.Current.CreateStreamResponse(fileStream, contentType);
    

    I already tried to put fileStream in using brackets but in that case, the Stream is closed before being read from client.

    How can I solve that ?

    Thanks in advance for your suggestions.

    Tuesday, January 9, 2018 9:14 AM

Answers

  • Hi NicolasC,

    I suggest you use code below which will resolve this error.

            public Message GetObject(string folderName, string fileName, string contentType, string url)
            {
                try
                {
                    var fileInfo = new FileInfo(fileName);
                    var update = !fileInfo.Exists;
    
                    if (update)
                    {
                        var webClient = new WebClient();
                        webClient.Headers.Clear();
                        webClient.Headers.Add("Content-Type", contentType);
                        webClient.DownloadFile(url, fileName);
                        Trace.TraceInformation("Downloaded data from {0} to {1}", url, fileName);
                    }
                    string headerInfo = String.Format("inline; filename={0}", fileName);
                    WebOperationContext.Current.OutgoingResponse.Headers["Content-Type"] = contentType;
                    WebOperationContext.Current.OutgoingResponse.Headers["Content-Disposition"] = headerInfo;
    
                    var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                    var message = WebOperationContext.Current.CreateStreamResponse(fileStream, contentType);
                    return message;
                }
                catch (Exception ex)
                {
                    Trace.TraceError(ex.Message);
                    return null;
                }
            }
    

    Best Regards,

    Tao Zhou


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by NicolasC Saturday, January 20, 2018 10:57 AM
    Monday, January 15, 2018 6:38 AM

All replies

  • Hi NicolasC,

    >> Everything works fine but only once as the Stream is not closed after being read from client

    What do you mean by this issue? Did you get any exception?

    Did you develop WCF Soap Service or Rest Service? Based on “WebOperationContext”, it seems you are developing with WCF Rest Service, am I right?

    Do you want to return file stream to client by WCF Rest Service?

    How did the client call WCF Service?

    It would be helpful if you could share us a simple demo which contains WCF Service and client, and then we could try to reproduce your issue.

    Best Regards,

    Tao Zhou


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, January 10, 2018 3:22 AM
  • Hi Tao,

    It's a WCF SOAP service. Here is the interface :

        [ServiceContract]
        public interface IPublicServer
        {
            [OperationContract]
            [WebGet()]
            Message GetStreamedObject(string folderName, string fileName, string contentType, string url);
        }
    

    Here is the method :

            public Message GetStreamedObject(string folderName, string fileName, string contentType, string url)
            {
                try
                {
                    var folder = CacheFolder.CreateSubdirectory(folderName);
                    var filePath = Path.Combine(folder.FullName, fileName);
                    var fileInfo = new FileInfo(filePath);
                    var update = !fileInfo.Exists;
                    if (update)
                    {
                        var webClient = new WebClient();
                        webClient.Headers.Clear();
                        webClient.Headers.Add("Content-Type", contentType);
                        webClient.DownloadFile(url, filePath);
                        TraceSources.Activity.TraceInformation("Downloaded data from {0} to {1}", url, filePath);
                    }
                    string headerInfo = String.Format("inline; filename={0}", fileName);
                    WebOperationContext.Current.OutgoingResponse.Headers["Content-Disposition"] = headerInfo;
                    var fileStream = new FileStream(filePath, FileMode.Open);
                    return WebOperationContext.Current.CreateStreamResponse(fileStream, contentType);
    
                }
                catch (Exception ex)
                {
                    TraceSources.Activity.TraceData(TraceEventType.Error, ex.HResult, ex);
                    return null;
                }
            }
    

    And here is how I initialize the service from server-side:

                    var httpUrl2 = new Uri(String.Format("http://{0}:1984/PublicServer", ServerAlias));
                    var hostPublisher2 = new WebServiceHost(typeof(Server), httpUrl2);
                    var binding2 = new WebHttpBinding();
                    hostPublisher2.AddServiceEndpoint(typeof(IPublicServer), binding2, String.Empty);
                    hostPublisher2.Description.Endpoints[0].EndpointBehaviors.Add(new WebHttpBehavior());
                    hostPublisher2.Open();
                    status.Add(String.Format("Public server is now available at {0}", httpUrl2));
    

    And here is I connect to the service from client and how I call it:

                var bindingWeb = new WebHttpBinding();
                bindingWeb.ReaderQuotas.MaxArrayLength = bindingWeb.ReaderQuotas.MaxArrayLength * 2;
                var publicServerEndPointService = new EndpointAddress("http://localhost:1984/PublicServer");
                var publicServerChannel = new ChannelFactory<IPublicServer>(bindingWeb, publicServerEndPointService);
                publicServerChannel.Endpoint.EndpointBehaviors.Add(new System.ServiceModel.Description.WebHttpBehavior());
                BaseItem.PublicServer = publicServerChannel.CreateChannel();
                Console.WriteLine("Connected to end-point");
    
    
                var message = PublicServer.GetStreamed("Dell", String.Format("Warranty_{0}.xml", serviceTag), "Application/xml", url);
                var xs = new XmlSerializer(typeof(Dell.AssetWarrantyDTO));
                using (var rdr = message.GetReaderAtBodyContents())
                {
                    var data = xs.Deserialize(rdr) as Dell.AssetWarrantyDTO;
                    Console.WriteLine("{0} - {1}", data.AssetWarrantyResponse.AssetWarrantyResponse.ProductHeaderData.SystemDescription, data.AssetWarrantyResponse.AssetWarrantyResponse.AssetHeaderData.ServiceTag);
                    foreach (var e in data.AssetWarrantyResponse.AssetWarrantyResponse.AssetEntitlementData)
                        Console.WriteLine("From {0} to {1}: {2}", e.StartDate, e.EndDate, e.ServiceLevelDescription);
                }
    
                publicServerChannel.Close();
    

    I can retrieve my streamed object from the server and deserialize it. The problem is the Stream is not closed and if someone else tries to get the same data, it returns an empty body...

    I did not put the entire code as it's pretty big. I'll put more information if that is not enough.

    Anyway, thank you for replying. Have a nice day.

    Nicolas

    Wednesday, January 10, 2018 8:13 AM
  • Hi Nicolas,

    Could you share us a simple solution through OneDrive?

    I tried to reproduce your issue with provided code, but, it seems you provide wrong client code by mistake. Your Service method is GetStreamedObject, but your client is GetStreamed.

    GetStreamedObject returns a Message with file stream, I am afraid you could not deserialize to an object.

    I found you have enabled TraceSource, is there any error in the log while it returns null?

    I suggest you debug your code line by line to check whether there is value in fileStream  while CreateStreamResponse return.

    Best Regards,

    Tao Zhou


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, January 11, 2018 3:20 AM
  • Hi Tao,

    You're right, I made a mistake in my code. The client calls method "GetStreamedObject" and not "GetStream".

    I'm preparing a simplified version I'll put on OneDrive. My project has a lot of dependencies so putting the entire project would be difficult.

    For information, I can deserialize data from a message using method GetReaderAtBodyContents().
    And it works. My problem is the file open from FileStream stays open on the server and is no longer available for next requests.

    Thank you again for your help.

    Thursday, January 11, 2018 11:58 AM
  • Hi NicolasC,

    >> My problem is the file open from FileStream stays open on the server and is no longer available for next requests

    In general, we dispose the FileStream by using brackets. I see you have tried, did you try something like below:

                string filePath = @"C:\Users\xx.gif";
                using (var fileStream = new FileStream(filePath, FileMode.Open))
                {
                       return WebOperationContext.Current.CreateStreamResponse(fileStream, contentType);
                }
    

    When the test demo is ready, please feel free to share us.

    Best Regards,

    Tao Zhou


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, January 12, 2018 2:49 AM
  • Hi Tao,

    Here is a very simplified (but working) example: https://we.tl/RmKyeobDjj.

    For testing, lunch DellWarranty.exe (admin rights may be required) then connect to the url http://localhost:1984/PublicServer/GetIP.

    The app will download the IP public in a text file and a stream of this file will be returned to the browser.
    If you try a 2nd time, it will try to read the file but won't work until you restart the app because the stream is still open.

    If we close the stream before the message is returned (or if we use brackets), the browser will say the connection has been reinitialized.

    Thanks in advance for your help.

    Sunday, January 14, 2018 3:28 PM
  • Hi NicolasC,

    I suggest you use code below which will resolve this error.

            public Message GetObject(string folderName, string fileName, string contentType, string url)
            {
                try
                {
                    var fileInfo = new FileInfo(fileName);
                    var update = !fileInfo.Exists;
    
                    if (update)
                    {
                        var webClient = new WebClient();
                        webClient.Headers.Clear();
                        webClient.Headers.Add("Content-Type", contentType);
                        webClient.DownloadFile(url, fileName);
                        Trace.TraceInformation("Downloaded data from {0} to {1}", url, fileName);
                    }
                    string headerInfo = String.Format("inline; filename={0}", fileName);
                    WebOperationContext.Current.OutgoingResponse.Headers["Content-Type"] = contentType;
                    WebOperationContext.Current.OutgoingResponse.Headers["Content-Disposition"] = headerInfo;
    
                    var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                    var message = WebOperationContext.Current.CreateStreamResponse(fileStream, contentType);
                    return message;
                }
                catch (Exception ex)
                {
                    Trace.TraceError(ex.Message);
                    return null;
                }
            }
    

    Best Regards,

    Tao Zhou


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by NicolasC Saturday, January 20, 2018 10:57 AM
    Monday, January 15, 2018 6:38 AM
  • Hi Tao,

    Thanks a lot! That works :)

    But I'm wondering if there is a way to close the Stream after reading the data from the client.

    I guess I have to implement a manual mechanism like a timer from server or something equivalent to WebOperationContext with OnCompleted or OnRead event ...

    Anyway, I marked your message as "answer".

    Thanks again.

    Saturday, January 20, 2018 10:57 AM