I am attempting to utilize ABP.IO Blazor WASM to download files from Azure Blob storage service. I am using azurite emulator to test/play around.
Using Swagger, I can accurately get a list of all blobs in the container, and I can also get the file to download.
On the UI side, I am able to list the files, but when I try to download the file, I get a 404 error.
I have minimally reproduced the project, which I can add you to if requested.
I don't want to paste all of the code here: but I will provide the basics just in case it helps!
API
[HttpGet("{name}")]
public async Task<IActionResult> Get(string name)
{
var result = await _fileService.GetBlobAsync(Uri.UnescapeDataString(name));
return File(result.Content, result.ContentType, result.Name);
}
FileService
public async Task<ContentDto> GetBlobAsync(string blobName)
{
var container = new BlobContainerClient(_azureConnectionString, _containerName);
var blobClient = container.GetBlobClient(blobName);
if (await blobClient.ExistsAsync())
{
//var blob = await blobClient.DownloadAsync(); <- depreciated
// UPDATED
var blob = await blobClient.DownloadContentAsync();
var content = new ContentDto()
{
//Content = await blob.Value.Content.CreateMemoryStreamAsync(),
Content = blob.Value.Content.ToArray(),
ContentType = blob.Value.ContentType,
Name = blobClient.Name
};
return content;
}
throw new UserFriendlyException("Blob file not found.", "AB001", blobClient.Name);
}
ContentDto
public class ContentDto
{
//public Stream Content { get; set; }
public byte[] Content {get; set;} // probably not prefered
public string Name { get; set; }
public string ContentType { get; set; }
}
Blazor page (Files.razor.cs) related to the download
private async Task Download(string name, string contentType)
{
try
{
var contentDto = await _fileService.GetBlobAsync(name);
// CORS is enabled, but when looking at API vs what is transmitting, the Port
// numbers are different so I reset the baseaddress to test.
// I cannot get past the response with or without changing the baseaddress.
// Http.BaseAddress = new Uri("https://localhost:{port}/api/Files/");
//var response = await Http.GetByteArrayAsync(name);
await JSRuntime.InvokeVoidAsync("download", contentDto.Name, contentDto.Content);
// Errors out now with 'unable to find "download" in Microsoft.JSInterop'
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
OnInitializedAsync()
protected override async Task OnInitializedAsync()
{
_jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./scripts/file.js");
await base.OnInitializedAsync();
}
Blazor page (Files.razor) I am using Blazorise for UI
<DropdownItem Clicked="() => Download(context.FileUrl, context.ContentType)">
Download
</DropdownItem>
Script file (even though I can't get to that point)
export function download(options) {
var bytes = new Uint8Array(options.byteArray);
var blob=new Blob([bytes], {type: options.contentType});
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=options.fileName;
link.click();
link.remove();
window.URL.revokeObjectURL(link.href);
}
Answer: I updated my code. The DownloadAsync() was depreciated and I replaced it with DownloadContentAsync(). I updated my ContentDto to be a byte[] instead of a Stream and it returns it all. However my issue now is I get an error when trying to execute the javascript file. I added my OnInitializedAsync to show I'm importing it. I am Injecting the IJSRuntime so I don't know why it wouldn't find it.
So after some reading, you have to inject the javascript file into their global.js file through the use of their BundleContributor class and then run a command in the terminal to get it bundled.