Генерация переносимых файлов на стороне клиента HTML - никаких внешних ресурсов или вызовов сервера

У меня следующая ситуация:

Я установил серию заданий 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-файлах является весьма желательной функцией по двум предыдущим причинам.

"Переносимое" ограничение генерации файла CSV из файла HTML означает:

  1. Я не могу позвонить на сервер. Это означает, что ВСЕ генерация файлов должна выполняться на стороне клиента.

  2. Я хочу, чтобы один файл HTML, прикрепленный к электронному письму, содержал все ресурсы для создания файла CSV. Это означает, что я не могу использовать библиотеки jQuery или flash для генерации файла.

По понятным причинам безопасности я понимаю, что запись файлов на диск с использованием JavaScript не поддерживается ни одним браузером. Я не хочу создавать файл без ведома пользователя; Я хотел бы создать файл с использованием JavaScript в памяти, а затем предложить пользователю "загрузить" файл из памяти.

Я рассматривал создание файла CSV как URI, однако, согласно моим исследованиям и тестированию, у этого подхода есть несколько проблем:

  • URI для файлов не поддерживаются IE ( см. Здесь)

  • URI в FireFox сохраняет файл со случайным именем и в виде файла.part

Как бы мне ни было больно, я могу принять тот факт, что IE<=v9 не будет создавать URI для файлов. Я хотел бы создать полу-кросс-браузерное решение, в котором Chrome, Firefox и Safari создают URI для загрузки файла CSV после того, как JavaScript DOM скомпилирует данные.

Моя таблица примеров:

<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 и Chrome
  • И ( 1. ИЛИ 2.):
      • пользователю предлагается сохранить файл
      • пользователю не нужно "правый клик Сохранить как"
      • автоматически сохраняет файл в директории по умолчанию для браузера
      • имя файла по умолчанию - это имя таблицы с расширением.csv

Я добавил <script> тег с функцией обхода DOM doSomething(), Реальная помощь мне нужна с форматированием URI к тому, что я хочу в doSomething() функция. Подход № 2 (см. Мой код) кажется наиболее перспективным.

Я согласен с тем, что желаемой функциональности невозможно достичь, используя только HTML и JavaScript, если утверждение представлено с доказательством; такие как документация браузера, стандарты DOM, документация JavaScript/ECMAscript или логический счетчик, демонстрирующий невозможность.

При этом, я думаю, что решение возможно от кого-то с более глубоким фоном / большим опытом работы с URI, даже если решение немного хакерское.

3 ответа

Решение

Пока вы не возражаете против поддержки только IE10+, когда дело доходит до IE, используйте FileSaver.js. Вы можете сгенерировать CSV и добавить его в Blob, а затем saveAs() капля Вы сможете сохранить имя файла сохраненного файла только в тех браузерах, которые поддерживают <a download>, как Chrome.

В комментариях вы заявляете, что "вам НЕ НУЖНО создавать отдельные файлы HTML и CSV", но в этом случае ваша фундаментальная потребность заключается в предоставлении данных в двух форматах - создание двух копий данных не является необоснованным ограничением. Повторять, то, что вы просите, неосуществимо.

Наиболее близким решением, которое я могу представить для вашей проблемы, было бы упаковать данные CSV в HTML-файл (с помощью отображения: нет div или аналогичного) и динамически заполнить его таблицами HTML с использованием JavaScript во время загрузки. Это будет медленно, если у вас большие наборы записей, но было бы просто добавить кнопку для отображения и выбрать текст CSV для копирования-вставки. Безвкусный? Да жаль. Есть причина, почему HTML не является форматом обмена.

При этом вы можете упаковать свои табличные данные в PDF-файл и включить в него файлы CSV в виде вложений.

Я хочу, чтобы один файл HTML, прикрепленный к электронному письму, содержал все ресурсы для создания файла CSV. Это означает, что я не могу использовать jQuery

Это ложное утверждение. Если вы хотите использовать функциональность jQuery в одном файле HTML без вызова сервера, просто скопируйте / вставьте уменьшенную библиотеку jQuery в сам HTML. Это увеличит размер каждого HTML-файла на 32 КБ, что не так уж и страшно, если только вы не отправляете сотни писем за раз.

Поэтому, если вы знакомы с 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);
}
Другие вопросы по тегам