4

我有以下情况:

我在公司内部服务器上设置了一系列 Cron 作业来运行旨在检查数据完整性的各种 PHP 脚本。每个 PHP 脚本都会查询公司数据库,将返回的查询数据格式化为包含一个或多个<tables>. 根据我的经验,大多数 PHP 脚本生成的 HTML 文件只有几个表格,但是有一些 PHP 脚本可以创建大约 30 个表格的 HTML 文件。之所以选择 HTML 文件作为这些扫描的分发格式,是因为 HTML 可以轻松地在浏览器窗口中一次查看多个表。

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

因为是否需要将数据导出为 CSV 格式由客户自行决定,无法预测哪些表将受到审查,并且间歇性使用我不想为每个表创建 CSV 文件。

通常创建 CSV 文件不会太难,使用 JavaScript/jQuery 执行 DOM 遍历并利用服务器调用或 flash 库将 CSV 文件数据生成字符串以方便下载过程;但我有一个限制条件:HTML 文件必须是“可移植的”。

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

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

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

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

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

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

  • IE 不支持文件的 URI(请参阅此处

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

尽管让我很痛苦,但我可以接受 IE<=v9 不会为文件创建 URI 的事实。我想创建一个半跨浏览器解决方案,其中 Chrome、Firefox 和 Safari 在 JavaScript DOM 遍历编译数据后创建一个 URI 来下载 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
  • 使用 a<button>或 a<a>
  • 代码按照现代版本的 FireFox、Safari 和 Chrome 中的描述工作
  • (1.2.):
      • 提示用户保存文件
      • 用户不需要“右键单击另存为”
      • 自动将文件保存到默认浏览器保存目录
      • 默认文件名是带有 .csv 文件扩展名的表的名称

我添加了一个<script>带有 DOM 遍历功能的标签doSomething()。我需要的真正帮助是将 URI 格式化为函数中我想要的内容doSomething()。方法#2(见我的代码)似乎最有希望。

我愿意接受我想要的功能是不可能实现仅使用 HTML 和 JavaScript 如果声明提供证明; 例如浏览器文档、DOM 标准、JavaScript/ECMAscript 文档或证明不可能的逻辑反例。

话虽如此,我认为解决方案可能来自具有更深背景/更多 URI 经验的人,即使解决方案有点 hack。

4

3 回答 3

3

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

于 2012-09-13T16:04:47.090 回答
1

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

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

话虽如此,您可以将表格数据打包在 PDF 中,并将 CSV 作为附件包含在其中。

于 2012-09-01T22:34:41.357 回答
-1

我希望附加到电子邮件的单个 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);
}
于 2012-09-14T16:38:40.797 回答