【问题标题】:How to save .xlsx data to file as a blob如何将 .xlsx 数据作为 blob 保存到文件
【发布时间】:2016-05-01 19:16:37
【问题描述】:

我对这个问题有类似的问题(Javascript: Exporting large text/csv file crashes Google Chrome):

我正在尝试保存 excelbuilder.jsEB.createFile() 函数创建的数据。如果我将文件数据作为链接的href 属性值,它可以工作。但是,当数据很大时,它会使 Chrome 浏览器崩溃。代码是这样的:

//generate a temp <a /> tag
var link = document.createElement("a");
link.href = 'data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,' + encodeURIComponent(data);
link.style = "visibility:hidden";
link.download = fileName;

document.body.appendChild(link);
link.click();
document.body.removeChild(link);

我使用 excelbuilder.js 创建数据的代码如下:

var artistWorkbook = EB.createWorkbook();
var albumList = artistWorkbook.createWorksheet({name: 'Album List'});

albumList.setData(originalData); 

artistWorkbook.addWorksheet(albumList);

var data = EB.createFile(artistWorkbook);

正如类似问题 (Javascript: Exporting large text/csv file crashes Google Chrome) 的答案所建议的,需要创建一个 blob。

我的问题是,文件中保存的不是 Excel 可以打开的有效 Excel 文件。我用来保存blob 的代码是这样的:

