Cómo convertir una imágen en Base64 en un archivo de imágen y subirlo asíncronamente al servidor usando jQuery

La primera solución que querrás implementar y probablemente la unica idea que se le occurre a todo mundo, cuando necesitas subir una imágen de formato Base64 al servidor, es la de subir la cadena de texto pura en base64, procesarla en el servidor y convertirla en un archivo. Esta implementación funciona a la perfección, sin embargo tiene sus desventajas, por ejemplo, todo servidor tiene un límite de carga en información enviado en formato POST, por lo que si el tamaño de la cadena base64 es mayor a este limite la información no llegará.

Si buscas una alternativa optima para imágenes de mayor tamaño, en este artículo te enseñaremos a convertir tu cadena de Base64 en un Blob que se subira como un archivo plano al servidor. Esta acción es equivalente al que un usuario arrastre algun archivo a un campo de archivo en el formulario.

Implementación

Para comenzar, necesitamos convertir una cadena base64 en un "archivo" usando Javascript, para hacerlo, vamos a convertir una cadena Base64 en un Blob y este se interpretará como un archivo en nuestro servidor.

Vamos a utilizar el siguiente método para convertir una cadena base64 en un blob:

/**
 * Convierte una cadena de base64 en un Blob de acuerdo al tipo de datos que se envie y su contentType.
 * 
 * @param b64Data {String} La cadena de texto en base64 sin su incluido
 * @param contentType {String} el tipo de contenido p. ej (image/jpeg - image/png - text/plain)
 * @param sliceSize {Int} Tamaño de las porcioens a procesar el número de caracteres en bytes
 * @see http://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
 * @return Blob
 */
function b64toBlob(b64Data, contentType, sliceSize) {
        contentType = contentType || '';
        sliceSize = sliceSize || 512;

        var byteCharacters = atob(b64Data);
        var byteArrays = [];

        for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
            var slice = byteCharacters.slice(offset, offset + sliceSize);

            var byteNumbers = new Array(slice.length);
            for (var i = 0; i < slice.length; i++) {
                byteNumbers[i] = slice.charCodeAt(i);
            }

            var byteArray = new Uint8Array(byteNumbers);

            byteArrays.push(byteArray);
        }

      var blob = new Blob(byteArrays, {type: contentType});
      return blob;
}

Ahora, como simplemente no puedes adjuntar un blob a una entrada de archivo visible para que el usuario lo envie, debemos hacerlo de forma asíncrona mediante una solicitud POST. Vamos a utilizar el siguiente formulario HTML:

<form id="myAwesomeForm" method="post" action="accion_que_guarda_imagenes.php">
    <!-- Nombre de archivo -->
    <input type="text" id="filename" name="filename" />
    <!-- Boton de enviar -->
    <input type="submit" id="submitButton" name="submitButton" />
</form>

El envío del formulario anterior solo enviaría el parámetro de texto del nombre de archivo pero no ningún archivo por si mismo. Para ello, usaremos Javascript para obtener la información del formulario y pasarlo como primer parámetro a una nueva instancia de FormData, que incluirá la entrada de texto a nuestro formulario asíncrono, además, procesará su cadena de Base64 para que se ajuste al formato requerido por la función b64toblob (primer parámetro solo la base64 cadena sin tipo de contenido y como segundo parámetro solo el tipo de datos):

// Obtener formulario
var form = document.getElementById("myAwesomeForm");

var ImageURL = "data:image/gif;base64,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";
// Divide la cadena de base64 en información real y tipo de información
var block = ImageURL.split(";");
// Obtener tipo de contenido, en este caso "image/gif"
var contentType = block[0].split(":")[1];// In this case "image/gif"
// Obtener información real en base64, en este caso "iVBORw0KGg...."
var realData = block[1].split(",")[1];

// Convertir a blob
var blob = b64toBlob(realData, contentType);

// Crear FormaData y agrega información de archivo
var formDataToUpload = new FormData(form);
formDataToUpload.append("image", blob);

Ahora que FormData tiene un archivo , solo necesitas enviarlo de forma asíncrona utilizando jQuery:

