JAVASCRIPTLaboratorioPHP

Envío y recepción de un archivo desde JavaScript con la API Fetch a un servidor con PHP

Introducción

Esta práctica trata sobre enviar un archivo con la API Fetch desde JavaScript en un host X (ejemplo.host-1.com) a otro host Z (ejemplo.host-2.com) donde será recibido y procesado con PHP para dar una respuesta JSON.

Requisitos

  • 1 host X configurado para archivos .html y .js
  • 1 host Z configurado para archivos .php

Paso 1: Configuraciones en el archivo php.ini

  • file_uploads = on

Verificar que los valores de las siguientes variables estén establecidas de un modo que puedan cumplir con a las necesidades de cada quien

  • upload_max_filesize
  • post_max_size
  • memory_limit
  • max_execution_time
  • max_input_time

Paso 2: Permisos y propietarios del directorio

Verificar que los script de PHP puedan realmente tener permisos para escribir en el directorio donde se pretende subir el archivo, así que hay que verificar con cuidado esto, que dependerá del juego de configuraciones establecidas entre el sistema de directorios, Apache y PHP. Por ejemplo, normalmente quien ejecuta los archivos de PHP es el usuario www-data:www-data y sí el directorio donde se pretende subir es de user-x:user-x entonces dependiendo de los permisos 755, 775 se podrá realizar sin problemas o no. En caso de tener problemas de permisos se pude probar momentáneamente con 777 sin embargo esto es malo para la seguridad del sistema, por tanto hay que procurar evitarlo siempre.

Paso 3: Crear el código que enviará el archivo

a.- Creamos un archivo .html común y corriente

<!DOCTYPE html>
<html lang="es_MX">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Envío de Archivo con Fetch</title>
</head>
<body>
    
</body>
</html>

b.- Ponemos un input y un botón

<!DOCTYPE html>
<html lang="es_MX">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Envío de Archivo con Fetch</title>
</head>
<body>
    <h1>Campo y botón para enviar archivo</h1>
        <input type="file" />
        <button type="button">Enviar Archivo</button>
</body>
</html>

c.- Creamos la función que enviará el archivo

<!DOCTYPE html>
<html lang="es_MX">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Envío de Archivo con Fetch</title>
</head>
<body>
    <h1>Campo y botón para enviar archivo</h1>
        <input type="file" />
        <button type="button">Enviar Archivo</button>



    <script>
        function enviarArchivo(){
            const datosFormulario = new FormData();

            const campoArchivo = document.querySelector('input[type="file"]');
            datosFormulario.append("avatar", campoArchivo.files[0]);

            var respuesta_clonada;

            fetch("http://ejemplo.host-2.com/archivos/recepcion-archivo-1.php", {
                    method: "POST",
                    body: datosFormulario,
            })
            .then((respuesta) => {
                //Para obtener el estado de la respuesta
                console.info(respuesta.status);
                //Devuelve verdadero si la respuesta es 2xx
                console.info(respuesta.ok);
                //Para obtener los encabezados
                console.info(respuesta.headers);
                //Obtiene el tipo de petición
                console.info(respuesta.type);
                //Obtiene la URL de quien emitió la respuesta
                console.info(respuesta.url);
                //Obtenemos el cuerpo de la respuesta solo sí el contenido es un objeto JSON
                respuesta.json()
                .then((json) => {
                console.log('Tuviste éxito');
                console.log(json);
                })
                .catch(error_json => {
                    console.warn('Ocuarrio un error con el formato JSON');
                    console.warn(error_json);
                })
            })
            .catch(error => {
                console.error('Ocuarrio un error');
                console.error(error);
            })
        }
    </script>
</body>
</html>

d.- Hacemos que el botón llame a la función que enviará el archivo

<!DOCTYPE html>
<html lang="es_MX">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Envío de Archivo con Fetch</title>
</head>
<body>
    <h1>Campo y botón para enviar archivo</h1>
        <input type="file" />
        <button type="button" onclick="enviarArchivo()">Enviar Archivo</button>



    <script>
        function enviarArchivo(){
            const datosFormulario = new FormData();

            const campoArchivo = document.querySelector('input[type="file"]');
            datosFormulario.append("avatar", campoArchivo.files[0]);

            var respuesta_clonada;

            fetch("http://php81.programadorvagabundo.com/archivos/recepcion-archivo-1.php", {
                    method: "POST",
                    body: datosFormulario,
            })
            .then((respuesta) => {
                //Para obtener el estado de la respuesta
                console.info(respuesta.status);
                //Devuelve verdadero si la respuesta es 2xx
                console.info(respuesta.ok);
                //Para obtener los encabezados
                console.info(respuesta.headers);
                //Obtiene el tipo de petición
                console.info(respuesta.type);
                //Obtiene la URL de quien emitió la respuesta
                console.info(respuesta.url);
                //Obtenemos el cuerpo de la respuesta solo sí el contenido es un objeto JSON
                respuesta.json()
                .then((json) => {
                console.log('Tuviste éxito');
                console.log(json);
                })
                .catch(error_json => {
                    console.warn('Ocuarrio un error con el formato JSON');
                    console.warn(error_json);
                })
            })
            .catch(error => {
                console.error('Ocuarrio un error');
                console.error(error);
            })
        }
    </script>
