¿Es correcto crear un archivo de esta manera?

  • Autor Autor xcodex
  • Fecha de inicio Fecha de inicio
X

xcodex

Hola!, hace unos días publiqué que me encontraba creando un archivo CVS, la diea siempre fué que descargue uno, y que se copie en el servidor, pero esto último no lo puedo lograr:
Este es el código:

PHP:
<?php
require "../include/bd-connect.php";
$pdo = bd_connect();

//Header
header("Content-Type:text/csv; charset=utf-8");
header("Content-Disposition: attachment; filename=report.csv");

//Salida de archivo
$exit = fopen("php://output", "w");
$exit2 = fopen("report.csv", "w");

//Encabezados:
fputcsv($exit, array('id', 'Title'));
fputcsv($exit2, array('id', 'Title'));

//Consulta para crear reporte:
$sql_libros = $pdo->prepare("SELECT id,title FROM books WHERE active = 1 ORDER BY id ASC");
$sql_libros->execute();
$resultado_libros = $sql_libros->fetchAll();

foreach ($resultado_libros as $row) {
    fputcsv($exit, array($row["id"], $row["title"]));
    fputcsv($exit2, array($row["id"], $row["title"]));
}
?>

Los errores que me aparecen son, uno de permisos al hacer:
PHP:
$exit2 = fopen("report.csv", "w");
Y luego cuando hace el encabezado:
PHP:
fputcsv($exit2, array('id', 'Title'));
y en el foreach:
PHP:
fputcsv($exit2, array($row["id"], $row["title"]));
me dice que espera un valor booleando.
Y bueno, no se que hacer. ¿Alguna sugerencia?.
 
PHP:
<?php

//Encabezados:
fputcsv($exit, array('id', 'Title'));
fputcsv($exit2, array('id', 'Title'));

?>

Yo he probado un código similar en mi localhost y funciona bien, prueba quitando los encabezados, debería bastar con el foreach,
mira la documentación https://www.php.net/manual/es/function.fputcsv.php
Me parece rarísimo, además el problema es cuando hago ambo archivos en simultáneo, cuando hago cada uno por separado todo funciona correcto.
 
Me parece rarísimo, además el problema es cuando hago ambo archivos en simultáneo, cuando hago cada uno por separado todo funciona correcto.

Mira, entonces solo llama a este:
PHP:
$exit2 = fopen("report.csv", "w");

el archivo se creará en donde le indiques, y ya después lo llamas, así evitas ejecutar los dos al mismo tiempo.
 
es incorrecto

function csvstr(array $fields) : string
{
$f = fopen('php://memory', 'r+');
if (fputcsv($f, $fields) === false) {
return false;
}
rewind($f);
$csv_line = stream_get_contents($f);
return rtrim($csv_line);
}

puede usar esto como referencia.
 
No lo cerras al archivo con fclose?, ademas si estas usando un solo nombre vas a tener problema de concurrencia si dos ejecutan a la vez el php, tendrias concatenarle el timestamp por ahi al nombre.
 
Me voy a tomar el lujo de darte un par de recomendaciones.
  1. NO incluyas un Header antes de tener el archivo listo para salir. Si se genera algún error, dicho error sí o sí se va a descargar como CSV, y eso, está mal.
  2. No considero que sea necesario especificar el nombre de las columnas en "//Encabezados:". Técnicamente tienes un `array` asociativo, así que podrías sacar las `keys` para obtener las columnas que tendrá el CSV. Imagina si el día de mañana necesitas añadir nueva columna ¿Cuántos pasos necesitarás para poder añadirla?
  3. No empezaría a abrir el archivo hasta que tenga la respuesta del SQL ¿Qué pasaría si el SQL falla o hay un bug? La conexión con los archivos se quedaría abierta.
  4. Como te han recomendado, yo crearía el archivo en el servidor con un hash que lo identifique inequívocamente, por ejemplo la fecha y la hora. Después ese archivo se lo enviaría al cliente.
 
Me voy a tomar el lujo de darte un par de recomendaciones.
  1. NO incluyas un Header antes de tener el archivo listo para salir. Si se genera algún error, dicho error sí o sí se va a descargar como CSV, y eso, está mal.
  2. No considero que sea necesario especificar el nombre de las columnas en "//Encabezados:". Técnicamente tienes un `array` asociativo, así que podrías sacar las `keys` para obtener las columnas que tendrá el CSV. Imagina si el día de mañana necesitas añadir nueva columna ¿Cuántos pasos necesitarás para poder añadirla?
  3. No empezaría a abrir el archivo hasta que tenga la respuesta del SQL ¿Qué pasaría si el SQL falla o hay un bug? La conexión con los archivos se quedaría abierta.
  4. Como te han recomendado, yo crearía el archivo en el servidor con un hash que lo identifique inequívocamente, por ejemplo la fecha y la hora. Después ese archivo se lo enviaría al cliente.
Gracias por tu respuesta.
¿Podría consultarte como quedaría mas o menos en mi código?
 
Mira yo sugiero primero crear el fichero, y después compruebas que existe en tu servidor, y recién ahí forzar la descarga.

PHP:
<?php

require "../include/bd-connect.php";

$pdo = bd_connect();

//Salida de archivo

$exit2 = fopen("report.csv", "w");

//Consulta para crear reporte:

$sql_libros = $pdo->prepare("SELECT id,title FROM books WHERE active = 1 ORDER BY id ASC");

$sql_libros->execute();

$resultado_libros = $sql_libros->fetchAll();

foreach ($resultado_libros as $row) {

    fputcsv($exit2, array($row["id"], $row["title"]));

}

fclose($exit2);


// forzar la descarga del archivo.
$nombre_fichero = 'report.csv';

if (file_exists($nombre_fichero)) {

    header("Content-Type:text/csv; charset=utf-8");
    header("Content-Disposition: attachment; filename=".$nombre_fichero);
   
    readfile($nombre_fichero);
    exit;
} else {

    echo "El fichero $nombre_fichero no existe";

}

?>
 
Mira yo sugiero primero crear el fichero, y después compruebas que existe en tu servidor, y recién ahí forzar la descarga.

PHP:
<?php

require "../include/bd-connect.php";

$pdo = bd_connect();

//Salida de archivo

$exit2 = fopen("report.csv", "w");

//Consulta para crear reporte:

$sql_libros = $pdo->prepare("SELECT id,title FROM books WHERE active = 1 ORDER BY id ASC");

$sql_libros->execute();

$resultado_libros = $sql_libros->fetchAll();

foreach ($resultado_libros as $row) {

    fputcsv($exit2, array($row["id"], $row["title"]));

}

fclose($exit2);


// forzar la descarga del archivo.
$nombre_fichero = 'report.csv';

if (file_exists($nombre_fichero)) {

    header("Content-Type:text/csv; charset=utf-8");
    header("Content-Disposition: attachment; filename=".$nombre_fichero);
  
    readfile($nombre_fichero);
    exit;
} else {

    echo "El fichero $nombre_fichero no existe";

}

?>
Wooow, muchas gracias 🙂 🙂
 
Atrás
Arriba