Download file from ASP.NET Core api from Blazor client application
Asked Answered
I

3

6

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 ?

Isometry answered 14/5, 2020 at 16:34 Comment(0)
P
5

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);
}
Punak answered 12/1, 2021 at 15:31 Comment(2)
In my opinion this is the best solution, thank you for the proposalIsometry
Downloading huge files across the internet, always have downsides. Splitting chunks is also difficult with this approach.Punak
I
5

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

Isometry answered 20/5, 2020 at 6:15 Comment(0)
P
5

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);
}
Punak answered 12/1, 2021 at 15:31 Comment(2)
In my opinion this is the best solution, thank you for the proposalIsometry
Downloading huge files across the internet, always have downsides. Splitting chunks is also difficult with this approach.Punak
M
0

I don't know if in fact your way is possible, but what I do is similar for my site https://pixeldatabase.net .

The user clicks the download button, and I show a link like this:

public async void Download()
{
    // Set the ImagePath
    DownloadLink = ImagePath;
}

enter image description here

Then on the page, I just show a Download link conditionallay:

@if  (HasDownloadLink)
{
    <a class="downloadlink" download="@FileName" href="@DownloadLink" 
target="_blank">Download</a>
}
Michaelamichaele answered 14/5, 2020 at 17:0 Comment(2)
Hello Data Juggler, thank you for your answer. What I want is to start directly the download process, without first download the file to local computer and then provide a link to that file. I think it is in fact what you do here. I mean, I want to use directly the FileStreamResult as source of the download functionality of the browser.Isometry
Are you still wanting the 'Save As' dialog box to appear for the user? The only thing I can think of is save the file and maybe some JavaScript way can click the button or start the download automatically.Michaelamichaele

© 2022 - 2024 — McMap. All rights reserved.