Obtenha dados de arquivo de codificação Base64 no formulário de entrada

100

Eu tenho um formulário HTML básico do qual posso obter algumas informações que estou examinando no Firebug.

Meu único problema é que estou tentando codificar os dados do arquivo em base64 antes de enviá-los ao servidor, onde precisam estar nessa forma para serem salvos no banco de dados.

<input type="file" id="fileupload" />

E em Javascript + jQuery:

var file = $('#fileupload').attr("files")[0];

Tenho algumas operações baseadas em javascript disponível: .getAsBinary (), .getAsText (), .getAsTextURL

No entanto, nenhum desses retorna texto utilizável que pode ser inserido, pois contém 'caracteres' inutilizáveis - não quero que ocorra um 'postback' no meu arquivo carregado e preciso ter vários formulários direcionados a objetos específicos, por isso é importante. obtenha o arquivo e use Javascript desta forma.

Como devo obter o arquivo de forma que possa usar um dos codificadores Javascript base64 que estão amplamente disponíveis !?

obrigado

Atualização - Começando bounty aqui, preciso de suporte para vários navegadores !!!

É aqui que estou:

<input type="file" id="fileuploadform" />

<script type="text/javascript">
var uploadformid = 'fileuploadform';
var uploadform = document.getElementById(uploadformid);


/* method to fetch and encode specific file here based on different browsers */

</script>

Alguns problemas com o suporte para vários navegadores:

var file = $j(fileUpload.toString()).attr('files')[0];
fileBody = file.getAsDataURL(); // only would works in Firefox

Além disso, o IE não suporta:

var file = $j(fileUpload.toString()).attr('files')[0];

Portanto, tenho que substituir por:

var element = 'id';
var element = document.getElementById(id);

Para suporte do IE.

Isso funciona no Firefox, Chrome e Safari (mas não codifica corretamente o arquivo ou, pelo menos, depois de postado, o arquivo não sai direito)

var file = $j(fileUpload.toString()).attr('files')[0];
var encoded = Btoa(file);

Além disso,

file.readAsArrayBuffer() 

Parece ser compatível apenas com HTML5?

Muitas pessoas sugeriram: http://www.webtoolkit.info/javascript-base64.html

Mas isso só retorna um erro no método UTF_8 antes de codificar em base64? (ou uma string vazia)

var encoded = Base64.encode(file); 
Jordan.baucke
fonte
O que você está enviando? Dados de texto ou binários?
beatgammit de
Dados binários do @tjamenson - qualquer coisa realmente 'documento do Word' 'pdf' 'imagem' etc. Eu ia atribuir o nome do arquivo em outra variável, mas comecei a me perguntar se eu tinha alguma falha fatal no meu entendimento do que é possível com fazer upload de formulários e html - este método é totalmente inviável e não é possível transmitir os dados do arquivo postando-os como uma string? Também sem HTML5 que estou lendo tem algumas soluções -
jordan.baucke
Interessante. Por que você está codificando em base 64 entre o navegador e o servidor e tentando fazer isso no navegador? Parece que se você está tentando proteger os dados, o SSL seria muito mais fácil, pois criptografa todos os dados HTTP (incluindo arquivos) durante a transmissão, e você pode usar uma base de 64 servidores, se necessário para seu banco de dados.
William Walseth
@William Não estou tentando proteger os dados, apenas coloque-os no formato correto. Salesforce.com (plataforma Apex) requer que o banco de dados codificado em base64 antes de o registro ser criado no banco de dados, sua funcionalidade para codificar dados de arquivo em base64 por meio de um formulário está muito mal documentada (descobri como fazer isso depois de fazer esta pergunta). De qualquer forma, parece que os dados do arquivo binário com codificação base64 só são suportados nativamente em HTML5 ou Mozilla File API
jordan.baucke
1
OK, agora vejo porque você precisa disso. Nenhum servidor intermediário + requisitos de nuvem loucos + nenhuma solução de navegador cruzado = 1 bagunça. Desculpe.
William Walseth

Respostas:

121

É totalmente possível em javascript do lado do navegador.

O caminho fácil:

O método readAsDataURL () já pode codificá-lo como base64 para você. Você provavelmente precisará remover o material inicial (até o primeiro), mas isso não é nada demais. Isso acabaria com toda a diversão.

O jeito difícil:

Se você quiser tentar da maneira mais difícil (ou não funcionar), observe readAsArrayBuffer(). Isso lhe dará um Uint8Array e você pode usar o método especificado. Provavelmente, isso só é útil se você quiser mexer nos próprios dados, como manipular dados de imagem ou fazer outra mágica vodu antes de fazer o upload.

Existem dois métodos:

  • Converta em string e use o integrado btoaou similar
    • Não testei todos os casos, mas funciona para mim - basta obter os char-codes
  • Converta diretamente de um Uint8Array para base64