var blob = new Blob(
    [data],
    {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,"}
);

// Programatically create a link and click it:
var a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = fileName;
a.click();

如果我将上述代码中的[data]替换为[Base64.decode(data)],保存的文件中的内容看起来更像是预期的excel数据,但仍然无法被Excel打开。

谢谢!

【问题讨论】:

    标签: javascript xlsx bloburls


    【解决方案1】:

    我和你有同样的问题。事实证明,您需要将 Excel 数据文件转换为 ArrayBuffer。

    var blob = new Blob([s2ab(atob(data))], {
        type: ''
    });
    
    href = URL.createObjectURL(blob);
    

    s2ab(字符串到数组缓冲区)方法(我从https://github.com/SheetJS/js-xlsx/blob/master/README.md得到的)是:

    function s2ab(s) {
      var buf = new ArrayBuffer(s.length);
      var view = new Uint8Array(buf);
      for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
      return buf;
    }
    

    【讨论】:

    • 这似乎不适用于原始数据(即服务器按原样返回的 excel 文件)。实际上“s”返回未定义。有什么线索吗?
    • @Ron T atob 是什么?
    • @tjvg1991:就是函数,window.atob()。这里解释了它的含义和作用:stackoverflow.com/questions/33854103/…
    • 确保您的返回文件内容已经在 base64 编码的字符串中才能正常工作。这取决于您的服务器缓冲区对象创建逻辑。 ```
    【解决方案2】:

    答案above 是正确的。请确保您在 data 变量中有一个 base64 字符串数据,没有任何前缀或类似原始数据的东西。

    这是我在服务器端(asp.net mvc 核心)所做的:

    string path = Path.Combine(folder, fileName);
    Byte[] bytes = System.IO.File.ReadAllBytes(path);
    string base64 = Convert.ToBase64String(bytes);
    

    在客户端,我做了以下代码:

    const xhr = new XMLHttpRequest();
    
    xhr.open("GET", url);
    xhr.setRequestHeader("Content-Type", "text/plain");
    
    xhr.onload = () => {
        var bin = atob(xhr.response);
        var ab = s2ab(bin); // from example above
        var blob = new Blob([ab], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;' });
    
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = 'demo.xlsx';
    
        document.body.appendChild(link);
    
        link.click();
    
        document.body.removeChild(link);
    };
    
    xhr.send();
    

    它对我来说非常有效。

    【讨论】:

      【解决方案3】:

      这适用于:v0.14.0 of https://github.com/SheetJS/js-xlsx

      /* generate array buffer */
      var wbout = XLSX.write(wb, {type:"array", bookType:'xlsx'});
      /* create data URL */
      var url = URL.createObjectURL(new Blob([wbout], {type: 'application/octet-stream'}));
      /* trigger download with chrome API */
      chrome.downloads.download({ url: url, filename: "testsheet.xlsx", saveAs: true });
      

      【讨论】:

      • 谢谢你,大卫。我试图模拟上传的 Excel 文件进行测试,这使我能够让它工作。我不得不将类型更改为“应用程序/二进制”,但之后效果很好。
      【解决方案4】:

      我找到了适合我的解决方案:

      const handleDownload = async () => {
         const req = await axios({
           method: "get",
           url: `/companies/${company.id}/data`,
           responseType: "blob",
         });
         var blob = new Blob([req.data], {
           type: req.headers["content-type"],
         });
         const link = document.createElement("a");
         link.href = window.URL.createObjectURL(blob);
         link.download = `report_${new Date().getTime()}.xlsx`;
         link.click();
       };
      

      我只是点了一个responseType: "blob"

      【讨论】:

      • 谢谢!适用于 laravel Maatwebsite\Excel 和 axios。
      【解决方案5】:

      这是我使用 fetch api 的实现。服务器端点发送字节流,客户端接收字节数组并从中创建一个 blob。然后将生成一个 .xlsx 文件。

      return fetch(fullUrlEndpoint, options)
        .then((res) => {
          if (!res.ok) {
            const responseStatusText = res.statusText
            const errorMessage = `${responseStatusText}`
            throw new Error(errorMessage);
          }
          return res.arrayBuffer();
        })
          .then((ab) => {
            // BE endpoint sends a readable stream of bytes
            const byteArray = new Uint8Array(ab);
            const a = window.document.createElement('a');
            a.href = window.URL.createObjectURL(
              new Blob([byteArray], {
                type:
                  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
              }),
            );
            a.download = `${fileName}.XLSX`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
          })
          .catch(error => {
            throw new Error('Error occurred:' + error);
          });
      

      【讨论】:

      • 终于,经过一整天的搜索和尝试不同的解决方案。唯一适用于 fetch 的东西。我想重点在于 Unit8Array 女巫,我必须探索和了解实际在做什么:)
      【解决方案6】:

      我的解决方案。

      步数:1

      <a onclick="exportAsExcel()">Export to excel</a>
      

      步骤:2

      我正在使用文件保护程序库。

      阅读更多:https://www.npmjs.com/package/file-saver

      npm i file-saver
      

      步数:3

      let FileSaver = require('file-saver'); // path to file-saver
      
      function exportAsExcel() {
          let dataBlob = '...kAAAAFAAIcmtzaGVldHMvc2hlZXQxLnhtbFBLBQYAAAAACQAJAD8CAADdGAAAAAA='; // If have ; You should be split get blob data only
          this.downloadFile(dataBlob);
      }
      
      function downloadFile(blobContent){
          let blob = new Blob([base64toBlob(blobContent, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')], {});
          FileSaver.saveAs(blob, 'report.xlsx');
      }
      
      function base64toBlob(base64Data, contentType) {
          contentType = contentType || '';
          let sliceSize = 1024;
          let byteCharacters = atob(base64Data);
          let bytesLength = byteCharacters.length;
          let slicesCount = Math.ceil(bytesLength / sliceSize);
          let byteArrays = new Array(slicesCount);
          for (let sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
              let begin = sliceIndex * sliceSize;
              let end = Math.min(begin + sliceSize, bytesLength);
      
              let bytes = new Array(end - begin);
              for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
                  bytes[i] = byteCharacters[offset].charCodeAt(0);
              }
              byteArrays[sliceIndex] = new Uint8Array(bytes);
          }
          return new Blob(byteArrays, { type: contentType });
      }
      

      为我工作。 ^^

      【讨论】:

        【解决方案7】:

        试试FileSaver.js 库。它可能会有所帮助。

        https://github.com/eligrey/FileSaver.js/

        【讨论】:

        • 谢谢回复,结果还是一样。
        【解决方案8】:

        这个答案取决于前端和后端都具有兼容的返回对象,因此同时提供前端和后端逻辑。确保后端返回数据采用 base64 编码,以使以下逻辑正常工作。

        // Backend code written in nodejs to generate XLS from CSV
        import * as XLSX from 'xlsx';
        
        export const convertCsvToExcelBuffer = (csvString: string) => {
          const arrayOfArrayCsv = csvString.split("\n").map((row: string) => {
            return row.split(",")
          });
          const wb = XLSX.utils.book_new();
          const newWs = XLSX.utils.aoa_to_sheet(arrayOfArrayCsv);
          XLSX.utils.book_append_sheet(wb, newWs);
          const rawExcel = XLSX.write(wb, { type: 'base64' })
          // set res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') 
          // to include content type information to frontend. 
          return rawExcel
        }
        
        //frontend logic to get the backend response and download file. 
        
        // function from Ron T's answer which gets a string from ArrayBuffer. 
        const s2ab = (s) => {
          var buf = new ArrayBuffer(s.length);
          var view = new Uint8Array(buf);
          for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
          return buf;
        };
        
        const downloadExcelInBrowser = ()=>{
          const excelFileData = await backendCall();
          const decodedFileData = atob(excelFileData.data);
          const arrayBufferContent = s2ab(decodedFileData); // from example above
          const blob = new Blob([arrayBufferContent], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;' });
          var URL = window.URL || window.webkitURL;
          var downloadUrl = URL.createObjectURL(fileBlob);
          if (filename) {
            // use HTML5 a[download] attribute to specify filename
            var a = document.createElement('a');
            // safari doesn't support this yet
            if (typeof a.download === 'undefined') {
              window.location.href = downloadUrl;
            } else {
              a.href = downloadUrl;
              a.download = 'test.xlsx';
              document.body.appendChild(a);
              a.click();
            }
          } else {
            window.location.href = downloadUrl;
          }
        }
        

        【讨论】:

          猜你喜欢
          • 2021-09-20
          • 1970-01-01
          • 2016-08-14
          • 1970-01-01
          • 2022-07-21
          • 2018-06-11
          • 2011-02-14
          • 2018-04-20
          • 1970-01-01
          相关资源
          最近更新 更多