2013-01-03 53 views
14

我試圖使用ServiceStack以RESTful方式將文件返回給ServiceStack客戶端。如何使用ServiceStack客戶端使用文件

我讀過SO(herehere)的其他問題,建議使用HttpResult和FileInfo對象或MemoryStream來允許將ContentType標題更改爲相關文件類型。

當我通過瀏覽器調用服務時,這適用於我,正確的文件自動開始下載。但是,如何使用其中一個ServiceStack客戶端來使用該文件?

我使用的是請求DTO並試圖使用類似於

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

如何將消耗這與JsonServiceClient例如東西回來?

回答

13

你不會使用ServiceStack's .NET ServiceClients的文件,因爲它們主要是發送DTO的。

你可以使用任何普通的WebRequest下載文件,在ServiceStack的v3.9.33介紹了一些handy WebRequest extensions HTTP Utils,使這個容易,如:

對於文本文件:

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

凡fileInfoHeaders包含W3C ContentDisposition HTTP Header,例如返回FileInfoServiceStack returns時:

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

要下載的二進制文件,你可以使用:

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

感謝mythz,我發現了WebRequest擴展,同時調查了loannis的答案,並開始懷疑需要標準的WebRequest而不是ServiceClient。我已經成功地使用了DownloadAsString,但在您的示例中將切換到DownloadUrl,這要歸功於它提供的Headers的訪問權限。再次感謝您確認WebRequest應該用於文件下載,我發現了很多用於提供文件的服務代碼,但卻試圖找到使用該服務的示例:) – thudbutt

+0

+1:好,簡單,實用。 –

+0

我沒有意識到這個功能,那簡直是無摩擦的驚人! –

1

您可以攔截之前,它被使用響應濾波器,如下面處理的響應:

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

然而,這不是處理的最佳方式,因爲到client.Method()實際調用將導致在ArgumentException當客戶端試圖讀取響應流(因爲它已經被response.DownloadFile(...)以前讀過。我還沒有想出一個方法來優雅地處理它,但如果我這樣做,我會更新我的答案。)

+0

非常感謝loannis,確實有效,但正如您所說,當原始調用嘗試讀取時引發異常。如果您查看ServiceStack源代碼,您可以看到,在使用void return調用Get時,響應過濾器在響應流上調用ReadFully之前應用於響應流,該響應流將導致異常。我不確定是否有優雅的處理方式(拋開異常),因爲我不認爲這是響應過濾器的目的。 – thudbutt

+0

你是對的;此外,恕我直言,編寫處理將會發生的異常的代碼是不好的做法。 –

15

我也要求我跟蹤的流媒體文件下載的進度提出了類似要求。我做到了,大致是這樣的:

服務器端

服務:

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

StreamedResult類:

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(); 
     } 
    } 
} 

客戶端

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)); 

的「的copyfrom」擴展方法是直接從該項目這裏的源代碼文件「StreamHelper.cs」借:Copy a Stream with Progress Reporting(榮譽給恩寧Dieterichs)

和榮譽給mythz任何貢獻者ServiceStack。偉大的項目!

+2

很好的答案,我喜歡它:)。還有@竹的[Reactive Observable Streaming Client](https://gist.github.com/bamboo/5078236)。 – mythz

+0

我懷疑有沒有在這裏正確處理的危險。在處理響應流之後,HttpWebResponse需要正式處理。這是根據[MSDN文檔](http://msdn.microsoft.com/en-us/library/system.net.webresponse.getresponsestream.aspx)以及其他人在未遵循此過程時遇到內存泄漏的問題。 –

2

我已經找到答案mythz工作很好,但它也可以使用其內置的JSonServiceClient也處理文件請求爲好,只是稍微非直觀的方式,因爲你不能實際使用你期望的返回類型。

對於這樣一個模型定義:

[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; } 
} 

你可以定義你的服務來回報的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); 
    } 
} 

然後你可以使用這個獲取模板正確獲得web上下文:

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

我發現無法使用new API Get methods作爲他們會嘗試反序列化HttpResult,它實際上不是真正的返回類型,而是表示服務堆棧已創建的Web上下文的類。

相關問題