/**
 * El siguiente código debería enviar 2 parametros POST:
 * 
 * filename: proporcionado por el campo de texto
 * image: un archivo, dinámicamente creado y añadido a partir de una
 *        cadena de base64 usando JS
 *
 * Depende de ti como lo recibes el archivo en el servidor.
 */
$.ajax({
    url: "/codigo-php-que-manipula-cargadearchivos.php",
    // Añade como información la instancia de FormData anteriormente creada y modificada
    data: formDataToUpload,
    type: "POST",
    contentType: false,
    processData: false,
    cache: false,
    // Cambia el datatype de acuerdo al tipo de información que recibes de tu servidor
    dataType:"json",
    error: function(err){
        console.error(err);
    },
    success: function(data){
        console.log(data);
    },
    complete: function(){
        console.log("Solciitud finalizada.");
    }
});

Depende de ti cómo manejar el código anterior, puedes evitar el evento de envío del formulario y luego ejecutar el código como se muestra en el siguiente ejemplo:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Cadena de texto Base64 a archivo en formulario</title>
    </head>
    <body>
        <form id="myAwesomeForm" method="post" action="endpoint.php">
            <!-- Nombre de archivo -->
            <input type="text" id="filename" name="filename" />
            <!-- Boton de enviar -->
            <input type="submit" id="submitButton" name="submitButton" />
        </form>
        <script>
            function b64toBlob(b64Data, contentType, sliceSize) {
                contentType = contentType || '';
                sliceSize = sliceSize || 512;

                var byteCharacters = atob(b64Data);
                var byteArrays = [];

                for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
                    var slice = byteCharacters.slice(offset, offset + sliceSize);

                    var byteNumbers = new Array(slice.length);
                    for (var i = 0; i < slice.length; i++) {
                        byteNumbers[i] = slice.charCodeAt(i);
                    }

                    var byteArray = new Uint8Array(byteNumbers);

                    byteArrays.push(byteArray);
                }

              var blob = new Blob(byteArrays, {type: contentType});
              return blob;
            }

            $("#myAwesomeForm").submit(function(e){
                e.preventDefault();
                appendFileAndSubmit();
            });

            function appendFileAndSubmit(){
                // Obtener formulario
                var form = document.getElementById("myAwesomeForm");

                var ImageURL = "data:image/gif;base64,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";
                // Divide la cadena de base64 en información real y tipo de información
                var block = ImageURL.split(";");
                // Obtener tipo de contenido, en este caso "image/gif"
                var contentType = block[0].split(":")[1];
                // Obtener información real en base64, en este caso "iVBORw0KGg...."
                var realData = block[1].split(",")[1];// 

                // Convertir a blob
                var blob = b64toBlob(realData, contentType);

                // Crear FormaData y agrega información de archivo
                var fd = new FormData(form);
                fd.append("image", blob);

                // Enviar información y subir archivo
                $.ajax({
                    url:"endpoint.php",
                    // La clase FormData está disponible en todos los exploradores modernos
                    data: fd,
                    type:"POST",
                    contentType:false,
                    processData:false,
                    cache:false,
                    dataType:"json",
                    error:function(err){
                        console.error(err);
                    },
                    success:function(data){
                        console.log(data);
                    },
                    complete:function(){
                        console.log("Solicitud finalizada");
                    }
                });
            }
        </script>
    </body>
</html>

Obteniendo el archivo en el lado del servidor

Depende de ti cómo recibir el archivo en el servidor, ya que no sabemos si estás utilizando PHP, un framework, Django e incluso otros lenguajes de programación, sin embargo, proporcionamos 2 ejemplos con PHP que podrían ser útiles para entender como obtener el archivo que enviamos desde el lado del cliente:

Symfony 2

<?php

use Symfony\Component\HttpFoundation\Request;

public function retrieveAction(Request $request){
    // Obtener el archivo con el nombre especificado del formulario
    // Puedes ver mas con var_dump($request->files->all());
   $file = $request->files->get('image');
   $nameInTextInput = $request->request->get("filename");
}

PHP plano

// Has algo con la imágen en formato GIF
$file = $_FILES['image']['tmp_name'];

Que te diviertas !

Esto podría ser de tu interes

Conviertete en un programador más sociable