Trouble downloading file from DB

I using the file input component to store files in an MSSQL db table.
The db field is nvarchar(max)
I can input the file and I can see the string starts data:application/pdf;base64,JVBERi0xLjcN
When I try and download it I get an error.

image

My code to download this file is:

public async Task<IActionResult> OnPostDownloadAsync(int? id)
        {
            var myDoc = dbContext.TblDocuments.FirstOrDefault(m => m.DocumentID == id);// rDefaultAsync(m => m.Id == id);
            if (myDoc == null)
            {
                return NotFound();
            }

            if (myDoc.Document == null)
            {
                return null;
            }
            else            {
                byte[] byteArr = Encoding.UTF8.GetBytes(myDoc.Document);

                string mimeType = "application/pdf";
                return new FileContentResult(byteArr, mimeType)
                {
                    FileDownloadName = $"Invoice {myDoc.DocumentName}.pdf"
                };
            }

        }

What is the decode format I need?

I have tried:

  • Convert.FromBase64 with and without removing the characters up to the comma,
  • ASCII but my PDF viewer either renders and empty page and says there is an error or I get a full blown webpage error.

Thanks John

The format used is Data URI which is base64 encoded. If you need to make a binary file from it you have to remove the prefix and decode it to binary. I recommend searching online for some working code example.

Atanas,

I got the controller to work with the data from the db.

When i post this from a form all works as expected and the PDF apears as it should . Now i want to call this from a button in a datagrid. I have almost got it to works but not quite.

When i click the yellow button in my datagrid it routes correctly to the controller and it returns but I do not get the PDF.


        [Inject]
        NavigationManager navigationManager { get; set; }
        
        public async Task PostFormAsync(int docID)
        {
            HttpClient client = new HttpClient();

            var Json = JsonConvert.SerializeObject(docID);
            var stringContent = new StringContent(Json, Encoding.UTF8, "application/Json");

            var response= client.PostAsync($"{navigationManager.BaseUri}api/Download/DownloadFile/{docID}", stringContent);

What am I missing to get the PDF to finally display?

thanks

John

@johnmu,

Blazor works through web socket - you cannot download a file. You will need to perform full navigation similar to export from our CRUD pages.

What.. there is no way to create a download link in the datagrid column that will make the base64 encoded file (pdf file) download to the end user? I need to be able to do this and have been looking for the solution. What are my options if it's not possible in a Radzen Blazor app? Is it possible if I create a Radzen Angular app?

Thanks

You’ve missed to read the second part of my reply:

OK thanks for that.
Do you guys have any detailed/instructions for doing this or a link where I can find a working example or code? Seems like several people are looking for this.

All I need is to have the datagrid column that shows the file (data:application/pdf) display a "Download" link. When clicked it can either display the PDF in the browser or download it, either way would be good.

image
I have tried many things and found this post very good and it almost worked. For what ever reason adding the <a [href]="data.Attachment | safe" download="Attachment.${getExtension(data.Attachment)}">download to the template field caused an issue.
Maybe it didn't work because it's 2-3 years old? It makes no difference to me if my app is Blazor or Angular. I'm learning as I go and really appreciate your help and guidance.

Thanks

The code you've posted is for Angular - completely different framework with completely different architecture and syntax. I'm posting again my previous reply:





In short. You need to use NavigationManager NavigateTo() with forceLoad = true to request a controller where you can return a file. You will need an additional parameter to retrieve the relevant record/field value.

from my form is added this to a custom class

    [Inject]
    NavigationManager navigationManager { get; set; }
    public async Task<bool> GetDocument(int documentid)
    {
        ExportService exportService = new ExportService(dbContext, navigationManager);

        await exportService.ShowDocument(documentid);
        return true;
    }

then in a new service class

    public async Task ShowDocument(int documentid)
    {
        Query query = new Query();
        navigationManager.NavigateTo(query.ToUrl($"export/report/document/download/{documentid}") ,true);
    }

you will need to register this service class in a partial startup class

public partial class Startup
{
    partial void OnConfigureServices(IServiceCollection services)
    {
        services.ConfigureApplicationCookie(options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
        });

        services.AddScoped<ErrorLogService>();

// ************* this is the bit you need to remember *******************

   services.AddScoped<ExportService>();
    }

then add a new controller and set your routes

    [HttpGet("/export/report/tblopportunities/excel")]
    [HttpGet("/export/report/tblopportunities/excel(fileName='{fileName}')")]
    [HttpGet("/export/report/tblopportunities/excel(fileName='{fileName}')/{ownerid}")]
    public FileStreamResult ExportTblOpportunitiesToExcel( string fileName = null,int ownerid=0)
    {
        var _pipe = GetPipeline(ownerid);

        return ToExcel(_pipe.ToList().AsQueryable(), fileName) ;
    }

and it should all work. My code is a bit messy and I am a amateur coder but this does work

I used this to return PDFs

[HttpGet("/export/report/document/download/{documentid}")]
public FileStreamResult ShowDocument(int documentid)
{
var myDoc = dbContext.TblDocuments.FirstOrDefault(m => m.DocumentID == documentid);

        if (myDoc == null)
        {
            return null;
        }

        if (myDoc.Document == null)
        {
            return null;
        }


        byte[] byteArr = Convert.FromBase64String(myDoc.Document.Substring(myDoc.Document.LastIndexOf(',') + 1));
        var stream= new MemoryStream(byteArr);

        var mimeType = "application/pdf";


        var result = new FileStreamResult(stream, mimeType)
        {
            FileDownloadName = myDoc.DocumentName + ".pdf"
        };

        return result;

Thanks @enchev,
Yes I realize the code I posted was for Angular and I have been trying to get that to work in a Angular app that I've been testing with. I have also been trying to figure out how to get similar to work in Blazor using a download link in the datagrid. It seems quite clear that this is just not possible unless I use a button as you have described.
Thank you for posting the code and helping with that.

Thanks johnmu, works great. Question though: I am trying to have an "inline" content disposition for PDFs files so that they would automatically open in the browser when I download them...

Any thoughts on how to do that?

Thanks!

Sorry I haven’t looked into that. Be interested if you get a solution.

Thanks

John

You can check this Stackoverflow question and the accepted answer: https://stackoverflow.com/questions/38897764/asp-net-core-content-disposition-attachment-inline