Obtenga datos de archivo codificados en Base64 desde el formulario de entrada

100

Tengo un formulario HTML básico del que puedo obtener un poco de información que estoy examinando en Firebug.

Mi único problema es que estoy tratando de codificar en base64 los datos del archivo antes de enviarlos al servidor donde se requiere que esté en ese formulario para guardarlos en la base de datos.

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

Y en Javascript + jQuery:

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

Tengo algunas operaciones basadas en javascript disponible: .getAsBinary (), .getAsText (), .getAsTextURL

Sin embargo, ninguno de estos devuelve texto utilizable que se pueda insertar ya que contienen 'caracteres' inutilizables ; no quiero que se produzca una 'devolución de datos' en mi archivo cargado, y necesito tener varios formularios dirigidos a objetos específicos, por lo que es importante que obtenga el archivo y use Javascript de esta manera.

¿Cómo debo obtener el archivo de tal manera que pueda usar uno de los codificadores Javascript base64 que están ampliamente disponibles?

Gracias

Actualización: comenzando con la recompensa aquí, ¡se necesita compatibilidad con varios navegadores!

Aquí es donde estoy:

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

Un par de problemas con la compatibilidad con varios navegadores:

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

Además, IE no admite:

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

Entonces tengo que reemplazar con:

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

Para soporte de IE.

Esto funciona en Firefox, Chrome y Safari (pero no codifica correctamente el archivo, o al menos después de que se ha publicado, el archivo no sale correctamente)

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

También,

file.readAsArrayBuffer() 

¿Parece que solo es compatible con HTML5?

Mucha gente sugirió: http://www.webtoolkit.info/javascript-base64.html

¿Pero esto solo devuelve un error en el método UTF_8 antes de que se codifique en base64? (o una cadena vacía)

var encoded = Base64.encode(file); 
jordan.baucke
fuente
¿Qué estás subiendo? ¿Datos de texto o binarios?
beatgammit
@tjamenson datos binarios: cualquier cosa realmente 'documento de Word' 'pdf' 'imagen', etc. Iba a asignar el nombre de archivo en otra variable, pero comencé a preguntarme si tenía algún error fatal en mi comprensión de lo que es posible con cargar formularios y html: ¿este método es totalmente imposible de realizar y no es posible transmitir datos de archivos mediante su publicación como una cadena? También sin HTML5 que estoy leyendo tiene algunas soluciones -
jordan.baucke
Interesante. ¿Por qué está codificando en base 64 entre el navegador y el servidor, y tratando de lograr esto en el navegador? Parece que si intenta proteger los datos, SSL sería mucho más fácil ya que encripta todos los datos HTTP (incluidos los archivos) a través del cable., Y podría basarse en el lado del servidor 64, si es necesario para su base de datos.
William Walseth
@William No estoy tratando de proteger los datos, solo consígalos en el formato correcto. Salesforce.com (plataforma Apex) requiere que la base de datos esté codificada en base64 antes de que se cree el registro en la base de datos, su funcionalidad para codificar datos de archivo en base64 a través de un formulario está muy mal documentada (descubrí cómo hacer esto después de hacer esta pregunta). De todos modos, parece que los datos de archivos binarios con codificación base64 solo se admiten de forma nativa en HTML5 o Mozilla File API
jordan.baucke
1
Bien, ahora veo por qué necesitas esto. Sin servidor intermedio + requisitos de nube loca + sin solución de navegador cruzado = 1 desorden. Lo siento.
William Walseth

Respuestas:

121

Es completamente posible en javascript del lado del navegador.

La manera fácil:

Es posible que el método readAsDataURL () ya lo codifique como base64 para usted. Probablemente necesitará eliminar las cosas iniciales (hasta la primera), pero eso no es gran cosa. Sin embargo, esto quitaría toda la diversión.

El camino difícil:

Si quieres probarlo de la manera más difícil (o no funciona), mira readAsArrayBuffer(). Esto le dará un Uint8Array y podrá usar el método especificado. Esto probablemente solo sea útil si desea manipular los datos en sí, como manipular datos de imágenes o hacer otra magia vudú antes de cargar.

Hay dos métodos:

  • Convierta a cadena y use el incorporado btoao similar
    • No he probado todos los casos, pero funciona para mí, solo obtenga los códigos de caracteres
  • Convierta directamente de un Uint8Array a base64

Recientemente implementé tar en el navegador . Como parte de ese proceso, hice mi propia implementación directa de Uint8Array-> base64. No creo que lo necesites, pero está aquí si quieres echarle un vistazo; es bastante ordenado.

Qué hago ahora:

El código para convertir a una cadena desde un Uint8Array es bastante simple (donde buf es un 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 de ahí, simplemente haz:

var base64 = btoa(uint8ToString(yourUint8Array));

Base64 ahora será una cadena codificada en base64, y debería cargarse como un peachy. Pruebe esto si desea verificar dos veces antes de presionar:

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

Esto lo descargará como un archivo.

Otra información:

Para obtener los datos como un Uint8Array, consulte los documentos de MDN:

beatgammit
fuente
genial: pensé en readAsArrayBuffer()generar lo que parecían datos base64 correctos , pero no se procesaba debido a la parte inicial de la cadena.¡Lo intentaré ahora!
jordan.baucke
1
readAsArrayBuffer () no parece existir en Chrome / Safari y supongo que será problemático en otros navegadores ~ ¿hay un equivalente que pueda usar en estos otros navegadores?
jordan.baucke
createObjectURL () <- esto parece prometedor Intentaré implementarlo, pero estoy seguro de que IE va a necesitar una tercera rueda
jordan.baucke
¿Es posible asignar un nombre de archivo antes de ejecutar su solución de descarga?
Ωmega
@ Ωmega - No. Todo lo que esto hace es descargar un flujo binario. No hay forma (AFAIK) de especificar un nombre de archivo. Sin embargo, algunos navegadores adivinan una extensión. Para obtener los nombres de archivo, tendrá que cargar y luego descargar nuevamente.
beatgammit
37

Mi solución fue el uso readAsBinaryString()y btoa()el 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
fuente
3
De lejos, la forma más fácil, ¿por qué no tiene más votos?
sarink
14

Usé FileReader para mostrar la imagen al hacer clic en el botón de carga de archivos sin usar ninguna solicitud Ajax. A continuación se muestra el código, espero que pueda ayudar a alguien.

$(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
fuente
1

Después de luchar con esto yo mismo, llegué a implementar FileReader para navegadores que lo admitan (Chrome, Firefox y el aún inédito Safari 6), y un script PHP que hace eco de los datos del archivo POST como datos codificados en Base64 para el otro navegadores.

Chris Nolet
fuente
0

Comencé a pensar que usar el 'iframe' para la carga de estilo Ajax podría ser una opción mucho mejor para mi situación hasta que HTML5 complete el círculo y no tenga que admitir navegadores heredados en mi aplicación.

jordan.baucke
fuente
Solo por curiosidad, ¿por qué no toma [ webtoolkit.info/javascript-base64.html#] , y luego comenta input = Base64._utf8_encode(input);y output = Base64._utf8_decode(output);? ¿Algún problema que no sea el error de conversión utf-8?
Shr
@shr ¿No sé cómo tomar los datos del archivo binario para ingresarlos en estos métodos en cada navegador? $('#inputid').files[0](no funciona en IE7, por lo que puedo decir ). ¿Si fuera tan fácil? var output = Base64.encode($('#inputid').files[0])??
jordan.baucke
0

Inspirado por la respuesta 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
fuente