Como converter um arquivo para base64 em JavaScript?

186

Agora estou recebendo o objeto File por esta linha:

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

Preciso enviar esse arquivo via json na base 64. O que devo fazer para convertê-lo em string base64?

Vassily
fonte

Respostas:

118

Maneira ES6 moderna (assíncrona / aguardar)

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:

Se você deseja pegar erros

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;
   }
   //...
}
Дмитрий Васильев
fonte
Este código está incorreto. Se awaituma função que retorna uma Promessa rejeitada, você não receberá um Erro retornado pela chamada; será lançado e você precisará pegá-lo.
Dancrumb
1
Ótimo exemplo de uso das funções e promessas assíncronas
Thiago Frias
292

Experimente a solução usando a FileReader classe :

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

Observe que esse .files[0]é um Filetipo, que é uma subcategoria de Blob. Assim, pode ser usado com FileReader.
Veja o exemplo de trabalho completo .

Dmitri Pavlutin
fonte
2
ler mais sobre FileReader API: developer.mozilla.org/en-US/docs/Web/API/FileReader apoio e navegador: caniuse.com/#feat=filereader
Lukas Liesis
7
Tentei usar return reader.resulta getBase64()função (em vez de usar console.log(reader.result)) porque quero capturar a base64 como uma variável (e enviá-la ao script do Google Apps). Chamei a função com: var my_file_as_base64 = getBase64(file)e tentei imprimir no console console.log(my_file_as_base64 )e acabei de receber undefined. Como atribuir corretamente a base64 a uma variável?
precisa saber é o seguinte
1
Fiz uma pergunta a partir do comentário acima, se alguém puder responder. stackoverflow.com/questions/47195119/…
user1063287
Eu preciso abrir esse arquivo Base64 no navegador com o mesmo nome de arquivo, estou abrindo-o usando window.open (url, '_blank') que está funcionando bem, como posso atribuir um nome de arquivo a isso? por favor ajude.
Munish Sharma
Obrigado! Eu acho que isso não é explicado muito bem no developer.mozilla.org/pt-BR/docs/Web/API/FileReader/…
johey
123

Se você procura uma solução baseada em promessas, este é o código do @ Dmitri adaptado para isso:

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
fonte
Eu preciso abrir esse arquivo Base64 no navegador com o mesmo nome de arquivo, estou abrindo-o usando window.open (url, '_blank') que está funcionando bem, como posso atribuir um nome de arquivo a isso? por favor ajude.
Munish Sharma
42

Com base nas respostas Dmitri Pavlutin e joshua.paling, aqui está uma versão estendida que extrai o conteúdo base64 (remove os metadados no início) e também garante que o preenchimento seja feito corretamente .

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
fonte
2
Chrome 69, a primeira substituição é pegar o arquivo vazio, a segunda substituição está ausente por vírgula - codificada = reader.result.replace ("data:", "") .replace (/^.*; base64, /, "");
user3333134
Minha palavra, eu realmente senti falta daquele coma. O que é incrível é que isso não parecia incomodar meu back-end, eu ainda era capaz de fazer upload de arquivos do Excel com sucesso o_O. Corrigi o regex para levar em conta seu caso de uso de arquivo vazio também. Obrigado.
Arnaud P
2
Eu tenho uma versão ainda mais fácil: resolve(reader.result.replace(/^.*,/, ''));. Como o coma ,está fora do alfabeto base64, podemos remover qualquer coisa que surja até e incluindo o coma. stackoverflow.com/a/13195218/1935128
Johnride
Ok, obrigado pelo aviso, embora de acordo com a regex que escrevi aqui (eu precisaria experimentar novamente para ter certeza), pode haver apenas data:, sem vírgula, por isso vou manter a primeira parte como está. Atualizei a resposta de acordo.
Arnaud P
1
@ArnaudP Erro de texto: A propriedade 'replace' não existe no tipo 'string | ArrayBuffer '.
Romel Gomez
12

A função JavaScript btoa () pode ser usada para converter dados em string codificada em base64

Pranav Maniar
fonte
6
btoa funciona apenas com string. Como usá-lo com arquivo?
Vassily
10
você terá que ler o arquivo primeiro e depois passá-lo para esta função .. Algo como jsfiddle.net/eliseosoto/JHQnk
Pranav Maniar
1
@PranavManiar Seu violino não funciona mais. Você pode atualizar o link?
Dan
5

Aqui estão algumas funções que escrevi para obter um arquivo em um formato json que pode ser transmitido facilmente:

    //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
fonte
1
Prometa tudo, com base em um array.map funciona muito bem! Pelo menos para mim.
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]);
    }
}
user2120895
fonte