Recentemente, implementei o tar no navegador . Como parte desse processo, fiz minha própria implementação direta Uint8Array-> base64. Não acho que você vá precisar disso, mas está aqui se você quiser dar uma olhada; é muito legal.

O que faço agora:

O código para converter em string de um Uint8Array é muito simples (onde buf é um Uint8Array):

function uint8ToString(buf) {
    var i, length, out = '';
    for (i = 0, length = buf.length; i < length; i += 1) {
        out += String.fromCharCode(buf[i]);
    }
    return out;
}

A partir daí, basta fazer:

var base64 = btoa(uint8ToString(yourUint8Array));

Base64 agora será uma string codificada em base64 e deve ser carregada com perfeição. Tente fazer isso se quiser verificar antes de pressionar:

window.open("data:application/octet-stream;base64," + base64);

Isso fará o download como um arquivo.

Outras informações:

Para obter os dados como um Uint8Array, veja os documentos MDN:

beatgammit
fonte
ótimo - pensei em readAsArrayBuffer()mostrar o que parecia ser um dado base64 correto , mas não iria processar por causa da parte inicial da string - vou tentar agora!
jordan.baucke
1
readAsArrayBuffer () não parece existir no Chrome / Safari e estou assumindo que será problemático em outros navegadores ~ há um equivalente que posso usar nesses outros navegadores?
jordan.baucke de
createObjectURL () <- parece promissor, tentarei implementá-lo, mas tenho certeza de que o IE precisará de uma 3ª roda
jordan.baucke
É possível atribuir um nome de arquivo antes de executar sua solução de download?
Ωmega
@ Ωmega - Não. Tudo o que isso faz é baixar um fluxo binário. Não há como (AFAIK) especificar um nome de arquivo. Porém, alguns navegadores adivinham uma extensão. Para obter os nomes dos arquivos, você terá que fazer upload e baixar novamente.
beatgammit
37

Minha solução foi o uso readAsBinaryString()e btoa()o resultado.

uploadFileToServer(event) {
    var file = event.srcElement.files[0];
    console.log(file);
    var reader = new FileReader();
    reader.readAsBinaryString(file);

    reader.onload = function() {
        console.log(btoa(reader.result));
    };
    reader.onerror = function() {
        console.log('there are some problems');
    };
}
Josef
fonte
3
De longe a maneira mais fácil, por que isso não tem mais votos?
sarink 01 de
14

Usei o FileReader para exibir a imagem ao clicar no botão de upload de arquivo, sem usar nenhuma solicitação Ajax. A seguir está o código que espero que possa ajudar alguém.

$(document).ready(function($) {
    $.extend( true, jQuery.fn, {        
        imagePreview: function( options ){          
            var defaults = {};
            if( options ){
                $.extend( true, defaults, options );
            }
            $.each( this, function(){
                var $this = $( this );              
                $this.bind( 'change', function( evt ){

                    var files = evt.target.files; // FileList object
                    // Loop through the FileList and render image files as thumbnails.
                    for (var i = 0, f; f = files[i]; i++) {
                        // Only process image files.
                        if (!f.type.match('image.*')) {
                        continue;
                        }
                        var reader = new FileReader();
                        // Closure to capture the file information.
                        reader.onload = (function(theFile) {
                            return function(e) {
                                // Render thumbnail.
                                    $('#imageURL').attr('src',e.target.result);                         
                            };
                        })(f);
                        // Read in the image file as a data URL.
                        reader.readAsDataURL(f);
                    }

                });
            });
        }   
    });
    $( '#fileinput' ).imagePreview();
});
Rozar
fonte
1

Depois de lutar com isso sozinho, eu vim implementar FileReader para navegadores que o suportam (Chrome, Firefox e o ainda não lançado Safari 6), e um script PHP que ecoa dados de arquivo POSTed como dados codificados em Base64 para os outros navegadores.

Chris Nolet
fonte
0

Comecei a pensar que usar o 'iframe' para upload no estilo Ajax pode ser uma escolha muito melhor para a minha situação até que o HTML5 feche o círculo e eu não precise mais suportar navegadores legados em meu aplicativo!

Jordan.baucke
fonte
Só por curiosidade, por que você não pega [ webtoolkit.info/javascript-base64.html#] , e depois comenta input = Base64._utf8_encode(input);e output = Base64._utf8_decode(output);? Qualquer problema diferente de erro de conversão utf-8?
abril
@shr Não sei como obter os dados do arquivo binário para inserir nesses métodos em cada navegador? $('#inputid').files[0](não funciona no IE7, pelo que eu sei ). Se fosse assim tão fácil? var output = Base64.encode($('#inputid').files[0])??
jordan.baucke
0

Inspirado pela resposta de @ Josef:

const fileToBase64 = async (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = () => resolve(reader.result)
    reader.onerror = (e) => reject(e)
  })

const file = event.srcElement.files[0];
const imageStr = await fileToBase64(file)
mb21
fonte