Генерация переносимых файлов на стороне клиента 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 означает:
Я не могу позвонить на сервер. Это означает, что ВСЕ генерация файлов должна выполняться на стороне клиента.
Я хочу, чтобы один файл 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);
}