10

I'm using Service Stack for a simple web application.

In this app i need to "export" some content to Excel.. So this is the approach I took:

  1. I get the HTML content of a table with jQuery a get the HTML content of a table, and send to the service.
  2. The Service write the content to a file (with some CSS)
  3. Using the same service, but with a GET method, I read the file and make the download with ContentType:"application/vnd.ms-excel"

I used this approach without Service Stack in other times, and worked well.. The XLS file had the right content and some colors.

Now, when I get the file using GET method (i.e., visiting the url with the browser), the file contents are bad.

ServiceStack let download with some formats: html, csv, jsv, json, xml.

The html format shows up a default report page, the only format that works a little is jsv.

My question is: how can I download the file like plain html file?

Some code:

public class ExcelService : RestServiceBase<Excel>
{   
    public override object OnGet (Excel request)
    {
        string file = request.nombre;
        //Response.Clear();
        HttpResult res = new HttpResult();
        res.Headers[HttpHeaders.ContentType] = "application/vnd.ms-excel";
        res.Headers[HttpHeaders.ContentDisposition] = "attachment; filename="+file+".xls";
        string archivo = System.IO.File.ReadAllText("tmp/"+file+".html");
        res.Response = archivo;
        return res;
    }
}

Thanks in advance

Ortiga
  • 8,455
  • 5
  • 42
  • 71
matiasfha
  • 1,270
  • 4
  • 23
  • 42

1 Answers1

12

The HttpResult Constructor in ServiceStack as an overload that supports file downloads, e.g:

return new HttpResult(new FileInfo("tmp/"+file+".html"), 
    asAttachment: true, 
    contentType: "application/vnd.ms-excel");

Which will set the ContentDisposition HTTP headers required for file downloading.

Creating your own Http Result

For more fine-grained control of the HttpOutput you can also create your own Result class. Here is an example of downloading an Excel spreadsheet taken from the customized responses in ServiceStack question:

public class ExcelFileResult : IHasOptions, IStreamWriterAsync
{
    private readonly Stream _responseStream;
    public IDictionary<string, string> Options { get; private set; }

    public ExcelFileResult(Stream responseStream)
    {
        _responseStream = responseStream;

        Options = new Dictionary<string, string> {
             {"Content-Type", "application/octet-stream"},
             {"Content-Disposition", ""attachment; filename=\"report.xls\";"}
         };
    }

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

        await _responseStream.CopyToAsync(responseStream, token);
        await responseStream.FlushAsync(token);
    }
}
mythz
  • 141,670
  • 29
  • 246
  • 390
  • Good example, worked for me. Worth noting that this class above is the reponse class which is returned by the service action. – Ali Oct 31 '14 at 00:35
  • How should you update this answer with IStreamWriterAsync? – labilbe Feb 04 '20 at 15:20
  • @mythz thank you.In a web environment (.NET Framework 4.6) I had to replace "CopyToAsync" with "WriteToAsync". Otherwise, the content is a 0 length file (even if Unit Testing with "CopyToAsync" returns a stream with expected length). – labilbe Feb 05 '20 at 09:25
  • @labilbe Strange, maybe your `_responseStream` Position isn't set to `0`? – mythz Feb 05 '20 at 09:28
  • @mythz kudos!! That was the reason. I'm impressed! – labilbe Feb 05 '20 at 13:08