Crear un archivo CSV para un usuario en PHP

220

Tengo datos en una base de datos MySQL. Estoy enviando al usuario una URL para que saque sus datos como un archivo CSV.

Tengo cubierto el correo electrónico del enlace, la consulta MySQL, etc.

¿Cómo puedo, cuando hacen clic en el enlace, tener una ventana emergente para descargar un CVS con el registro de MySQL?

Ya tengo toda la información para obtener el registro. Simplemente no veo cómo hacer que PHP cree el archivo CSV y les permita descargar un archivo con una extensión .csv.

d -_- b
fuente

Respuestas:

280

Tratar:

header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=file.csv");
header("Pragma: no-cache");
header("Expires: 0");

echo "record1,record2,record3\n";
die;

etc.

Editar: Aquí hay un fragmento de código que uso para codificar opcionalmente campos CSV:

function maybeEncodeCSVField($string) {
    if(strpos($string, ',') !== false || strpos($string, '"') !== false || strpos($string, "\n") !== false) {
        $string = '"' . str_replace('"', '""', $string) . '"';
    }
    return $string;
}
Oleg Barshay
fuente
11
Tenga en cuenta que las reglas para los CSV son importantes. Para garantizar una buena visualización, coloque comillas dobles alrededor de sus campos y no olvide reemplazar las comillas dobles dentro de los campos por comillas dobles: `echo '"' .str_replace ('"', '" "', $ record1). '","'. str_replace ....
Mala
46
Solo para aclarar, el encabezado HTTP Content-Type correcto para CSV es text / csv, no application / csv. Dudo que a cualquier navegador moderno le importe de cualquier manera, pero dado que hay estándares, también podríamos usarlos.
fentie 15/10/10
10
En general, siga RFC 4180 cuando genere archivos CSV. tools.ietf.org/html/rfc4180
Oleg Barshay
55
@Oleg Barshay: OMG, ese RFC es una pieza maestra: "Si se usan comillas dobles para encerrar campos, entonces se debe escapar una comilla doble que aparece dentro de un campo precediéndola con otra comilla doble". ¡OTRA COTIZACIÓN DOBLE!
aefxx
465
header("Content-Type: text/csv");
header("Content-Disposition: attachment; filename=file.csv");

function outputCSV($data) {
  $output = fopen("php://output", "wb");
  foreach ($data as $row)
    fputcsv($output, $row); // here you can change delimiter/enclosure
  fclose($output);
}

outputCSV(array(
  array("name 1", "age 1", "city 1"),
  array("name 2", "age 2", "city 2"),
  array("name 3", "age 3", "city 3")
));

php: // salida
fputcsv

Andrii Nemchenko
fuente
64
Más personas necesitan ver esto y votarlo. Esta es fácilmente la mejor respuesta en esta página. PHP ya tiene funciones integradas para formatear contenido CSV. Todos deberían usarlos. Gran respuesta, @Andrey :)
maček
2
Lástima que fputcsv () no funcione correctamente :( bugs.php.net/bug.php?id=50686
gou1
44
Agradable, pero la outputCSVfunción es innecesariamente complicada. Puede simplemente foreachterminar $array, enviando sus valores directamente fputcsv. No hay necesidad de eso __outputCSVy el array_walk:)foreach($data as $vals) {fputcsv($filehandler,$vals);}
Sygmoral
Tengo algunos problemas con los caracteres franceses en la matriz porque eventualmente necesito que el .csv esté abierto en Excel. Se ahoga con los caracteres acentuados. Cosas como ¿ "Prévalence","age 1","city 1" Alguna idea? Jugar con UTF-8 no ha ayudado hasta ahora.
Voodoo
1
@theosem Delimiter depende de la configuración de exportación del SO o Excel. He agregado una nota sobre delimitador.
Andrii Nemchenko
19

Aquí hay una versión mejorada de la función de php.net que @Andrew publicó.

function download_csv_results($results, $name = NULL)
{
    if( ! $name)
    {
        $name = md5(uniqid() . microtime(TRUE) . mt_rand()). '.csv';
    }

    header('Content-Type: text/csv');
    header('Content-Disposition: attachment; filename='. $name);
    header('Pragma: no-cache');
    header("Expires: 0");

    $outstream = fopen("php://output", "wb");

    foreach($results as $result)
    {
        fputcsv($outstream, $result);
    }

    fclose($outstream);
}

Es realmente fácil de usar y funciona muy bien con los conjuntos de resultados MySQL (i) / PDO.

download_csv_results($results, 'your_name_here.csv');

Recuerde exit()después de llamar a esto si ha terminado con la página.

Xeoncross
fuente
¿Cómo se cambia el separador de, a; ?
Gruñón
1
¿Cómo deberíamos agregar un botón para activar este evento, de modo que el usuario pueda descargar el archivo CSV a pedido?
CanCeylan
Tendré el error "SyntaxError: token inesperado ILEGAL" con la línea "fopen (" php: // output "," w ");" Cuando lo cambio a "$ fp = fopen ('stats.csv', 'w');" No muestra error. Pero, por supuesto, no funciona. ¿Cómo debería resolverlo?
CanCeylan
@CanCeylan, esa es una pregunta en sí misma y necesito más detalles para resolverla.
Xeoncross
1
Agregue fputcsv($outstream, array_keys($results[0]));justo antes de foreachque también incluya encabezados de columna.
Justin
16

Además de todo lo que ya se dijo, es posible que deba agregar:

header("Content-Transfer-Encoding: UTF-8");

Es muy útil cuando se manejan archivos con varios idiomas, como los nombres de las personas o las ciudades.

Stan
fuente
9

El hilo es un poco viejo, lo sé, pero para referencia futura y para novatos como yo:

Todos los demás aquí explican cómo crear el CSV, pero pierden una parte básica de la pregunta: cómo vincular. Para vincular la descarga del archivo CSV, solo tiene que vincular al archivo .php, que a su vez responde como un archivo .csv. Los encabezados PHP hacen eso. Esto permite cosas interesantes, como agregar variables a la cadena de consulta y personalizar la salida:

<a href="my_csv_creator.php?user=23&amp;othervariable=true">Get CSV</a>

my_csv_creator.php puede trabajar con las variables dadas en la cadena de consulta y, por ejemplo, utilizar consultas de bases de datos diferentes o personalizadas, cambiar las columnas del CSV, personalizar el nombre del archivo, etc., por ejemplo:

User_John_Doe_10_Dec_11.csv
LBJ
fuente
1
Uh, cualquiera que lea esto tenga mucho cuidado con este enfoque. Apostaría a que las entradas no se escapan correctamente y esto puede conducir a grandes agujeros de seguridad en su aplicación.
calumbrodie
Su método es muy seguro y funciona muy bien. Puede controlar fácilmente el acceso con la sesión, y también puede almacenar toda la información que desea poner en un csv en una sesión var. Luego, simplemente desármalo como parte del proceso convirtiéndolo en un csv. Solo sugeriría usar vars de sesión en lugar de una cadena de consulta.
inorganik
1
No hay agujeros de seguridad per se con este método. Mientras el archivo php al que se hace referencia esté haciendo su trabajo de saneamiento y verificación de acceso. Pero realmente no tiene sentido mostrar todo eso aquí. El punto de la publicación es que puede vincular a cualquier URL que genere el csv.
danielson317
8

Cree su archivo y luego devuelva una referencia con el encabezado correcto para activar Guardar como: edite lo siguiente según sea necesario. Ponga sus datos CSV en $ csvdata.

$fname = 'myCSV.csv';
$fp = fopen($fname,'wb');
fwrite($fp,$csvdata);
fclose($fp);

header('Content-type: application/csv');
header("Content-Disposition: inline; filename=".$fname);
readfile($fname);
typemismatch
fuente
1
El uso de la disposición de contenido "en línea" significa que el navegador debe intentar mostrarlo sin descargarlo (IE tiende a incrustar una instancia de Excel para eso). "adjunto" es lo que se necesita para forzar una descarga.
Gert van den Berg
5

Aquí hay un ejemplo de trabajo completo que usa PDO e incluye encabezados de columna:

$query = $pdo->prepare('SELECT * FROM test WHERE id=?');
$query->execute(array($id));    
$results = $query->fetchAll(PDO::FETCH_ASSOC);
download_csv_results($results, 'test.csv'); 
exit();


function download_csv_results($results, $name)
{            
    header('Content-Type: text/csv');
    header('Content-Disposition: attachment; filename='. $name);
    header('Pragma: no-cache');
    header("Expires: 0");

    $outstream = fopen("php://output", "wb");    
    fputcsv($outstream, array_keys($results[0]));

    foreach($results as $result)
    {
        fputcsv($outstream, $result);
    }

    fclose($outstream);
}
Justin
fuente
4

Primero haga los datos como una Cadena con coma como delimitador (separados con ","). Algo como esto

$CSV_string="No,Date,Email,Sender Name,Sender Email \n"; //making string, So "\n" is used for newLine

$rand = rand(1,50); //Make a random int number between 1 to 50.
$file ="export/export".$rand.".csv"; //For avoiding cache in the client and on the server 
                                     //side it is recommended that the file name be different.

file_put_contents($file,$CSV_string);

/* Or try this code if $CSV_string is an array
    fh =fopen($file, 'w');
    fputcsv($fh , $CSV_string , ","  , "\n" ); // "," is delimiter // "\n" is new line.
    fclose($fh);
*/
Behzad Ravanbakhsh
fuente
3

Hola, funciona muy bien ... !!!! Gracias Peter Mortensen y Connor Burton

<?php
header("Content-type: application/csv");
header("Content-Disposition: attachment; filename=file.csv");
header("Pragma: no-cache");
header("Expires: 0");

ini_set('display_errors',1);
$private=1;
error_reporting(E_ALL ^ E_NOTICE);

mysql_connect("localhost", "user", "pass") or die(mysql_error());
mysql_select_db("db") or die(mysql_error());

$start = $_GET["start"];
$end = $_GET["end"];

$query = "SELECT * FROM customers WHERE created>='{$start} 00:00:00'  AND created<='{$end} 23:59:59'   ORDER BY id";
$select_c = mysql_query($query) or die(mysql_error());

while ($row = mysql_fetch_array($select_c, MYSQL_ASSOC))
{
    $result.="{$row['email']},";
    $result.="\n";
    echo $result;
}

?>

Kaddy
fuente
No NO utilizar este código en la producción, es vulnerable a inyecciones SQL
ntzm
2

Método simple

$data = array (
    'aaa,bbb,ccc,dddd',
    '123,456,789',
    '"aaa","bbb"');

$fp = fopen('data.csv', 'wb');
foreach($data as $line){
    $val = explode(",",$line);
    fputcsv($fp, $val);
}
fclose($fp);

Por lo tanto, cada línea de la $datamatriz irá a una nueva línea de su archivo CSV recién creado. Solo funciona solo para PHP 5 y versiones posteriores.

usuario244641
fuente
2

Simplemente puede escribir sus datos en CSV utilizando la función fputcsv . Echemos un vistazo al siguiente ejemplo. Escribir la matriz de la lista en el archivo CSV

$list[] = array("Cars", "Planes", "Ships");
$list[] = array("Car's2", "Planes2", "Ships2");
//define headers for CSV 
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=file_name.csv');
//write data into CSV
$fp = fopen('php://output', 'wb');
//convert data to UTF-8 
fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
foreach ($list as $line) {
    fputcsv($fp, $line);
}
fclose($fp);
Shahbaz
fuente
1

La forma más fácil es usar una clase CSV dedicada como esta:

$csv = new csv();
$csv->load_data(array(
    array('name'=>'John', 'age'=>35),
    array('name'=>'Adrian', 'age'=>23), 
    array('name'=>'William', 'age'=>57) 
));
$csv->send_file('age.csv'); 
Sergiu
fuente
1

En vez de:

$query = "SELECT * FROM customers WHERE created>='{$start} 00:00:00'  AND created<='{$end} 23:59:59'   ORDER BY id";
$select_c = mysql_query($query) or die(mysql_error()); 

while ($row = mysql_fetch_array($select_c, MYSQL_ASSOC))
{
    $result.="{$row['email']},";
    $result.="\n";
    echo $result;
}

Utilizar:

$query = "SELECT * FROM customers WHERE created>='{$start} 00:00:00'  AND created<='{$end} 23:59:59'   ORDER BY id";
$select_c = mysql_query($query) or die(mysql_error()); 

while ($row = mysql_fetch_array($select_c, MYSQL_ASSOC))
{
    echo implode(",", $row)."\n";
}
Joshua
fuente
1

Ya llegó una muy buena solución. Solo estoy poniendo el código total para que un novato obtenga ayuda total

<?php
extract($_GET); //you can send some parameter by query variable. I have sent table name in *table* variable

header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=$table.csv");
header("Pragma: no-cache");
header("Expires: 0");

require_once("includes/functions.php"); //necessary mysql connection functions here

//first of all I'll get the column name to put title of csv file.
$query = "SHOW columns FROM $table";
$headers = mysql_query($query) or die(mysql_error());
$csv_head = array();
while ($row = mysql_fetch_array($headers, MYSQL_ASSOC))
{
    $csv_head[] =  $row['Field'];
}
echo implode(",", $csv_head)."\n";

//now I'll bring the data.
$query = "SELECT * FROM $table";
$select_c = mysql_query($query) or die(mysql_error()); 

while ($row = mysql_fetch_array($select_c, MYSQL_ASSOC))
{
    foreach ($row as $key => $value) {
            //there may be separator (here I have used comma) inside data. So need to put double quote around such data.
        if(strpos($value, ',') !== false || strpos($value, '"') !== false || strpos($value, "\n") !== false) {
            $row[$key] = '"' . str_replace('"', '""', $value) . '"';
        }
    }
    echo implode(",", $row)."\n";
}

?>

He guardado este código en csv-download.php

Ahora vea cómo he usado estos datos para descargar el archivo csv

<a href="csv-download.php?table=tbl_vfm"><img title="Download as Excel" src="images/Excel-logo.gif" alt="Download as Excel" /><a/>

Entonces, cuando hago clic en el enlace, descargo el archivo sin llevarme a la página csv-download.php del navegador.

zahid9i
fuente
0

Para que lo envíe como un CSV y que le dé el nombre del archivo, use header ():

http://us2.php.net/header

header('Content-type: text/csv');
header('Content-disposition: attachment; filename="myfile.csv"');

En cuanto a hacer el CSV en sí, simplemente recorrerá el conjunto de resultados, formateará la salida y la enviará, como lo haría con cualquier otro contenido.

Gavin M. Roy
fuente
0

Escribir su propio código CSV es probablemente una pérdida de tiempo, solo use un paquete como league / csv: trata todas las cosas difíciles para usted, la documentación es buena y es muy estable / confiable:

http://csv.thephpleague.com/

Tendrás que estar usando compositor. Si no sabe qué compositor es, le recomiendo que eche un vistazo: https://getcomposer.org/

John Hunt
fuente
0
<?
    // Connect to database
    $result = mysql_query("select id
    from tablename
    where shid=3");
    list($DBshid) = mysql_fetch_row($result);

    /***********************************
    Write date to CSV file
    ***********************************/

    $_file = 'show.csv';
    $_fp = @fopen( $_file, 'wb' );

    $result = mysql_query("select name,compname,job_title,email_add,phone,url from UserTables where id=3");

    while (list( $Username, $Useremail_add, $Userphone, $Userurl) = mysql_fetch_row($result))
    {
        $_csv_data = $Username.','.$Useremail_add.','.$Userphone.','.$Userurl . "\n";
        @fwrite( $_fp, $_csv_data);
    }
    @fclose( $_fp );
?>
Vish00722
fuente
0

¿Cómo escribir en un archivo CSV usando un script PHP? En realidad también estaba buscando eso también. Es una tarea fácil con PHP. fputs (controlador, contenido): esta función funciona de manera eficiente para mí. Primero debe abrir el archivo en el que necesita escribir contenido usando fopen ($ CSVFileName, 'wb').

$CSVFileName = test.csv”;
$fp = fopen($CSVFileName, wb’);

//Multiple iterations to append the data using function fputs()
foreach ($csv_post as $temp)
{
    $line = “”;
    $line .= Content 1 . $comma . $temp . $comma . Content 2 . $comma . 16/10/2012″.$comma;
    $line .= \n”;
    fputs($fp, $line);
}
ntzm
fuente
Siempre se recomienda agregar el código a sí mismo en lugar de vincular, ya que los enlaces tienden a cambiar o desaparecer con el tiempo.
Sgoettschkes
Su respuesta es a un hilo muy viejo. Mientras que en 2008, era una práctica estándar construir sus propias líneas separadas por comas, el método correcto desde PHP 5.1.0 sería utilizar la función fputcsv incorporada ( php.net/fputcsv ). Tenga cuidado al leer hilos antiguos para asegurarse de que la información contenida en ellos sigue siendo relevante.
Luke Mills
-1

Ponga en la $outputvariable los datos CSV y haga eco con los encabezados correctos

header("Content-type: application/download\r\n");
header("Content-disposition: filename=filename.csv\r\n\r\n");
header("Content-Transfer-Encoding: ASCII\r\n");
header("Content-length: ".strlen($output)."\r\n");
echo $output;
Lorenzo Massacci
fuente