简体   繁体   English

使用 ServiceStack Rest-API 下载文件

[英]Download files with ServiceStack Rest-API

I'm quite new to REST-services in general and I'm playing around with ServiceStack (which is awesome!).总的来说,我对 REST 服务很陌生,我正在使用 ServiceStack(这太棒了!)。 I have some services running and now I want to be able to download files (zip) via the service.我有一些服务正在运行,现在我希望能够通过该服务下载文件 (zip)。

My idea is to set a route (/download) to receive files and download them with the client to store them locally.我的想法是设置一个路由(/download)来接收文件并用客户端下载它们以将它们存储在本地。

My current approach looks like this:我目前的方法是这样的:

[Route("/download")]
public class DownloadRequest : IReturn<HttpResult>
{

}

public class FileDownloadService : Service
{
    public object Any(DownloadRequest request)
    {
        string fileFullPath = @"C:\Users\marcel\Downloads\test.zip";
        string mimeType = "application/zip";
        FileInfo fi = new FileInfo(fileFullPath);

        byte[] reportBytes = File.ReadAllBytes(fi.FullName);
        HttpResult result = new HttpResult(reportBytes, mimeType);

        result.Headers.Add("Content-Disposition", "attachment;filename=Download.zip;");

        return result;
    }
}

I'd like to change this implementation to send data as stream.我想更改此实现以将数据作为流发送。 I stumbled upon IStreamWriterAsync, but couldn't really find documentation on usage for this.我偶然发现了 IStreamWriterAsync,但无法真正找到有关此用法的文档。 I'd also like to be able to handle client-side download with the ServiceStack C#-Client.我还希望能够使用 ServiceStack C#-Client 处理客户端下载。

What would be a good strategy do implement my plan?实施我的计划的好策略是什么?

Edit: Something like that?编辑:类似的东西?

[Route("/download")]
public class DownloadRequest : IReturn<Stream>
{

}

public class FileDownloadService : Service, IHasOptions
{
    public IDictionary<string, string> Options { get; private set; }

    public Stream Any(DownloadRequest request)
    {
        string fileFullPath = @"C:\Users\marcel\Downloads\test.zip";
        FileInfo fi = new FileInfo(fileFullPath);

        Options = new Dictionary<string, string>
        {
            {"Content-Type","application/zip" },
            {"Content-Disposition", "attachment;filename=Download.zip;" }
        };

        return fi.OpenRead();
    }
}

An easy way to download a file is to return the fileInfo in a HttpResult, eg:下载文件的一种简单方法是在 HttpResult 中返回 fileInfo,例如:

return new HttpResult(new FileInfo(fileFullPath), asAttachment:true);

Or by using the Virtual File System或者通过使用虚拟文件系统

return new HttpResult(
    VirtualFileSources.GetFile(virtualPath), asAttachment:true);

Both of these APIs already write the file bytes as a Stream so there's no need to try manually doing it yourself.这两个 API 都已经将文件字节写为 Stream,因此无需尝试自己手动执行。

Note: HttpResult is just a server wrapper object not the response body itself so it should never be used in an IReturn<T> interface whose purpose is to tell clients what Response Type the Service returns.注意: HttpResult只是一个服务器包装器对象,而不是响应主体本身,因此它永远不应该用IReturn<T>接口中,该接口的目的是告诉客户端服务返回什么响应类型。

The IReturn<T> should specify what the Response Body is, in this case since it's not a Response DTO it can be either: IReturn<T>应该指定响应主体是什么,在这种情况下,因为它不是响应 DTO,它可以是:

IReturn<byte[]> or IReturn<Stream> 

Or you can just leave it unspecified as you'll still be able to download it using the ServiceClient's raw data APIs :或者您可以不指定它,因为您仍然可以使用ServiceClient 的原始数据 API下载它:

With IReturn<Stream> interface:使用IReturn<Stream>接口:

using (Stream stream = client.Get(new DownloadRequest())) {
   ...
}

Or you can just easily download the response as a Stream without the IReturn<T> by specifying how you want to access the raw data on the call-site, eg:或者,您可以通过指定您希望如何访问调用站点上的原始数据,轻松地将响应下载为 Stream 而不使用IReturn<T> ,例如:

Stream stream = client.Get<Stream>(new DownloadRequest());
byte[] bytes = client.Get<byte[]>("/download");

If you want to also access the Response HTTP Headers you can also request the raw HttpWebResponse to be returned which will let you access the Response HTTP Headers:如果您还想访问响应 HTTP 标头,您还可以请求返回原始HttpWebResponse ,这将允许您访问响应 HTTP 标头:

using (var webRes = client.Get<HttpWebResponse>(new DownloadRequest()))
using (var stream = webRes.GetResponseStream())
{
    var contentDisposition = webRes.Headers[HttpHeaders.ContentDisposition];
}

Alternatively you can also use HTTP Utils to download arbitrary files, eg:或者,您也可以使用HTTP Utils下载任意文件,例如:

 string info = null;
 var bytes = baseUrl.CombineWith("download").GetBytesFromUrl(
     responseFilter: res => info = res.Headers[HttpHeaders.ContentDisposition]);

Have a look at this article.看看这篇文章。 Basically, just return a Stream .基本上,只需返回一个Stream You can use fi.OpenRead and return that stream.您可以使用fi.OpenRead并返回该流。

To combine headers and stream, an option is a custom return type instead, something like this要组合标题和流,一个选项是自定义返回类型,就像这样

public class DownloadFileResult : IStreamWriterAsync, IHasOptions
{
    private readonly Stream _stream;
    public IDictionary<string, string> Options { get; }

    public DownloadFileResult(Stream responseStream, string mime, string filename)
    {
        _stream = responseStream;

        Options = new Dictionary<string, string>()
        {
            {"Content-Disposition", $"attachment; filename=\"{filename}\";"},
            {"Content-Type", mime}
        };
    }

    public async Task WriteToAsync(Stream responseStream, CancellationToken token)
    {
        if (_stream == null) { 
            return;
        }

        await _stream.CopyToAsync(responseStream);
        responseStream.Flush();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM