3

I am trying to create a .html file using Apex and when a user clicks a button on the front-end, they can download the .html file. Below is the code I have and when I am loading the component and clicking on the "Download Report" button, the button is just displaying the text [object Object] instead of downloading the attachment "attach". How can I make the report download to a users' computer? Appreciate your help on this!

Here's the lightning component:

<aura:component implements="force:appHostable, 
                        flexipage:availableForAllPageTypes,
                        flexipage:availableForRecordHome,
                        force:hasRecordId"
            access="global"
            controller="DownloadController">

    <aura:attribute name="downloadURL" type="String"/> 

    <lightning:button label="Download Report" 
                    iconName="utility:download"
                    iconPosition="right"
                    onclick="{!c.handleClick}"/>

    <ul>
        {!v.downloadURL}
    </ul>

</aura:component>   

Here's the JS Controller:

({
    handleClick : function(component, event, helper) {
    var action = component.get("c.ReportDownloadController"); //getting attachment from apex

    action.setCallback(this, function(response) {
           let state = response.getState();
           if (state === "SUCCESS") {
                component.set("v.downloadURL", response.getReturnValue());
               // console.log(response.getReturnValue());
           } else {
                console.log("Failed with state: " + state);
           }
    })

    $A.enqueueAction(action);
    }
})

And here's the Apex Controller:

public with sharing class DownloadController {

    @AuraEnabled
    public static Attachment DownloadController() {
        String txt = '<b>hello</b>';
        Blob txtblob = Blob.valueOf(txt); //converting string to a blob
        Attachment attach = new Attachment();
        attach.Name = 'text.html';
        attach.Body = txtblob;
        attach.ContentType = 'text/html';
        return attach;    
    }
}
3

The Attachment object is for saving data to the database. It has a number of fields, which is why you're seeing [object Object]. Instead, you should be able to do so with code like this:

@AuraEnabled public static String getFile() {
  String htmlText = '<html>...</html>';
  return EncodingUtil.base64Encode(Blob.valueOf(htmlText));
}

action.setCallback(this, function(response) {
       let state = response.getState();
       if (state === "SUCCESS") {
           let downloadLink = document.createElement("a");
           downloadLink.href = "data:text/html;base64,"+response.getReturnValue();
           downloadLink.download = "filename.html";
           downloadLink.click();
       } ...
  • 1
    thanks for the help! This is precisely what I was trying to do. Just a small typo in the code above. Instead of download.click(), need to use downloadLink.click(). – Illusion Jan 13 at 3:13
  • @Illusion Yeah, thanks for that. Fixed the typo. Glad I was able to help! – sfdcfox Jan 13 at 3:15
  • This solution doesn't work in Firefox. I have proposed a new answer -- see below. – Matthew Souther May 23 at 20:43
0

sfdcfox's answer got me most of the way there, but I discovered that his approach to calling the click() method on an element won't work in Firefox.

Reference: https://stackoverflow.com/questions/32225904/programmatical-click-on-a-tag-not-working-in-firefox

To address the Firefox limitation, you have to explicitly add the created element to the DOM.

Apex Controller:

@AuraEnabled public static String getFile() {
  String htmlText = '<html>...</html>';
  return EncodingUtil.base64Encode(Blob.valueOf(htmlText));
}

Javascript Controller/Helper:

action.setCallback(this, function(response) {
    let state = response.getState();
    if (state === "SUCCESS") {
        let downloadLink = document.createElement("a");
        downloadLink.setAttribute("type", "hidden");
        downloadLink.href = "data:text/html;base64,"+response.getReturnValue();
        downloadLink.download = "filename.html";
        document.body.appendChild(downloadLink);
        downloadLink.click();
        downloadLink.remove();
    } ...

This works in both Chrome and Firefox, until your file gets to be ~2 MB. It doesn't address an apparent Chrome limitation that prevents the download of files greater than 2 MB, based on URL size limitations and the fact that we are utilizing data URLs.

Reference: https://stackoverflow.com/questions/695151/data-protocol-url-size-limitations

Here is a more complex approach that addresses the Chrome limitation. It seems to work, although I haven't wrapped my head around all the elements.

Reference: https://stackoverflow.com/questions/10473932/browser-html-force-download-of-image-from-src-dataimage-jpegbase64/10473992#10473992

action.setCallback(this, function(response) {
    let state = response.getState();
    if (state === "SUCCESS") {
        var base64Url = "data:text/html;base64,"+response.getReturnValue();
        var decodedData = atob(response.getReturnValue());
        var arrayBuffer = new ArrayBuffer(decodedData.length);
        var view = new UintArray(arrayBuffer);
        for (var i = 0; i < decodedData.length; i++) {
            view[i] = decodedData.charCodeAt(i) & 0xff;
        }
        var blob = new Blob([arrayBuffer], {type: 'application/octet-stream'});
        var blobUrl = URL.createObjectURL(blob);
        let downloadLink = document.createElement("a");
        downloadLink.setAttribute("type", "hidden");
        downloadLink.href = blobUrl;
        downloadLink.download = "filename.html";
        document.body.appendChild(downloadLink);
        downloadLink.click();
        downloadLink.remove();
    } ...

While this one works, it seems like a lot of code to me. There are quite a few dependencies that could potentially break under various scenarios. I'd be happy if someone could provide a shorter version that achieves the same ends.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.