16Tengo muchos ejemplos de elaboración de informes en formato PDF, Word, Excel, etc., que podré utilizar en este tipo de aplicaciones con el Front-End de React.
Como habitualmente hago en los últimos tiempos, consulté con IA Deep Seek sobre este tema y me ofreció, con las múltiples preguntas y reflexiones que le hice, 3 alternativas que paso a mostraros.
Objetivo
Producir descarga de ficheros elaborados en el server PHP y entregado por React.
En este caso, la funcionalidad es:
- En REACT se establece un botón, que al ser pulsado hace una petición al Server- PHP.
- En PHP, valida que la petición es correcta y elabora un fichero PDF con el software TCPDF. Una vez acabado, lo entrega, en formato binario al Front-End- React.
- En React, se obtiene el fichero, se crea dinámicamente un enlace de descarga y se hace «click» sobre dicho enlace.
DEMO: https://fhumanes.com/download_f/
Está instalada la versión 1
Solución Técnica
Como he indicado, el código, con muy pequeños retoques, se ha obtenido a través de la IA Deep Seek.
Es muy pequeño el código generado, pero es muy ilustrativo, tanto en PHP, como en React.
Aunque normalmente yo trabajo con SLIM 4.0, para los servicios en PHP, he dejado el código de la IA porque entiendo puede ser de ayuda a aquellos que no estáis habituados a trabajar con SLIM.
Muy importante, las cabeceras, para no tener problemas con seguridad de las peticiones en diferentes dominios. También, para la entrega de ficheros binarios.
En REACT, dispongo de estos códigos:
import React, { useState } from 'react'; import axios from 'axios'; // Configuration partition of URL of access to SERVER const url = new URL(window.location.href); // console.log("URL de ejecución:", url); if ( url.hostname == "localhost") { var config_url = 'http://localhost/download_B/'; } else { var config_url = 'https://fhumanes.com/download_B/'; } const DescargaPDF = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const generarYDescargarPDF = async () => { setLoading(true); setError(null); try { // Cambia esta URL por la de tu backend const response = await axios.post(config_url+'generar_pdf.php', {}, { responseType: 'blob', // Importante para manejar archivos binarios }); // Crear un enlace temporal para descargar el PDF const url = window.URL.createObjectURL(new Blob([response.data])); console.log("Contenido de URL: ",url); console.log("Contenido de RESPONSE: ",response); console.log("contenido de DATA: ", response.data); const link = document.createElement('a'); link.href = url; link.setAttribute('download', 'documento_generado.pdf'); console.log("Contenido de LINK: ",link); document.body.appendChild(link); link.click(); // Limpiar link.parentNode.removeChild(link); window.URL.revokeObjectURL(url); } catch (err) { setError('Error al generar el PDF. Por favor, inténtalo de nuevo.'); console.error('Error:', err); } finally { setLoading(false); } }; return ( <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}> <h2>Generador de PDF</h2> <p>Haz clic en el botón para generar y descargar un PDF desde el servidor.</p> <button onClick={generarYDescargarPDF} disabled={loading} style={{ padding: '10px 15px', backgroundColor: '#4CAF50', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '16px' }} > {loading ? 'Generando...' : 'Generar y Descargar PDF'} </button> {error && <p style={{ color: 'red', marginTop: '10px' }}>{error}</p>} </div> ); }; export default DescargaPDF;
import React, { useState } from 'react'; import axios from 'axios'; import DownloadLink from 'react-download-link'; const DescargaPDF = () => { const [pdfData, setPdfData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const generarPDF = async () => { setLoading(true); setError(null); try { const response = await axios.post('http://localhost/download_B/generar_pdf.php', {}, { responseType: 'blob', }); setPdfData(response.data); return response.data; } catch (err) { setError('Error al generar el PDF'); console.error('Error:', err); throw err; } finally { setLoading(false); } }; return ( <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}> <h2>Generador de PDF</h2> <p>Haz clic en el botón para generar y descargar un PDF desde el servidor.</p> <DownloadLink label={loading ? "Generando PDF..." : "Descargar PDF"} filename="documento_generado.pdf" exportFile={() => pdfData || generarPDF()} style={{ padding: '10px 15px', backgroundColor: '#4CAF50', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '16px', textDecoration: 'none', display: 'inline-block' }} disabled={loading} /> {error && <p style={{ color: 'red', marginTop: '10px' }}>{error}</p>} </div> ); }; export default DescargaPDF;
import React, { useState } from 'react'; import axios from 'axios'; import useDownload from './useDownload'; const DescargaPDF = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const download = useDownload(); const handleDownload = async () => { setLoading(true); setError(null); try { const response = await axios.post('http://localhost/download_B/generar_pdf.php', {}, { responseType: 'blob', }); download(response.data, 'documento_generado.pdf'); } catch (err) { setError('Error al generar el PDF. Por favor, inténtalo de nuevo.'); console.error('Error:', err); } finally { setLoading(false); } }; return ( <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}> <h2>Generador de PDF</h2> <p>Haz clic en el botón para generar y descargar un PDF desde el servidor.</p> <button onClick={handleDownload} disabled={loading} style={{ padding: '10px 15px', backgroundColor: '#4CAF50', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer', fontSize: '16px' }} > {loading ? 'Generando...' : 'Generar y Descargar PDF'} </button> {error && <p style={{ color: 'red', marginTop: '10px' }}>{error}</p>} </div> ); }; export default DescargaPDF;
import { useCallback } from 'react'; const useDownload = () => { const downloadFile = useCallback((data, filename) => { const url = window.URL.createObjectURL(new Blob([data])); const link = document.createElement('a'); link.href = url; link.setAttribute('download', filename); document.body.appendChild(link); link.click(); link.parentNode.removeChild(link); window.URL.revokeObjectURL(url); }, []); return downloadFile; }; export default useDownload;
Son 3 formas de hacer lo mismo «funcionalmente» y la «versión3», es la que permite reutilizar el código «useDownload.js», si esta funcionalidad la utilizamos en varios puntos en la aplicación.
Espero que los ejemplos sean ilustrativos, así como la utilización de las IA’s en estos lenguajes generales REACT y PHP, para que podáis apreciar el potencial que hay detrás de todo esto.
Os dejo los ejemplos para que lo instaléis en vuestros PC’s y podáis hacer las pruebas y cambios que consideréis portunos.