如何使用ServiceStack客户端使用文件

我正在尝试使用ServiceStack以RESTful方式将文件返回到ServiceStack客户端。

我已经阅读了有关SO( 此处和此处 )的其他问题,建议使用HttpResult和FileInfo对象或MemoryStream来将ContentType标头更改为相关的文件类型。

当我通过浏览器调用服务时,这对我有用,正确的文件会自动开始下载。 我如何使用其中一个ServiceStack客户端使用该文件?

我正在使用Request DTO并尝试使用类似的东西返回

return new HttpResult(new FileInfo("file.xml"), asAttachment:true) { ContentType = "text/xml" }; 

例如,我如何使用JsonServiceClient消耗它?

您不会使用ServiceStack的.NET ServiceClients来使用文件,因为它们主要用于发送DTO。

你可以使用任何普通的WebRequest来下载文件,在ServiceStack的v3.9.33中引入了一些方便的WebRequest扩展HTTP Utils ,使这很容易,例如:

对于文本文件:

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

其中fileInfoHeaders包含W3C ContentDisposition HTTP标头 ,例如,当返回FileInfo , ServiceStack返回 :

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

要下载二进制文件,您可以使用:

 var rawBytes = downloadUrl.GetBytesFromUrl(httpRes => ...); 

我有类似的要求,也要求我跟踪流文件下载的进度。 我大致这样做了:

服务器端

服务:

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

StreamedResult类:

 public class StreamedResult : IHasOptions, IStreamWriter { public IDictionary Options { get; private set; } Stream _responseStream; public StreamedResult(Stream responseStream) { _responseStream = responseStream; long length = -1; try { length = _responseStream.Length; } catch (NotSupportedException) { } Options = new Dictionary { {"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(); } } } 

客户端

 string path = Path.GetTempFileName();//in reality, wrap this in try... so as not to leave hanging tmp files var response = client.Get("/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)); 

“CopyFrom”扩展方法直接从此项目中的源代码文件“StreamHelper.cs”借用: 复制带有进度报告的流 (感谢Henning Dieterichs)

并且对mythz和ServiceStack的任何贡献者都赞不绝口。 伟大的项目!

我发现mythz的答案工作得很好,但是也可以使用他们内置的JSonServiceClient来处理文件请求,只是以一种非直观的方式,因为你实际上不能使用你期望的返回类型。

对于像这样的模型定义:

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

您可以定义服务以返回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); } } 

然后在客户端,您可以使用此Get模板来正确获取Web上下文:

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

我发现不可能使用新的API Get方法,因为它们会尝试反序列化HttpResult,它实际上不是真正的返回类型,而是表示服务堆栈已创建的Web上下文的类。

您可以使用响应filter拦截响应之前的响应,如下所示:

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

但是,这不是处理它的最佳方法,因为当客户端尝试读取响应流时,实际调用client.Method()将导致ArgumentException (因为它之前已被response.DownloadFile(...)读取response.DownloadFile(...) 。我还没有想出办法优雅地处理它,但如果我这样做,我会更新我的答案。