¿Cómo convertir archivos a base64 en JavaScript?


187

Ahora obtengo el objeto File por esta línea:

file = document.querySelector('#files > input[type="file"]').files[0]

Necesito enviar este archivo a través de json en base 64. ¿Qué debo hacer para convertirlo en una cadena base64?

Respuestas:


118

Modo ES6 moderno (asíncrono / espera)

const toBase64 = file => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result);
    reader.onerror = error => reject(error);
});

async function Main() {
   const file = document.querySelector('#myfile').files[0];
   console.log(await toBase64(file));
}

Main();

UPD:

Si quieres atrapar errores

async function Main() {
   const file = document.querySelector('#myfile').files[0];
   const result = await toBase64(file).catch(e => Error(e));
   if(result instanceof Error) {
      console.log('Error: ', result.message);
      return;
   }
   //...
}

Este código es incorrecto Si tiene awaituna función que devuelve una Promesa rechazada, la llamada no le devolverá un Error; será arrojado y tendrás que atraparlo.
Dancrumb

1
Gran ejemplo de uso de las funciones y promesas asíncronas
Thiago Frias

292

Pruebe la solución usando la FileReader clase :

function getBase64(file) {
   var reader = new FileReader();
   reader.readAsDataURL(file);
   reader.onload = function () {
     console.log(reader.result);
   };
   reader.onerror = function (error) {
     console.log('Error: ', error);
   };
}

var file = document.querySelector('#files > input[type="file"]').files[0];
getBase64(file); // prints the base64 string

Tenga en cuenta que .files[0]es un Filetipo, que es una subclase de Blob. Por lo tanto, se puede usar con FileReader.
Vea el ejemplo de trabajo completo .


2
Lea más sobre FileReader API: developer.mozilla.org/en-US/docs/Web/API/FileReader y soporte del navegador: caniuse.com/#feat=filereader
Lukas Liesis

77
Traté de usar return reader.resultdesde la getBase64()función (en lugar de usar console.log(reader.result)) porque quiero capturar la base64 como una variable (y luego enviarla a Google Apps Script). Llamé a la función con: var my_file_as_base64 = getBase64(file)e intenté imprimir en la consola con console.log(my_file_as_base64 )y acabo de recibir undefined. ¿Cómo puedo asignar adecuadamente la base64 a una variable?
user1063287

1
Hice una pregunta con el comentario anterior si alguien puede responder. stackoverflow.com/questions/47195119/…
user1063287

Necesito abrir este archivo Base64 en el navegador con el mismo nombre de archivo, lo estoy abriendo usando window.open (url, '_blank') que funciona bien, ¿cómo puedo darle el nombre de archivo? por favor ayuda.
Munish Sharma el

¡Gracias! Creo que esto no se explica muy bien en developer.mozilla.org/en-US/docs/Web/API/FileReader/…
johey

124

Si buscas una solución basada en promesas, este es el código de @ Dmitri adaptado para eso:

function getBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result);
    reader.onerror = error => reject(error);
  });
}

var file = document.querySelector('#files > input[type="file"]').files[0];
getBase64(file).then(
  data => console.log(data)
);

Necesito abrir este archivo Base64 en el navegador con el mismo nombre de archivo, lo estoy abriendo usando window.open (url, '_blank') que funciona bien, ¿cómo puedo darle el nombre del archivo? por favor ayuda.
Munish Sharma

42

A partir de las respuestas de Dmitri Pavlutin y joshua.paling, aquí hay una versión extendida que extrae el contenido base64 (elimina los metadatos al principio) y también garantiza que el relleno se realice correctamente .

function getBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => {
      let encoded = reader.result.toString().replace(/^data:(.*,)?/, '');
      if ((encoded.length % 4) > 0) {
        encoded += '='.repeat(4 - (encoded.length % 4));
      }
      resolve(encoded);
    };
    reader.onerror = error => reject(error);
  });
}

2
Chrome 69, el primer reemplazo es capturar el archivo vacío, falta el segundo reemplazo con coma - codificado = reader.result.replace ("data:", "") .replace (/^.*; base64, /, "");
user3333134

Mi palabra, extrañaba ese coma. Lo increíble es que no pareció molestarme en absoluto en mi backend, todavía pude cargar archivos de Excel con éxito o_O. También arreglé la expresión regular para tener en cuenta su caso de uso de archivos vacíos. Gracias.
Arnaud P

2
Tengo una versión aún más fácil: resolve(reader.result.replace(/^.*,/, ''));. Dado que el coma ,está fuera del alfabeto base64, podemos quitar todo lo que aparezca hasta el coma, incluido el mismo. stackoverflow.com/a/13195218/1935128
Johnride

Ok, gracias por el aviso, aunque de acuerdo con la expresión regular que escribí aquí (necesitaría experimentar nuevamente para estar seguro), puede haber data:, sin ninguna coma, así que mantendré esa primera parte como está. He actualizado la respuesta en consecuencia.
Arnaud P

1
@ArnaudP Error mecanografiado: la propiedad 'reemplazar' no existe en el tipo 'cadena | ArrayBuffer '.
Romel Gomez


5

Aquí hay un par de funciones que escribí para obtener un archivo en formato json que se puede pasar fácilmente:

    //takes an array of JavaScript File objects
    function getFiles(files) {
        return Promise.all(files.map(file => getFile(file)));
    }

    //take a single JavaScript File object
    function getFile(file) {
        var reader = new FileReader();
        return new Promise((resolve, reject) => {
            reader.onerror = () => { reader.abort(); reject(new Error("Error parsing file"));}
            reader.onload = function () {

                //This will result in an array that will be recognized by C#.NET WebApi as a byte[]
                let bytes = Array.from(new Uint8Array(this.result));

                //if you want the base64encoded file you would use the below line:
                let base64StringFile = btoa(bytes.map((item) => String.fromCharCode(item)).join(""));

                //Resolve the promise with your custom file structure
                resolve({ 
                    bytes: bytes,
                    base64StringFile: base64StringFile,
                    fileName: file.name, 
                    fileType: file.type
                });
            }
            reader.readAsArrayBuffer(file);
        });
    }

    //using the functions with your file:

    file = document.querySelector('#files > input[type="file"]').files[0]
    getFile(file).then((customJsonFile) => {
         //customJsonFile is your newly constructed file.
         console.log(customJsonFile);
    });

    //if you are in an environment where async/await is supported

    files = document.querySelector('#files > input[type="file"]').files
    let customJsonFiles = await getFiles(files);
    //customJsonFiles is an array of your custom files
    console.log(customJsonFiles);

1
¡Promételo todo, basado en un array.map funciona muy bien! Al menos para mi.
davidwillianx

0
onInputChange(evt) {
    var tgt = evt.target || window.event.srcElement,
    files = tgt.files;
    if (FileReader && files && files.length) {
        var fr = new FileReader();
        fr.onload = function () {
            var base64 = fr.result;
            debugger;
        }
        fr.readAsDataURL(files[0]);
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.