HTML IFrame not allowed to download file

14.5k views Asked by At

im trying to download a file that constructs itself based on the value it recives. This is my code

<html>
<head>
<script>
    var myList=[];
    window.onmessage = function(event){
    if (event.data) {
      myList = event.data;
      if (myList.length>0) {
        buildHtmlTable();
      }
    }
    else {
      myList = [];
    }
  };
 function buildHtmlTable() {
     var columns = addAllColumnHeaders(myList);
 
     for (var i = 0 ; i < myList.length ; i++) {
         var row$ = $('<tr/>');
         for (var colIndex = 0 ; colIndex < columns.length ; colIndex++) {
             var cellValue = myList[i][columns[colIndex]];
 
             if (cellValue == null) { cellValue = ""; }
 
             row$.append($('<td/>').html(cellValue));
         }
         $("#excelDataTable").append(row$);
         
     }
     return exportF(); // Make Excel file download now
 }
 function addAllColumnHeaders(myList)
 {
     var columnSet = [];
     var headerTr$ = $('<tr/>');
 
     for (var i = 0 ; i < myList.length ; i++) {
         var rowHash =`enter code here` myList[i];
         for (var key in rowHash) {
             if ($.inArray(key, columnSet) == -1){
                 columnSet.push(key);
                 headerTr$.append($('<th/>').html(key));
             }
         }
     }
     $("#excelDataTable").append(headerTr$);
 
     return columnSet;
 }
 function exportF() {
  var table = document.getElementById("excelDataTable");
  var html = table.outerHTML;

  var url = 'data:application/vnd.ms-excel,' + escape(html);
  var link = document.getElementById("downloadLink");
  link.setAttribute("href", url);
  link.setAttribute("download", "export.xls"); // Choose the file name here
  link.click(); // Download your excel file   
  return false;
}
 </script>
</head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body onLoad="">
    <table id="excelDataTable" border="1">
     </table>
     <a style="display: none" id="downloadLink"></a>
</body>

</html>

The code itself works, but the error i get is "Download is disallowed. The frame initiating or instantiating the download is sandboxed, but the flag ‘allow-downloads’ is not set. See https://www.chromestatus.com/feature/5706745674465280 for more details."

What can i do to work around this? It feels like ive tried everything i can get my hands on but nothing seems to work for it to download

3

There are 3 answers

0
Kaiido On

As the warning message says, you can't initialize downloads from a sandboxed iframe if it doesn't have the allow-downloads permission.

All solutions will imply having access to the page where the iframe is displayed (hereafter "the embedder").

The easiest and recommended way,

is to ask the embedder to add this permission when they define their iframe:

<iframe src="yourpage.html" sandbox="allow-scripts allow-downloads"></iframe>

An other way would be to ask them to not sandbox that iframe at all,

<iframe src="yourpage.html"></iframe>

but I guess that if they did, it's because they don't trust your page enough.


Finally a more complex way would be to pass the generated file back to the parent window.

For this you'd need to define a new API with your clients.
You could obviously just emit a global message event back to them, but I guess the cleanest is to make them pass a MessageChannel's MessagePort along with the myList data, so they can wait for the response there easily and be sure they'll only catch the response and no other unrelated message.

So in the embedder page they'd do

frame.onload = (evt) => {
  const channel = new MessageChannel();
  // handle the response from the iframe
  channel.port2.onmessage = (evt) => {
    const file = evt.data;
    saveAs( file, "file.html" ); // the embedder is reponsible to initialize the download
  };
  frame.contentWindow.postMessage( embedders_data, [ channel.port1 ] );
};

And in your page you'd do

window.onmessage = (evt) => {
  const myList = evt.data;
  // get the MessageChannel's port out of the transfer-list
  const port = evt.ports[ 0 ];
  // buildHtmlTable has to return the final file, not to make it download
  const file = buildHtmlTable( myList );
  if( port ) { 
    port.postMessage( file ); // send back to embedder
  }
};

See it as a live plnkr.

Ps: note that your files are not xlsx files but HTML markup.

1
Grebyn On

Thanks for answers, i didnt find a sollution with the recomended answers. What i did is that i made a completely new page, instead of initializing a html iframe i redirected the current window to the new page i created. The new page took a variable from "www.page.com/?page={value} and downloaded what i needed from there instead. Its messy but it works so if anyone else has this problem i recomend this if you are using wix.

0
Pedro Ribeiro On

The correct answer

Under normal circumstances Kaiido's answer is indeed the correct solution to your problem. They will NOT work in your case though.

The answer that will work on Wix

Since you are using Wix there is no way for you to directly edit the Sandbox attribute of the iframe element. This is just how Wix does things. You can, however, use custom code (only applies to premium websites) to get the class name of the iframe and programatically use javascript to set the new attribute to the existing iframe.

You must use the web inspector to find out the class name (iframes in Wix do not have ids) then add "allow-downloads" to the sandbox attribute. You might then need to reload the iframe using js as well. Go to your website's settings -> Custom Code -> Create custom code at the end of the body tag


If you do not have a premium website then you unfortunately cannot do this. This is due to Wix's own limitations as a platform. If this is an absolute "must" for you project, I recommend you to not use Wix since they limit your freedom as a developer when it comes to working with technologies that were not made by them. Not to mention that they lock features such as custom elements behind a pay wall. So we can't even test our ideas before committing to a hosting plan. For anyone reading this in the future, take this into consideration and look into other platforms.