How to consume a file with a ServiceStack client

后端 未结 4 657
盖世英雄少女心
盖世英雄少女心 2021-02-01 23:34

I am trying to use ServiceStack to return a file to a ServiceStack client in a RESTful manner.

I have read other questions on SO (here and here) which advise using HttpR

相关标签:
4条回答
  • 2021-02-02 00:01

    I have found mythz answer to work well, but it is also possible to use their built in JSonServiceClient to also process file requests as well, just in a slightly non-intuitive way because you can't actually use the return type you would expect.

    For a model definition like this:

    [Route("/filestorage/outgoing/{Name}.{Extension}", "GET")]
    [Route("/filestorage/outgoing", "GET")]
    public class GetFileStorageStream : IReturn<HttpResult>
    {
        public string Name { get; set; }
        public string Extension { get; set; }
        public bool ForDownload { get; set; }
    }
    

    You can define your service to return an HttpResult:

    public class FileStorageService : Service
    {
        public HttpResult Get(GetFileStorageStream fileInformation)
        {
            var internalResult = GetFromFileStorage(fileInformation);
            var fullFilePath = Path.Combine("C:\Temp", internalResult.FileName);
            return new HttpResult(new FileInfo(fullFilePath), asAttachment: fileInformation.ForDownload);
        }
    }
    

    Then on the client side you can use this Get template to properly get the web context:

    var client = new JsonServiceClient("http://localhost:53842");
    var httpResponse = client.Get<HttpWebResponse>("/filestorage/outgoing/test.jpg");
    pictureBox1.Image = Image.FromStream(httpResponse.GetResponseStream());
    

    I found it was not possible to use the new API Get methods as they would attempt to deserialize the HttpResult which isn't actually a true return type but a class representing the web context that service stack has created.

    0 讨论(0)
  • 2021-02-02 00:12

    I had a similar requirement which also required me to track progress of the streaming file download. I did it roughly like this:

    server-side:

    service:

    public object Get(FooRequest request)
    {
        var stream = ...//some Stream
        return new StreamedResult(stream);
    }
    

    StreamedResult class:

    public class StreamedResult : IHasOptions, IStreamWriter
    {
        public IDictionary<string, string> Options { get; private set; }
        Stream _responseStream;
    
        public StreamedResult(Stream responseStream)
        {
            _responseStream = responseStream;
    
            long length = -1;
            try { length = _responseStream.Length; }
            catch (NotSupportedException) { }
    
            Options = new Dictionary<string, string>
            {
                {"Content-Type", "application/octet-stream"},
                { "X-Api-Length", length.ToString() }
            };
        }
    
        public void WriteTo(Stream responseStream)
        {
            if (_responseStream == null)
                return;
    
            using (_responseStream)
            {
                _responseStream.WriteTo(responseStream);
                responseStream.Flush();
            }
        }
    }
    

    client-side:

    string path = Path.GetTempFileName();//in reality, wrap this in try... so as not to leave hanging tmp files
    var response = client.Get<HttpWebResponse>("/foo/bar");
    
    long length;
    if (!long.TryParse(response.GetResponseHeader("X-Api-Length"), out length))
        length = -1;
    
    using (var fs = System.IO.File.OpenWrite(path))
        fs.CopyFrom(response.GetResponseStream(), new CopyFromArguments(new ProgressChange((x, y) => { Console.WriteLine(">> {0} {1}".Fmt(x, y)); }), TimeSpan.FromMilliseconds(100), length));
    

    The "CopyFrom" extension method was borrowed directly from the source code file "StreamHelper.cs" in this project here: Copy a Stream with Progress Reporting (Kudos to Henning Dieterichs)

    And kudos to mythz and any contributor to ServiceStack. Great project!

    0 讨论(0)
  • 2021-02-02 00:14

    You wouldn't consume files with the ServiceStack's .NET ServiceClients as they're mainly for sending DTO's.

    You can just use any normal WebRequest to download files, in the v3.9.33 of ServiceStack introduced some handy WebRequest extensions HTTP Utils that make this easy, e.g:

    For a text file:

    var xmlFile = downloadUrl.GetXmlFromUrl(responseFilter: httpRes => {
            var fileInfoHeaders = httpRes.Headers[HttpHeaders.ContentDisposition];
        });
    

    Where fileInfoHeaders contains the W3C ContentDisposition HTTP Header, e.g. when returning a FileInfo, ServiceStack returns:

    attachment;filename="file.xml";size={bytesLen};
    creation-date={date};modification-date={date};read-date={date};
    

    To download a binary file you can use:

    var rawBytes = downloadUrl.GetBytesFromUrl(httpRes => ...);
    
    0 讨论(0)
  • 2021-02-02 00:27

    You can intercept the response prior to it being handled by using a response filter, like below:

    ServiceClientBase.HttpWebResponseFilter = response =>
    {
        if (response.Headers["Content-Disposition"] != null)
        {
            var t = response.DownloadText();
            Console.WriteLine(t);
        }
    };
    

    However, this is not the best way to handle it, since the actual call to client.Method() will result in an ArgumentException when the client attempts to read the response stream (since it has been read previously by response.DownloadFile(...). I haven't yet figured out a way to handle it gracefully, but I 'll update my answer if I do.

    0 讨论(0)
提交回复
热议问题