Search code examples
ember.jsember-data

How to get a file from the backend withou getting it JSON-parsed


I’m able to get an xlsx file from my rails backend with a GET-Request to “/companies/export_xslx”, now I’m facing the problem of getting the file passed the JSON parser. For every request the console shows “JSON.parse: unexpected character at line 1 column 1 of the JSON data”.

This is my setup:

//company model ...
exportXlsx: function() {
  const adapter = this.store.adapterFor('company');
  return adapter.exportXlsx();
}

//adapters/company.js
import DS from 'ember-data';
import TokenAuthorizerMixin from 'ember-simple-auth-token/mixins/token-authorizer';

export default DS.JSONAPIAdapter.extend(TokenAuthorizerMixin, {

  exportXlsx() {
    const url = 'companies/export_xlsx';
    return this.ajax(url, 'GET',
      { dataType: 'text',
         accepts: { xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
       } });
  }
});

I’ll try to alter the default accept header but the requests gets sent with “Accept: application/vnd.api+json”.

I already tried different approaches with “ember-custom-actions” or “ember-cli-file-saver”, they all failed with the JSON.parse… response.


Solution

  • I've found a solution. I tackle the problem in the component with a download service:

    // components/companies-download.js
    import Component from '@ember/component';
    import { computed } from '@ember/object';
    import { inject as service } from '@ember/service';
    
    export default Component.extend({
    
      download: service(),
    
      actions: {
    
        downloadXlsx() {
          let url = `/companies/export_xlsx`;
          this.get('download').file(url);
        }
    
      }
    });
    
    
    
    // services/download.js
    import Service from "@ember/service";
    import { inject as service } from '@ember/service';
    
    export default Service.extend({
    
      session: service(),
    
      file(url) {
        let xhr = new XMLHttpRequest();
        xhr.responseType = 'blob';
        xhr.onload = () => {
          let [, fileName] = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(
            xhr.getResponseHeader("Content-Disposition")
          );
          let file = new File([xhr.response], decodeURIComponent(fileName));
          let link = document.createElement('a');
          link.style.display = 'none';
          link.href = URL.createObjectURL(file);
          link.download = file.name;
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
        };
        xhr.open('GET', url);
        xhr.setRequestHeader(
          'Authorization',
          'Bearer ' + this.get('session.data.authenticated.token')
        );
        xhr.send();
      }
    });