1

I have a controller endpoint which serves files as follows;

    [HttpGet]
    [ProducesResponseType(typeof(FileStreamResult), 200)]
    [Route("{documentId}")]
    public IActionResult GetDocumentImage(int documentId)
    {
        var response = _service.Get(documentId);

        HttpAssert.Success(response);
        HttpAssert.IsNotNull(response);

        Stream stream = new MemoryStream(response.Result.Data);

        if (stream == null)
            return NotFound();

        return File(stream, response.Result.MimeType); 
    }

This works well when setting to src on a img tag and can confirm the files are being successfully sent.

I want to consume the result in a blazor razor page. I deserialise the call using

var responseContent = await response.Content.ReadAsStringAsync();

if (response.IsSuccessStatusCode)
{
 var result = JsonConvert.DeserializeObject<T>(responseContent);
 return new ServiceResponse<T> { Result = result, HasError = false, HttpErrorCode = -1, Error = null };
}

However, when I deserialize to an object (with JsonConvert.DeserializeObject<T>(responseContent))

An error is thrown with no ex.Message when I debug and tried to evaluate in the watch window I get

'Unexpected character encountered while parsing value: %. Path '', line 0, position 0.'

The return in this situation is a PDF file. Can anyone direct me as to where I am going wrong please?

Matthew Flynn
  • 3,661
  • 7
  • 40
  • 98

3 Answers3

4

As TomTom mentioned you can not convert a PDF to json, PDF is not like a text or a json file.

So in Blazor, you will have to do in the following way.

When you get back the result from your endpoint, it will be a stream. So you can do the following to get the stream from the endpoint:

var fileStream = new FileStream()
{
    Stream = await httpResponseMessage.Content.ReadAsStreamAsync(),
    Filename = $"someFile.pdf"
};

After you get the file stream, you will have to convert it to byte:

byte[] bytes;
using (var memoryStream = new System.IO.MemoryStream())
{
    fileStream.Stream.CopyTo(memoryStream);
    bytes = memoryStream.ToArray();
}

Once you have it in byte array, this is now in array format which we can now use and download it via users browser. You will have to create the following js to trigger the browser download in Blazor

Here we are basically creating an anchor in the HTML DOM and triggering a click event (source):

var saveAsFile = function (filename, bytesBase64) {
    var link = document.createElement('a');
    link.download = filename;
    link.href = "data:application/octet-stream;base64," + bytesBase64;
    document.body.appendChild(link); // Needed for Firefox
    link.click();
    document.body.removeChild(link);
}

We can call the below in Blazor c# code and trigger this via JSInterop

await JsRunTime.InvokeAsync<object>("saveAsFile", fileStream.Filename,
    Convert.ToBase64String(bytes));
Umair
  • 4,864
  • 3
  • 28
  • 47
1

Ah, pointing out the obvious, but you already tell us waht is wrong:

The return in this situation is a PDF file.

See, a PDF file is not JSON.

You try to deserialize a PDF file with a JSON Converter:

var result = JsonConvert.DeserializeObject(responseContent);

I am not sure where yo uget the idea that this should work. PDF is not JSON. Heck, evn "ReadAsStringAsync()" may not make any sense - Iam not even sure PDF is totally async. It definitely is not JSON.

TomTom
  • 61,059
  • 10
  • 88
  • 148
  • Thanks for that, worryingly it never occurred to me. how would I deserialize the `FileStreamResult` to an object or even `byte[]` then please? – Matthew Flynn Jul 10 '20 at 13:58
  • Start with the documentation. I mean, you found ReadAsString - it should be trivial to go through all the extension methods to find the one reading a stream as byte array. Content is a stream. Stream to byte array - you bascially ask me to read the documentation for you. NEVER doing that. – TomTom Jul 10 '20 at 14:00
0

Similar to Umair's answer, instead of creating a new FileStream and a new MemoryStream, you could just do this:

using var streamReader = new StreamReader(await httpResponseMessage.Content.ReadAsStreamAsync());
var fileString = await streamReader.ReadToEndAsync();
var bytes = Encoding.ASCII.GetBytes(fileString);

Then still follow Umair's example JS Blazor trigger code calling saveFileAs and InvokeAsync.

HappyGoLucky
  • 379
  • 3
  • 9