¿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?

Vastamente
fuente

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;
   }
   //...
}
Дмитрий Васильев
fuente
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 .

Dmitri Pavlutin
fuente
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)
);
joshua.paling
fuente
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);
  });
}
Arnaud P
fuente
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
12

La función JavaScript btoa () se puede usar para convertir datos en una cadena codificada en base64

Pranav Maniar
fuente
66
btoa funciona solo con string. ¿Cómo usarlo con archivo?
Vassily
10
primero tendrá que leer el archivo y luego pasarlo a esta función. Algo así como jsfiddle.net/eliseosoto/JHQnk
Pranav Maniar
1
@PranavManiar Tu violín ya no funciona. ¿Puedes actualizar el enlace?
Dan
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);
tkd_aj
fuente
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]);
    }
}
usuario2120895
fuente