Я пытаюсь экспортировать несколько таблиц из базы данных и создаю файл excel для каждой таблицы с помощью PHP. Однако петля «прослушивается». Вместо того, чтобы создавать новый файл для каждого цикла, он просто помещает все данные в один и тот же файл.
Код:
foreach ($pdo->query("SHOW TABLES;") as $allTables) {
$table = $allTables[0];
$allDataStmt = $pdo->prepare("SELECT * FROM $table;");
$allDataStmt->execute();
$fieldcount = $allDataStmt->columnCount();
$col_title = "";
$data = "";
for ($i = 0; $i < $fieldcount; $i++) {
$col = $allDataStmt->getColumnMeta($i);
$column = $col['name'];
$col_title .= '<Cell ss:StyleID = "2"><Data ss:Type = "String">' . $column . '</Data></Cell>';
}
$col_title = '<Row>' . $col_title . '</Row>';
while ($row = $allDataStmt->fetch(PDO::FETCH_NUM)) {
$line = '';
foreach ($row as $value) {
if ((!isset($value)) or ($value == "")) {
$value = '<Cell ss:StyleID = "1"><Data ss:Type = "String"></Data></Cell>\t';
} else {
$value = str_replace('"', '', $value);
$value = '<Cell ss:StyleID = "1"><Data ss:Type = "String">' . $value . '</Data></Cell>\t';
}
$line .= $value;
}
$data .= trim("<Row>" . $line . "</Row>") . "\n";
}
$data = str_replace("\r", "", $data);
header("Content-Type: application/vnd.ms-excel;");
header("Content-Disposition: attachment; filename = " . $table . ".xls");
header("Pragma: no-cache");
header("Expires: 0");
}
}
Если я остановлю цикл после первой итерации, он правильно экспортирует данные из ОДНОЙ таблицы. Если я позволю циклу запуститься, он просто загрузит тот же файл с данными, и файл будет поврежден. Я даже не могу открыть его в Excel.
Что мне не хватает? Застрял с этим в течение нескольких часов.
Благодарю вас!
@MaxVoisard Я тоже об этом думал, но когда я распечатываю таблицы и только таблицы, они распечатываются правильно. Они появляются не более одного раза. Надеюсь, я правильно понял, что вы имеете в виду.
После некоторых исследований я обнаружил, что то, что вы пытаетесь сделать, невозможно для одного HTTP-запроса, и лучшим решением является добавление в файл .zip. Проверьте мой ответ.
То, что вы пытаетесь сделать с несколькими разными вложениями в одном и том же HTTP-запросе, невозможно. Протокол HTTP не поддерживает загрузку нескольких файлов. Самый распространенный обходной путь — поместить файлы в zip-архив для загрузки клиентом, например:
$zip = new ZipArchive;
$zip_name = "excel_tables.zip";
if ($zip->open($zip_name, ZipArchive::CREATE|ZipArchive::OVERWRITE) === TRUE) {
foreach ($pdo->query("SHOW TABLES;") as $allTables) {
// (Your logic to build Excel file)
$file_content = $col_title . "\n" . $data;
$file_name = $table . ".xls";
file_put_contents($file_name, $file_content);
// Add file to the zip file
$zip->addFile($file_name);
}
$zip->close();
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename=' . $zip_name);
header("Content-Length: " . filesize($zip_name));
header("Pragma: no-cache");
header("Expires: 0");
ob_clean();
flush();
readfile(__DIR__ . "/" . $zip_name);
}
вау еще лучше. Благодарю вас! На самом деле это было то, что я планировал сделать после исправления этой проблемы, ха-ха. Я попробовал это сейчас, но продолжаю получать эту ошибку: Предупреждение: ZipArchive::addFile(): Нет такого файла или каталога в xxxxx
Смотрите мое редактирование - похоже, что корневой путь сервера должен быть включен для расположения zip-файла. Я также отредактировал метод open()
, чтобы он был заключен в условие, которое перезаписывает тот же самый zip-файл, а не создает его. Не уверен, но надеюсь, что это сработает.
к сожалению, я продолжаю получать ту же ошибку. Если я использую это условие, которое вы добавили, вообще ничего не происходит. Если я не использую его, я получаю ту же ошибку, что и раньше.
Вы изменили переменную $zip_name
, чтобы включить корень сервера?
да. $zip_name = $_SERVER['DOCUMENT_ROOT'] . "/excel_tables.zip";
Я понимаю, что проблема была не в пути сервера к zip-файлу, а в попытке добавить несуществующий файл. Вы когда-нибудь добавляли созданное вами содержимое в файл Excel, например, через переменные $col_title
и $data
, а затем отправляли его на сервер? Я не вижу этого в вашем коде. Я отредактировал свой ответ на случай, если это то, чего не хватает.
Макс, ты находка, теперь все работает. файлы записываются в zip-архив. Большое спасибо! Можно ли как-то загрузить zip-файл от имени пользователя? Я имею в виду, если кто-то нажмет «Загрузить», он будет сохранен на его / ее компьютере?
Пожалуйста, и приятно слышать, что это работает - что касается вашего вопроса, вы имеете в виду, что zip-файл просто загружается одним нажатием кнопки? Если это так, то да, вам просто нужно вызвать этот код при отправке формы.
Да, точно. Ну, это не работает. Файлы скачиваются в папку, указанную в коде. Но он не загружается в папку «Загрузки» на моем ПК.
Может быть несколько отсутствующих заголовков, необходимых для загрузки zip-архива на компьютер клиента. Проверьте обновленный ответ.
до сих пор не работает. Это странно, потому что я использовал точно такие же заголовки ранее, и он начал загрузку, когда я делал отдельные файлы. Странно, как он не загружается сейчас с теми же заголовками, ха-ха.
Судя по документам, которые я только что прочитал, везде, где zip-файл загружается на сервер, вы должны указать этот путь перед методом readfile(). Смотрите мое редактирование — если «ДИР» не работает, просто используйте текущий рабочий каталог, в котором находится код.
все еще не работает, к сожалению. Я посмотрю подробнее завтра. На данный момент здесь 5 утра, и я делаю это уже более 10 часов. Я очень ценю вашу помощь и ваше время! Ты меня проделал долгий путь. Большое спасибо еще раз!
Я решил это. Я что-то распечатывал в цикле foreach, из-за чего он не загружался. Теперь все работает нормально!
Хорошо, приятно слышать, что он загружается.
Случайно ли ваша переменная
$table
продолжает обращаться к первому индексу массива в$allTables[0]
? Возможно, вам нужна переменная счетчика/индекса, чтобы она не ссылалась на один и тот же экземпляр?