7

I'm trying to download a nodeServices generated pdf file that is in the form of byte array. here is my original code:

[HttpGet]
[Route("[action]/{appId}")]
public async Task<IActionResult> Pdf(Guid appId, [FromServices] INodeServices nodeServices)
{
    // generateHtml(appId) is a function where my model is converted to html.
    // then nodeservices will generate the pdf for me as byte[].
    var result = await nodeServices.InvokeAsync<byte[]>("./pdf", 
            await generateHtml(appId));
    HttpContext.Response.ContentType = "application/pdf";
    HttpContext.Response.Headers.Add("x-filename", "myFile.pdf");
    HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "x-filename");
    HttpContext.Response.Body.Write(result, 0, result.Length);
    return new ContentResult();
}

This code was working fine, it will show the pdf file in the browser, eg. chrome, and when I try to download it, I get "failed, network error".

I've searched here and there, I saw some suggestions to return File instead:

return File(result, "application/pdf");

that didn't work either, another thing was adding "Content-Disposition" header:

HttpContext.Response.Headers.Add("Content-Disposition", string.Format("inline;filename={0}", "myFile.pdf"));

others suggested to use FileStreamResult, no good either. I realized that the problem could be about my generated file (byte[]) does not have a path or a link of his own, so I saved the bytes to my server, then got the file again by its path, then to memory stream, and finally return a file containing the memory stream:

var result = await nodeServices.InvokeAsync<byte[]>("./pdf", await generateHtml(appId));
var tempfilepath = Path.Combine(_environment.WebRootPath, $"temp/{appId}.pdf");

System.IO.File.WriteAllBytes(tempfilepath, result);

var memory = new MemoryStream();
using (var stream = new FileStream(tempfilepath, FileMode.Open))
{
    await stream.CopyToAsync(memory);
}
memory.Position = 0;

return File(memory, "application/pdf", Path.GetFileName(tempfilepath));

which worked! it showed the file in the browser, and I could download it, but, I did not want any files to be stored on my server, my question is, can't I just download the file without the need to store it?

ekad
  • 13,718
  • 26
  • 42
  • 44
Anan Raddad
  • 193
  • 2
  • 6

1 Answers1

11

You can still return the FileContentResult without converting the byte array to a stream. There is an overload of the File() method which takes fileContents as a byte array and the contentType as a string.

So you can refactor to something like:

public async Task<IActionResult> Pdf(Guid appId, [FromServices] INodeServices nodeServices)
{
    var result = await nodeServices.InvokeAsync<byte[]>("./pdf", 
            await generateHtml(appId));

    return File(result, "application/pdf","myFile.pdf");
}
Stewart_R
  • 11,596
  • 6
  • 45
  • 84
  • thanks alot !! I was aware of that overload, I even tried it as I explained in my question but it did not work for me before, I tried yours anyway and it turns out it works!!, so I went back in time to see the problem, which was that I did not comment out the `HttpContext.Response.Con..` lines in my original code !! which made the file undownloadable. so thank you again – Anan Raddad Oct 03 '17 at 19:36
  • My pleasure. Glad you got it working. I'd entirely missed the part of the question where you said you'd tried `return File(result, "application/pdf");`... :-/ – Stewart_R Oct 03 '17 at 19:39
  • 1
    i'm so glad you miss it ;) – Anan Raddad Oct 03 '17 at 19:46