6

I'm using a MVC 5 web Api Controller, and I want to return a file:

[Route("")]
public HttpResponseMessage GetFile()
{
    var statusCode = HttpStatusCode.OK;
    FileStream file = XLGeneration.XLGeneration.getXLFileExigence();

    return Request.CreateResponse(statusCode, file);
}

It dosn't work.

The exception from postman is:

"ExceptionMessage": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'."

MatteoSp
  • 2,940
  • 5
  • 28
  • 36
AtefB
  • 171
  • 1
  • 3
  • 16
  • Looking at the definition of the method, the second argument is the value you're trying to serialize. So what it's trying to do is serialize a `FileStream` which probably isn't serializable. Try reading the file into a string and using that string as the second argument. – Craig W. May 08 '15 at 14:23
  • Possibly related: http://stackoverflow.com/questions/9541351/returning-binary-file-from-controller-in-asp-net-web-api – dbc May 08 '15 at 14:28

4 Answers4

5

I'm posting what worked for me as an alternative in case anybody else is having trouble.

[ActionName("File")]
[HttpGet]
public HttpResponseMessage File()
{
        var response = new HttpResponseMessage(HttpStatusCode.OK);
        var stream = new System.IO.FileStream(yourFilePath, System.IO.FileMode.Open);
        response.Content = new StreamContent(stream);
        response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");

        return response;
}
3

I returned byte[] from WebAPI controller and download PDF successfully. I'm using iTextSharp (LGPL) 4.1.6 free PDF converter. To install iTextSharp (LGPL / MPL), run the following command in the Package Manager Console.

Install-Package iTextSharp-LGPL -Version 4.1.6

Server side code

    [Route("Export/ExportToPdf")]

    public byte[] ExportToPdf(string html)
    {           

        MemoryStream msOutput = new MemoryStream();
        TextReader reader = new StringReader(html);
        Document document = new Document(new Rectangle(842, 595));             
        PdfWriter writer = PdfWriter.GetInstance(document, msOutput);
        document.Open();
        document.HtmlStyleClass = @"<style>*{ font-size: 8pt; font-family:arial;}</style>";

        var parsedHtmlElements = HTMLWorker.ParseToList(new StringReader(html), null);
        foreach (var htmlElement in parsedHtmlElements)
        {
            document.Add(htmlElement as IElement);
        }

        document.Close();

        return msOutput.ToArray();
    }

Client Side Code.

//Call this function inside of AJAX success.
function ExportToPDF(data) {

    //base64 To ArrayBuffer
    var binaryString = window.atob(data);
    var binaryLen = binaryString.length;
    var bytes = new Uint8Array(binaryLen);
    for (var i = 0; i < binaryLen; i++) {
        var ascii = binaryString.charCodeAt(i);
        bytes[i] = ascii;
    }
    //-------

    var link = document.createElement('a');
    link.href = window.URL.createObjectURL(new Blob([bytes], { type: 'application/pdf' }));
    link.download = "Report.pdf";
    link.click();   
}
iehrlich
  • 3,572
  • 4
  • 34
  • 43
2

Try this...

[Route("")]
public HttpResponseMessage GetFile()
{
    var result = new HttpResponseMessage(HttpStatusCode.OK);
    try
    {
        var file = XLGeneration.XLGeneration.getXLFileExigence();
        result.Content = new StreamContent(file);
        result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
        var value = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
        value.FileName = "Whatever your filename is";
        result.Content.Headers.ContentDisposition = value;
    }
    catch (Exception ex)
    {
        // log your exception details here
        result = new HttpResponseMessage(HttpStatusCode.InternalServerError);
    }
    return result;
}

This should actually stream it back as a file.

Kevin
  • 1,462
  • 9
  • 9
  • @AtefB I've had this code running in production for almost a year now, the only difference is I'm reading my file contents into an array of bytes and creating a MemoryStream to create the StreamContent from. Other than that it is cut and paste from my production code. – Kevin May 08 '15 at 15:19
  • @AtefB There are two possibilities why this may not have worked for you. If your XLGeneration.XLGeneration.getXLFileExigence() doesn't actually return a stream, or if you didn't replace "Whatever your filename is" with an actual filename. – Kevin May 08 '15 at 15:27
  • it returns only the Name, but not the stream of the file – AtefB May 08 '15 at 15:36
  • @AtefB I'd know more about whta went wrong if I knew the specific exception my code gave you. The problem with the original code you posted is that WebAPI uses Newtonsoft's JSON.Net as the default serializer. So when you pass it a FileStream it doesn't know what to do with it. My code bypasses using that default serializer. – Kevin May 08 '15 at 15:39
  • @AtefB verify that the return from your XLGeneration.XLGeneration.getXLFileExigence() method is a stream, and that it has a length greater than 0. Sounds like you're not getting anything from that method other than possibly an empty stream. – Kevin May 08 '15 at 15:41
0

Just idea:

public HttpResponseMessage GetFile()
{
    FileStream file = XLGeneration.XLGeneration.getXLFileExigence();

    using(var sr = new StreamReader(file))
    {
        content = sr.ReadToEnd();

        return new HttpResponseMessage
        {
            Content = new StringContent(content, Encoding.UTF8, "application/json")
        };
    }
}
Miroslav Holec
  • 3,139
  • 25
  • 22
  • This would load the entire file into memory before sending it. @Kevin 's solution is preferable because it sends the stream directly to the client. – Moby Disk May 08 '15 at 21:40
  • @Miroslav Exception @ 'using(var sr = new StreamReader(file))' The stream can not be played – AtefB May 09 '15 at 21:56