</body>
</html>

Paso 4: Crear el código que recibirá y procesara el archivo

Creamos un archivo .php que será el encargado de recibir y procesar el archivo enviado desde la API Fetch del archivo .html

<?php
//Si quieres permitir todos los orígenes *** header('Access-Control-Allow-Origin: *');
//Acceso sólo para un origen
header('Access-Control-Allow-Origin: http://ejemplo.host-1.com');
// Encabezado de contenido de acuerdo a lo que se va a devolver
header("Content-type: application/json; charset=utf-8");



$directorio_subidas = 'subidas/';
//Obtiene el nombre del archivo del nombre del archivo subido por el cliente
$nombre_archivo = basename($_FILES['avatar']['name']);
$url_archivo_subido = $directorio_subidas . $nombre_archivo;



if (move_uploaded_file($_FILES['avatar']['tmp_name'], $url_archivo_subido)) {
  //Para devolver un estado de respuesta específico (201)
  http_response_code(201);
  //Crea un objeto JSON
  echo json_encode([
    'mensaje' => 'Se subió correctamente el archivo',
    'url' => $url_archivo_subido
  ]);

} else {
  //Para devolver un estado de respuesta específico (500)
  http_response_code(500);
  //Crea un objeto JSON
  echo json_encode(['No se pudo subir el archivo']);
}

Resumen

Felicidades lo lograste 🙂

Otras Consideraciones Importantes

  • Solo podemos elegir un método de lectura corporal, por ejemplo, si ya tenemos la respuesta con respuesta.text(), entonces response.json() no funcionará, ya que el contenido del cuerpo ya ha sido procesado, por esto, si se quiere seguir ocupando la respuesta se tendría que clonar antes con respuesta.clone() y almacenar el valor en una variable.
  • En esta prueba el modo predefinido sobre CORS para fetch fue ‘cors’, así que esto quiere decir que debe estar correctamente configurado el host a donde se enviaron los datos para que pueda dar una respuesta apropiada. De lo contrario nos podra dar errores como respuesta. Si te da errores el host a donde enviaste los datos puedes ocupar el modo ‘no-cors’ que se establece dentro de las opciones de fetch() pero en consecuencia solo podras recibir una respuesta vacía o seá la variable response de fetch API estará vacía

Bibliografía

JS-PHP

https://www.w3schools.com/php/php_file_upload.asp

https://phpenthusiast.com/blog/javascript-fetch-api-tutorial

https://www.espai.es/blog/2019/09/como-enviar-y-recibir-datos-con-la-api-fetch/

FormData

https://developer.mozilla.org/es/docs/Web/API/FormData

https://developer.mozilla.org/en-US/docs/Web/API/FormData/append

Fetch

https://developer.mozilla.org/en-US/docs/Web/API/Response/headers

https://developer.mozilla.org/en-US/docs/Web/API/Response

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

https://developer.mozilla.org/en-US/docs/Web/API/fetch

https://developer.mozilla.org/en-US/docs/Web/API/Request/mode

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API

https://developer.mozilla.org/en-US/docs/Web/API/Request

https://developer.mozilla.org/en-US/docs/Web/API/Request/formData

https://support.stripe.com/questions/how-to-fix-syntaxerror-unexpected-token-in-json-at-position-0?locale=es-419

https://fetch.spec.whatwg.org/#concept-filtered-response-opaque

https://processwire.com/talk/topic/27260-input-post-vs-js-fetch/

https://processwire.com/talk/topic/22516-when-using-js-fetch-it-seems-config-ajax-is-ignored/

https://javascript.info/fetch

PHP-Archivos

https://image.intervention.io/v2/introduction/configuration

https://www.hostinger.com/tutorials/what-is-php-ini/

https://www.php.net/manual/es/reserved.variables.files

https://www.php.net/manual/es/features.file-upload.post-method.php

PHP-Network

https://www.php.net/manual/es/function.http-response-code.php

HTTP

http://www.faqs.org/rfcs/rfc2616.html

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *