代码之家  ›  专栏  ›  技术社区  ›  Community wiki

HTML客户端可移植文件生成-无外部资源或服务器调用

  •  4
  • Community wiki  · 技术社区  · 2 年前

    我有以下情况:

    我在公司内部服务器上设置了一系列Cron作业,以运行各种旨在检查数据完整性的PHP脚本。每个PHP脚本查询一个公司数据库,将返回的查询数据格式化为包含一个或多个的HTML文件 <tables> ,然后将HTML文件作为附件发送给多个客户端电子邮件。根据我的经验,大多数PHP脚本生成的HTML文件只有几个表,但也有一些PHP脚本创建的HTML文件大约有30个表。之所以选择HTML文件作为这些扫描的分发格式,是因为HTML使在浏览器窗口中一次查看多个表变得容易。

    我想为客户端添加将HTML文件中的表作为CSV文件下载的功能。我预计客户端在根据表数据怀疑存在数据完整性问题时会使用此功能。对他们来说,最好能够获取有问题的表格,将数据导出到CSV文件中,然后进一步研究。

    因为将数据导出为CSV格式的需求由客户自行决定,不可预测的是什么表将受到审查,并且会间歇性地使用,所以我不想为每个表创建CSV文件。

    通常情况下,创建CSV文件不会太困难,使用JavaScript/jQuery预处理DOM遍历,并利用服务器调用或闪存库将CSV文件数据生成字符串,以方便下载过程;但我有一个限制:HTML文件需要是“可移植的”

    我希望客户能够获取他们的HTML文件,并在公司内部网之外对数据进行预处理分析。此外,这些HTML文件很可能会被存档,因此,由于前面两个原因,使导出功能“自我包含”在HTML文件中是一个非常理想的功能。

    从HTML文件生成CSV文件的“可移植”约束意味着:

    1. 我无法拨打服务器电话。这意味着 全部 文件生成必须在客户端完成。

    2. 我希望附加到电子邮件的单个HTML文件包含生成CSV文件的所有资源。这意味着我不能使用jQuery或flash库来生成文件。

    出于明显的安全原因,我知道任何浏览器都不支持使用JavaScript将文件写入磁盘。我不想在用户不知情的情况下创建文件;我想在内存中使用JavaScript生成文件,然后提示用户从内存中“下载”该文件。

    我曾考虑过将CSV文件生成为URI,但根据我的研究和测试,这种方法存在一些问题:

    • IE不支持文件的URI( See Here )

    • FireFox中的URI使用随机文件名将文件保存为.part文件

    尽管这让我很痛苦,但我可以接受这样一个事实:IE<=v9不会为文件创建URI。我想创建一个半跨浏览器解决方案,其中Chrome、Firefox和Safari创建一个URI,以便在JavaScript DOM遍历编译数据后下载CSV文件。

    我的示例表:

    <table>
        <thead class="resulttitle">
            <tr>
                <th style="text-align:center;" colspan="3">
                NameOfTheTable</th>
            </tr>
        </thead>
        <tbody>
            <tr class="resultheader">
                <td>VEN_PK</td>
                <td>VEN_CompanyName</td>
                <td>VEN_Order</td>
            </tr>
            <tr>
                <td class='resultfield'>1</td>
                <td class='resultfield'>Brander Ranch</td>
                <td class='resultfield'>Beef</td>
            </tr>
            <tr>
                <td class='resultfield'>2</td>
                <td class='resultfield'>Super Tree Produce</td>
                <td class='resultfield'>Apples</td>
            </tr>
            <tr>
                <td class='resultfield'>3</td>
                <td class='resultfield'>John's Distilery</td>
                <td class='resultfield'>Beer</td>
            </tr>
        </tbody>
        <tfoot>
            <tr>
              <td colspan="3" style="text-align:right;">
              <button onclick="doSomething(this);">Export to CSV File</button></td>
            </tr>
        </tfoot>
    </table>
    

    我的JavaScript示例:

    <script type="text/javascript">
      function doSomething(inButton) {
    
        /* locate elements */
        var table = inButton.parentNode.parentNode.parentNode.parentNode;
        var name  = table.rows[0].cells[0].textContent;
        var tbody = table.tBodies[0];
    
        /* create CSV String through DOM traversal */
        var rows  = tbody.rows;
        var csvStr = "";
        for (var i=0; i < rows.length; i++) {
          for (var j=0; j < rows[i].cells.length; j++) {
            csvStr += rows[i].cells[j].textContent +",";
          }
          csvStr += "\n";
        }
    
        /* temporary proof DOM traversal was successful */
        alert("Table Name:\t" + name + "\nCSV String:\n" + csvStr);
    
        /* Create URI Here!
         * (code I am missing)
         */
    
        /* Approach #1 : Auto-Download From Browser
         * Attempts to redirect the browser to the URI
         * and have the browser download the data as a file
         *
         * Approach does downloads CSV data but:
         * In FireFox downloads as randomCharacers.part instead of name.csv
         * In Chrome downloads without prompting the user and without correct file name
         * In Safari opens the files in browser (textfile)
         */
        /* Approach #1 Code:
           var hrefData = "data:text/csv;charset=US-ASCII," + encodeURIComponent(csvStr);
           document.location.href = hrefData;
         */
    
        /* Approach #2 : Right-Click Save As Link...
         * Attempts to remove "Download Button"
         * and replace the it with an anchor tag <a>
         * that the user can use to download the data
         *
         * Approach sort of works better:
         * When clicking on the link:
         * In FireFox downloads as randomCharacers.part instead of name.csv
         * In Chrome downloads without prompting the user (uses correct name) 
         * In Safari opens the files in browser (textfile)
         * 
         * When right-click "Save As" on the link:
         * In FireFox prompts the user with filename: randomCharacers.part 
         * In Chrome  prompts the user with filename: "download"
         * In Safari  prompts the user with filename: "Unknown"
         */
        /* Approach #2 Code:
           var hrefData = "data:text/csv;charset=US-ASCII," + encodeURIComponent(csvStr);
           var fileLink = document.createElement("a");
           fileLink.href = hrefData;
           fileLink.type  = "text/csv";
           fileLink.innerHTML = "CSV Download Link";
           fileLink.setAttribute("download",name+".csv"); // for Chrome
           parentTD = inButton.parentNode;
           parentTD.appendChild(fileLink);
           parentTD.removeChild(inButton);
         */
      }
    </script>
    

    我正在寻找一个示例解决方案,上面的示例表可以作为CSV文件下载:

    • 使用URI
    • 使用 <button> 或者 <a>
    • 代码的工作原理如现代版本的FireFox、Safari和;铬
    • 以及 (一)。 或者 2.):
        • 系统会提示用户保存文件
        • 用户不需要“右键单击另存为”
        • 自动将文件保存到默认浏览器保存目录
        • 默认文件名是扩展名为.csv的表的名称

    我添加了一个 <script> 带有DOM遍历函数的标记 doSomething() 。我真正需要的帮助是将URI格式化为 做某事() 作用方法#2(参见我的代码)似乎是最有前途的。

    我愿意接受我想要的功能是不可能只使用HTML&JavaScript,如果该语句带有证据;例如浏览器文档、DOM标准、JavaScript/ECMAscript文档,或者证明不可能的逻辑反例。

    话虽如此,我认为一个对URI有更深背景/更多经验的人可能会提出解决方案,即使这个解决方案有点像黑客。

    3 回复  |  直到 13 年前
        1
  •  3
  •   Eli Grey    13 年前

    只要你不介意在IE方面只支持IE10+,就可以使用 FileSaver.js 。您可以生成CSV并将其附加到Blob,然后 saveAs() 斑点。您只能在支持的浏览器中保留已保存文件的文件名 <a download> ,比如Chrome。

        2
  •  1
  •   Winfield Trail    13 年前

    你在评论中说你“不应该” 需要 创建单独的HTML和CSV文件,”但在这种情况下,您的基本需求是以两种格式提供数据-创建数据的两个副本并不是一个不合理的限制 不可行。

    我能想到的最接近您的问题的解决方案是将CSV数据打包在HTML文件中(通过显示:none-div或类似内容),并在加载时使用JavaScript将其动态填充到HTML表中。如果你有大的记录集,这会很慢,但添加一个按钮来显示和选择CSV文本进行复制粘贴会很简单。不合法?是的,对不起。HTML不是交换格式是有原因的。

    也就是说,您可以将表格数据打包为PDF,并将CSV作为附件包含在其中。

        3
  •  -1
  •   MidnightLightning    13 年前

    我希望附加到电子邮件的单个HTML文件包含所有 资源来生成CSV文件。这意味着我不能使用jQuery

    这是一个错误的陈述。如果您想在一个没有服务器调用的HTML文件中使用jQuery功能,只需将缩小后的jQuery库复制/粘贴到HTML本身即可。它将使每个HTML文件大32K,这并不可怕,除非你一次发送数百封电子邮件。

    因此,如果您熟悉jQuery,并且能够自如地使用它来重构页面上的数据,请随意使用它。我建议您自动创建的HTML将数据放在javascript数组中,并在页面加载时创建表。然后,如果用户单击CSV选项,它就可以清除并重构它。

    var data = {
      title: 'NameOfTable',
      headers: ['VEN_PK', 'VEN_CompanyName', 'VEN_Order'],
      data: [
        ['1', 'Brander Ranch' 'Beef'],
        ['2', 'Super Tree Produce' 'Apples'],
        ['3', 'John\'s Distilery' 'Beer']
      ] 
    }
    $(document).ready(function() {
      var html = '<table>';
      html += '<thead>';
      html += '<tr><th style="text-align:center;" colspan="' + data.headers.length +'">' + data.title + '</th></tr>';
      html += '</thead><tbody>';
      html += '<tr class="resultheader">';
      for (i in data.headers) {
        html += '<td>'+data.headers[i]+'</td>';
      }
      html += '</tr>';
      for (i in data.data) {
        var row = data.data[i];
        html += '<tr>';
        for (j in row) {
          html += '<td class="resultfield">'+row[j]+'</td>';
        }
        html += '</tr>';
      }
      html += '</table>';
      $('#container').html(html);
    });
    
    function showCSV() {
       var csv = '';
       for (i in data.headers) {
         csv += data.headers[i] + ',';
       }
       csv += "\n";
       for (i in data.data) {
         var row = data.data[i];
         for (j in row) {
           csv += row[j] + ',';
         }
         csv += "\n";
       }
       $('#container').html(csv);
    }
    
    推荐文章