title: force-download xlsx from ajax response not working
body: <p>I have this little problem with downloading my xlsx-file.
  I am sending my request for the file over jquery Ajax and on the backend the data is correctly collected and assembled to a xlsx-file. So now on its way back to the frontend i am setting all the headers in preparation to force download the file, but the download never starts.</p>
  
  <p>These are the response headers of my request:</p>
  
  <pre><code>Connection      Keep-Alive
  Content-Disposition attachment; filename="export.xlsx"
  Content-Length  346420
  Content-Type    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
  Date            Mon, 23 Nov 2015 13:23:30 GMT
  Keep-Alive      timeout=5, max=91
  Server          Apache/2.4.16 (Win32) OpenSSL/1.0.1p PHP/5.6.12
  Set-Cookie      &lt;cookiesettings&gt;
  content-transfer-encoding   binary
  x-powered-by    PHP/5.6.12
  </code></pre>
  
  <p>imho the download should start immediately, but nothing happens.</p>
  
  <p>EDIT:
  Until now I used a form submit, but the data amount is really big so the time which is needed to assemble the file is also really long and sometimes a couple of minutes or even an hour, so this was no longer possible.</p>
  
  <p>So I built a java-job to build the file and startet an ajax snippet which asks for completion every second or so.</p>
  
  <p>So here is my Code.
  Frontend:
  This is called on button-click</p>
  
  <pre><code>download: function (type, maximum) {
          var
              self = this,
              oParams = this.oTable.oApi._fnAjaxParameters(this.oTable.fnSettings()),
              aoPost = [
                  { 'name': 'exportType', 'value': type },
                  { 'name': 'exportMax', 'value': maximum },
                  { 'name': 'handleId', 'value': self.options.handleId }
              ],
              nIFrame, nContentWindow, nForm, nInput, i
          ;
  
          // Call a self made function to get extra search parameters
          // without call an data update AJAX call.
          self.oTable.fnSettings().addAdditionalSearchData(oParams);
  
          // Create an IFrame to do the request
          nIFrame = document.createElement('iframe');
          nIFrame.setAttribute('id', 'RemotingIFrame');
          nIFrame.style.border = '0px';
          nIFrame.style.width = '0px';
          nIFrame.style.height = '0px';
  
          document.body.appendChild(nIFrame);
          nContentWindow = nIFrame.contentWindow;
          nContentWindow.document.open();
          nContentWindow.document.close();
  
          nForm = nContentWindow.document.createElement('form');
          nForm.className = 'export-table';
          nForm.setAttribute('method', 'post');
  
          // Add POST data.
          var formData = {};
          for (i = 0; i &lt; aoPost.length; i++) {
              nInput = nContentWindow.document.createElement('input');
              nInput.setAttribute('name', aoPost[ i ].name);
              nInput.setAttribute('type', 'text');
              nInput.value = aoPost[ i ].value;
              nForm.appendChild(nInput);
              formData[aoPost[ i ].name] = aoPost[ i ].value;
          }
  
          // Add dataTables POST.
          for (i = 0; i &lt; oParams.length; i++) {
              nInput = nContentWindow.document.createElement('input');
              nInput.setAttribute('name', oParams[ i ].name);
              nInput.setAttribute('type', 'text');
              nInput.value = oParams[ i ].value;
              nForm.appendChild(nInput);
              formData[oParams[ i ].name] = oParams[ i ].value;
          }
  
          nForm.setAttribute('action', '/service/exportTableData');
  
          // Add the form and the iFrame.
          nContentWindow.document.body.appendChild(nForm);
  
          // Send the request.
          //nForm.submit();
  
  
          // Send the request.
          var form = $(nContentWindow.document.body).find('form.export-table');
  
          var jobId = 0;
  
          form.ajaxForm(
              {
                  'showMessagesOnSuccess': false
              },
              {
                  'getData': function () {
                      return formData;
              }
              }
          ).data('ajaxForm').submit();
      }
  </code></pre>
  
  <p>The Ajax request on submit:</p>
  
  <pre><code>$.ajax({
      type: 'POST',
      url: self.handler.getServiceUrl(),
      timeout: GLOBALS.AJAX_REQUEST_TIMEOUT,
      cache: false,
      data: (&lt;get the Data&gt;)
      ,
      success: function (response) {
          if (response.success === true) {
              // Check if we have to wait for a result.
              if (response.jobId !== undefined &amp;&amp; response.jobId !== 0) {
                  self.checkJobStatus(response.jobId);
              } else {
                  &lt;success - show some messages&gt;
              }
          } else {
              self.handler.error(response);
          }
      },
      error: function () {
          &lt;Show error Message&gt;
      }
  });
  </code></pre>
  
  <p>The CheckJobStatus:</p>
  
  <pre><code>checkJobStatus: function (jobId) {
      var self = this;
      $.ajax({
          type: 'POST',
          timeout: GLOBALS.AJAX_REQUEST_TIMEOUT,
          cache: false,
          data: { 'jobId': jobId },
          url: self.handler.getServiceUrl(),
          success: function (response) {
              if(response !== null &amp;&amp; response.data !== undefined) {
                  if (response.data.isFinished === true) {
                      if (response.success === true) {
                          // Check if we have to wait for a result.
                          self.handler.success(response);
                      } else {
                          self.handler.error(response);
                      }
                  } else if (response.success === true &amp;&amp; response.data !== null) {
                      setTimeout(
                          function () {
                              self.checkJobStatus(jobId);
                          },
                          500
                      );
                  } else {
                      Helper.logFrontendError();
                  }
              } else if (response !== null &amp;&amp; response.success === true) {
                  setTimeout(
                      function () {
                          self.checkJobStatus(jobId);
                      },
                      1000
                  );
              } else {
                      Helper.logFrontendError();
              }
          },
          error: function (response) {
                  Helper.logFrontendError();
          }
      });
  }
  </code></pre>
  
  <p>Backend - php:</p>
  
  <pre><code>(...)
  if ($action == 'exportTableData' || $action == 'exportChartData') {
              $responseData = $service-&gt;execute();
              if(isset($responseData-&gt;data['contentType']) &amp;&amp; $responseData-&gt;data['contentType'] != null &amp;&amp; isset($responseData-&gt;data['data'])) {
                  $this-&gt;sendTextData($responseData-&gt;data['contentType'], $responseData-&gt;data['data']);
              } else {
                  $this-&gt;sendJsonData($responseData);
              }
          } else {
              $this-&gt;sendJsonData($service-&gt;execute());
          }
  (...)
  
  
  private function sendTextData($contentType, $data) {
      $this-&gt;set('filename', 'export.xlsx');
      $this-&gt;set('data', $data);
      $this-&gt;response-&gt;type($contentType);
      $this-&gt;render('/Layouts/excel', 'excel');
  }
  
  
  (...)
  $handlerResult = new HandlerResult();
  
      if($dataServiceResult == null) {
          $service = new DataService();
              $dataServiceResult = $service-&gt;exportTableData(
                      $controller-&gt;Auth-&gt;User('id'),
                      json_encode($request-&gt;data),
                      null
              );
      } else {
          if ($dataServiceResult-&gt;header-&gt;resultKey == 0) {
              $handlerResult-&gt;wsData['data'] = $dataServiceResult-&gt;data;
              $handlerResult-&gt;wsData['contentType'] = $dataServiceResult-&gt;contentType;
          }
      }
      $handlerResult-&gt;wsResultHeader = $dataServiceResult-&gt;header;
      return $handlerResult; // ++++ this result returns to the first codeblock in this section ++++
  </code></pre>
  
  <p>Backend - java - This is where the File is assembled:</p>
  
  <pre><code>(...)
  if (jobId &gt; 0) {
              FrontendJobStatus status = FrontendJobQueue.getJobStatus(context.userId, jobId);
              this.result = (WSExportTableDataResult) status.getResult();
              logger.info((this.result.data == null) ? "ByteArray is EMPTY" : "ByteArray is NOT EMPTY");
          } else {
              this.jobId = FrontendJobQueue.addJob(this.context.userId, new ExportTableDataJob(this.context, this.postData));
              this.result.header.jobId = this.jobId;
          }
  (...)
  
  The Jop:
  &lt;Workbook assembly&gt;
  ByteArrayOutputStream out = new ByteArrayOutputStream();
  wb.write(out);
  this.result.data = out.toByteArray();
          this.result.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
          // this.result.contentType = "application/vnd.ms-excel";
  
          this.result.setResultHeader(APIConstants.RESULT_SUCCESS);
  </code></pre>
  
  <p>Layout/excel:</p>
  
  <pre><code>&lt;?php
  header('Content-Disposition: attachment; filename="'.$filename.'"');
  header('Content-Transfer-Encoding: binary');
  ob_clean();
  echo $data;
  </code></pre>
  
  <p>EDIT 2:
  So I tried to open a new window on success with the Data, and i could start the download, but the file ist no valid xlsx File anymore.</p>
  
  <pre><code>var reader = new FileReader();
      var blob = new Blob([response.responseText], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
      reader.readAsDataURL(blob);
  
      reader.onloadend = function (e) {
          window.open(reader.result, 'Excel', 'width=20,height=10,toolbar=0,menubar=0,scrollbars=no', '_blank');
      }
  </code></pre>
  
  <p>Any Ideas?</p>