php

Cargar archivo a PHP desde JavaScript con FormData

Introducción

Enviar datos con AJAX es algo relativamente fácil; pues al final de todo son simples datos. Ya sean cadenas o números, todo es texto. Pero algo distinto pasa cuando queremos cargar un archivo o fichero con JavaScript hacia PHP.

Esto es más complicado cuando queremos hacerlo con AJAX, pues no hay una forma estandarizada de enviar un archivo; y nos topamos con que el usuario podría seleccionar archivos pesados.

Lo que nos quedaría sería poner un formulario y agregar un <input type="file"> pero nuestra página perdería dinamismo.

Afortunadamente desde hace algunos años existe la API de FormData, la cual ofrece una manera de enviar todo tipo de datos como se enviarían en un formulario; con la ventaja de poder hacerlo sin interrumpir al usuario o recargar la página.

Subir archivo a PHP con JavaScript + FormData

Esto será muy, muy simple y fácil de hacer. Lo que haremos será subir un archivo de cualquier tipo con JavaScript y guardarlo con PHP. No nos fijaremos en la seguridad o esas cosas.

Todo eso será usando FormData y fetch, el remplazo nativo de XMLHttpRequest. Por cierto, para esto debes tener configurado e instalado un servidor con PHP.

Ya habíamos hecho algo similar pero usando jQuery y CodeIgniter; ahora usaremos JavaScript y PHP sin frameworks.

El lado del cliente

Sólo necesitamos poner el input de tipo file para tomar el archivo y para darle al usuario una interfaz para que pueda seleccionarlo. Quedaría así:

<input id="inputFile" type="file">
<br><br>
<button id="btnEnviar">Enviar</button>

También agregamos un botón que será el encargado de enviar el archivo. Estos dos elementos los tomamos en el script:

const btnEnviar = document.querySelector("#btnEnviar");
const inputFile = document.querySelector("#inputFile");

Necesitamos a ambos. btnEnviar para escuchar su click y realizar la lógica necesaria; inputFile para obtener los archivos que el usuario ha elegido.

Entonces, en el click del botón agregamos esto:

btnEnviar.addEventListener("click", () => {
    if (inputFile.files.length > 0) {
        let formData = new FormData();
        formData.append("archivo", inputFile.files[0]); // En la posición 0; es decir, el primer elemento
        fetch("guardar.php", {
            method: 'POST',
            body: formData,
        })
            .then(respuesta => respuesta.text())
            .then(decodificado => {
                console.log(decodificado);
            });
    } else {
        // El usuario no ha seleccionado archivos
        alert("Selecciona un archivo");
    }
});

Lee sobre las funciones flecha aquí si no entiendes eso de () =>.

Primeramente comprobamos si hay archivos cargados. Los mismos son representados como un arreglo, así que usamos la propiedad length. Si no hay archivos, le avisamos al usuario con un alert.

Lo que estamos haciendo es crear un objeto de tipo FormData.

Le agregamos un elemento (podrían ser miles más, y no todos tienen que ser archivos, podrían ser cadenas o números) con la clave “archivo” justo en donde hacemos formData.append("archivo", inputFile.files[0]).

Es importante entender que esta clave servirá más tarde para leer el archivo del lado del servidor. Le estamos agregando lo que haya en el arreglo de archivos en la posición 0; es decir, el primer elemento.

Más tarde hacemos una petición con fetch, la cual es de tipo POST.

Como cuerpo enviamos el objeto formData. Cuando la promesa se resuelva decodificamos la respuesta como texto y cuando esta segunda promesa se resuelva imprimimos el contenido; en resumen simplemente estamos imprimiendo lo que el servidor dijo.

Aquí termina el lado del cliente.

Lado del servidor con PHP

No sé si es coincidencia, pero siempre el código del lado del servidor es más corto. Supongo que ha de ser porque no validamos ni hacemos tantas cosas como en el lado del cliente.

Pues bien, nuestro archivo estará en el arreglo superglobal de PHP $_FILES; en la posición clave, indicada por el nombre que le pusimos en el append de JavaScript.

Como le pusimos “archivo”, aquí también accederemos a “archivo” así:

<?php
$archivo = $_FILES["archivo"];

Pero sólo estamos obteniendo su información; porque cuando lo subimos se mueve a una carpeta temporal.

Para guardarlo en el disco duro, o mejor dicho, en donde está el script, usamos la función move_uploaded_file que quiere decir algo como mover_archivo_subido.

<?php
$resultado = move_uploaded_file($archivo["tmp_name"], $archivo["name"]);

Este toma dos argumentos. El primero indica en dónde está el archivo original, y esa ruta no la sabemos pero PHP sí, y la puso en $archivo["tmp_name"].

El segundo argumento indica en dónde pondremos el archivo. Aquí, por simplicidad, lo estoy guardando con su nombre original aunque no recomiendo hacerlo si no confías en el usuario final.

Si queremos, podemos cambiar el segundo argumento por un nombre generado por nosotros. Se me ocurre usar uniqid y concatenarlo con la extensión del archivo, la cual podemos obtener con pathinfo.

La función que mueve el archivo devuelve un booleano. Dependiendo de lo que devuelva imprimimos un mensaje, y eso será lo que reciba el cliente como respuesta.

<?php
$archivo = $_FILES["archivo"];
$resultado = move_uploaded_file($archivo["tmp_name"], $archivo["name"]);
if ($resultado) {
    echo "Subido con éxito";
} else {
    echo "Error al subir archivo";
}

Probando código

Aquí dejo un GIF:

Subir archivos con JavaScript, fetch y FormData a PHP

En él se puede observar que arrastramos un archivo (también podríamos simplemente pulsar el botón de Seleccionar archivo).

La carga es demasiado rápida, y el archivo aparece copiado a la carpeta en donde está el script.

Código final

Lo dejo en GitHub.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

2 semanas hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.