Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Download file from ASP.NET Core api from Blazor client application

I created an ASP.NET Core api controller which return a FileStreamResult object. (I can change the type of result if needed)

Here is the code of the Get function:

[HttpGet("[action]/{p_gInspectionID}/{p_nIndex}")]
public async Task<FileStreamResult> GetInspectionPictureToDownload(Guid p_gInspectionID, int p_nIndex)
{
  var l_strFilePath = await GetPictureFilePathAsync(p_gInspectionID, p_nIndex);

  using (var l_sReader = System.IO.File.OpenRead(l_strFilePath))
  {
    return (File(l_sReader, "image/jpeg"));
  }
}

Now I need to consume this result in the Blazor (Webassembly) client side application.

My goal is to have a button to launch the download of the file in the browser when user clicks on it.

This should launch download functionnality of the browser. Is it possible to achieve this in Blazor client application ?

like image 454
Jean-Daniel Gasser Avatar asked May 14 '20 16:05

Jean-Daniel Gasser


2 Answers

here is how I solved the problem. In fact the solution was really straightforward. Thank you @Data Juggler for pointing me in the right direction.

My Blazor solution holds two project:

  • the server side API (Blazor server)
  • the client side (Blazor WebAssembly).

Here is the code for the server side:

[AllowAnonymous]
[HttpGet("[action]/{p_strPictureFilePath}")]
public IActionResult GetInspectionPicture(string p_strPictureFilePath)
{
  var l_sReader = System.IO.File.OpenRead(p_strPictureFilePath);

  return (File(l_sReader, "application/octet-stream", Path.GetFileName(p_strPictureFilePath)));
}

... and the code on the client side:

Added this script in client-shared.js file:

window.downloadInspectionPicture = function (p_strServerFilePath)
{
  var link = document.createElement('a');
  link.href = 'api/Data/GetInspectionPicture/' + this.encodeURIComponent(p_strServerFilePath);
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}

of course, a reference to that file is present in index.html:

  <script src="client-shared.js"></script>

And finally, added a link in the razor file, and invoke script when link is clicked:

<a href="javascript:void(0)" @onclick="@(async () => await DownloadPictureAsync())">Download</a>

@code
{
  [Inject]
  IJSRuntime ThisJSRuntime { get; set; }

  private async Task DownloadPictureAsync()
  {
    await ThisJSRuntime.InvokeVoidAsync("downloadInspectionPicture", ServerFilePath);
  }
}

Hope my answer is clear and can be useful to someone

like image 117
Jean-Daniel Gasser Avatar answered Oct 11 '22 00:10

Jean-Daniel Gasser


I was trying to do the same thing, but my API was authorized, so after reading this article I end up downloading the bytes in the web assembly application and use JavaScript to download the file from the bytes.

function downloadFromByteArray(options: { 
  byteArray: string, 
  fileName: string, 
  contentType: string
}): void {

  // Convert base64 string to numbers array.
  const numArray = atob(options.byteArray).split('').map(c => c.charCodeAt(0));

  // Convert numbers array to Uint8Array object.
  const uint8Array = new Uint8Array(numArray);

  // Wrap it by Blob object.
  const blob = new Blob([uint8Array], { type: options.contentType });

  // Create "object URL" that is linked to the Blob object.
  const url = URL.createObjectURL(blob);

  // Invoke download helper function that implemented in 
  // the earlier section of this article.
  downloadFromUrl({ url: url, fileName: options.fileName });

  // At last, release unused resources.
  URL.revokeObjectURL(url);
}
like image 4
StPaulis Avatar answered Oct 10 '22 23:10

StPaulis