type convertir convert bytes arreglo array archivo javascript html html5 fileapi

javascript - convertir - input file to byte



Obtener una matriz de bytes a través de input type=file (4)

var profileImage = fileInputInByteArray; $.ajax({ url: ''abc.com/'', type: ''POST'', dataType: ''json'', data: { // Other data ProfileImage: profileimage // Other data }, success: { } }) // Code in WebAPI [HttpPost] public HttpResponseMessage UpdateProfile([FromUri]UpdateProfileModel response) { //... return response; } public class UpdateProfileModel { // ... public byte[] ProfileImage {get ;set; } // ... }

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

Estoy usando la llamada ajax para publicar el valor de byte [] de un tipo de entrada = entrada de archivo a la API web que recibe en formato byte []. Sin embargo, estoy experimentando dificultades para obtener una matriz de bytes. Espero que podamos obtener la matriz de bytes a través de File API.

Nota: primero necesito almacenar la matriz de bytes en una variable antes de pasar por la llamada ajax


[Editar]

Como se señaló en los comentarios anteriores, aunque todavía se encuentra en algunas implementaciones de UA, el método readAsBinaryString no llegó a las especificaciones y no debe usarse en la producción. En su lugar, use readAsArrayBuffer y readAsArrayBuffer su buffer para recuperar la cadena binaria:

document.querySelector(''input'').addEventListener(''change'', function() { var reader = new FileReader(); reader.onload = function() { var arrayBuffer = this.result, array = new Uint8Array(arrayBuffer), binaryString = String.fromCharCode.apply(null, array); console.log(binaryString); } reader.readAsArrayBuffer(this.files[0]); }, false);

<input type="file" /> <div id="result"></div>

Para una forma más robusta de convertir su arrayBuffer en cadena binaria, puede consultar esta respuesta .

[respuesta anterior] (modificado)

Sí, la API de archivo proporciona una forma de convertir su archivo, en el <input type="file"/> a una cadena binaria, gracias al objeto FileReader y su método readAsBinaryString .
Pero no lo uses en producción! ]

document.querySelector(''input'').addEventListener(''change'', function(){ var reader = new FileReader(); reader.onload = function(){ var binaryString = this.result; document.querySelector(''#result'').innerHTML = binaryString; } reader.readAsBinaryString(this.files[0]); }, false);

<input type="file"/> <div id="result"></div>

Si desea un búfer de matriz, puede usar el método readAsArrayBuffer() :

document.querySelector(''input'').addEventListener(''change'', function(){ var reader = new FileReader(); reader.onload = function(){ var arrayBuffer = this.result; console.log(arrayBuffer); document.querySelector(''#result'').innerHTML = arrayBuffer + '' ''+arrayBuffer.byteLength; } reader.readAsArrayBuffer(this.files[0]); }, false);

<input type="file"/> <div id="result"></div>


Esta es una manera simple de convertir archivos a Base64 y evitar que el "tamaño máximo de la pila de llamadas excedido en FileReader.reader.onload" con el archivo tenga un tamaño grande.

document.querySelector(''#fileInput'').addEventListener(''change'', function () { var reader = new FileReader(); var selectedFile = this.files[0]; reader.onload = function () { var comma = this.result.indexOf('',''); var base64 = this.result.substr(comma + 1); console.log(base64); } reader.readAsDataURL(selectedFile); }, false);

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


Esta es una publicación larga, pero estaba cansado de todos estos ejemplos que no funcionaban para mí porque usaban objetos Promise o un error que tenía un significado diferente cuando usas Reactjs. Mi implementación estaba usando un DropZone con reactjs, y obtuve los bytes usando un marco similar a lo que se publica en el siguiente sitio, cuando nada más funcionaría: https://www.mokuji.me/article/drop-upload-tutorial-1 . Había 2 llaves, para mí:

  1. Debe obtener los bytes del objeto de evento, utilizando y durante la función de carga de FileReader.
  2. Intenté varias combinaciones, pero al final, lo que funcionó fue:

    const bytes = e.target.result.split(''base64,'')[1];

Donde e es el evento. React requiere const , puedes usar var en Javascript simple. Pero eso me dio la cadena de bytes codificada en base64.

Así que solo voy a incluir las líneas aplicables para integrar esto como si estuvieras usando React, porque así es como lo estaba construyendo, pero intenta también generalizar esto y agregar comentarios cuando sea necesario, para que sea aplicable a un Javascript de vainilla implementación: advertí que no lo usé así en tal construcción para probarlo.

Estos serían sus enlaces en la parte superior, en su constructor, en un marco React (no relevante para una implementación de Javascript de vainilla):

this.uploadFile = this.uploadFile.bind(this); this.processFile = this.processFile.bind(this); this.errorHandler = this.errorHandler.bind(this); this.progressHandler = this.progressHandler.bind(this);

Y tendría onDrop={this.uploadFile} en su elemento DropZone. Si estaba haciendo esto sin React, esto es equivalente a agregar el controlador de eventos onclick que desea ejecutar cuando hace clic en el botón "Cargar archivo".

<button onclick="uploadFile(event);" value="Upload File" />

Luego la función (líneas aplicables ... Dejaré de lado mi reinicio de mi indicador de progreso de carga, etc.):

uploadFile(event){ // This is for React, only this.setState({ files: event, }); console.log(''File count: '' + this.state.files.length); // You might check that the "event" has a file & assign it like this // in vanilla Javascript: // var files = event.target.files; // if (!files && files.length > 0) // files = (event.dataTransfer ? event.dataTransfer.files : // event.originalEvent.dataTransfer.files); // You cannot use "files" as a variable in React, however: const in_files = this.state.files; // iterate, if files length > 0 if (in_files.length > 0) { for (let i = 0; i < in_files.length; i++) { // use this, instead, for vanilla JS: // for (var i = 0; i < files.length; i++) { const a = i + 1; console.log(''in loop, pass: '' + a); const f = in_files[i]; // or just files[i] in vanilla JS const reader = new FileReader(); reader.onerror = this.errorHandler; reader.onprogress = this.progressHandler; reader.onload = this.processFile(f); reader.readAsDataURL(f); } } }

Hubo esta pregunta sobre esa sintaxis, para vanilla JS, sobre cómo obtener ese objeto de archivo:

JavaScript / HTML5 / jQuery Arrastrar y soltar Subir - "Error de tipo no capturado: no se pueden leer los ''archivos'' de propiedad de indefinido"

Tenga en cuenta que DropZone de React ya colocará el objeto File en this.state.files por usted, siempre que agregue files: [], a su this.state = { .... } en su constructor. Agregué la sintaxis de una respuesta en esa publicación sobre cómo obtener su objeto File. Debería funcionar, o hay otras publicaciones allí que pueden ayudar. Pero todo lo que Q / A me dijo fue cómo obtener el objeto File , no los datos del blob, en sí. E incluso si hice fileData = new Blob([files[0]]); como en la respuesta de sebu, que no incluía var con ella por alguna razón, no me dijo cómo leer el contenido de ese blob y cómo hacerlo sin un objeto Promise. Así que ahí es donde entró FileReader, aunque en realidad lo intenté y descubrí que no podía usar su readAsArrayBuffer en vano.

Tendrá que tener las otras funciones que acompañan a esta construcción: una para manejar onerror , otra para onprogress (ambas se muestran más adelante) y luego la principal, onload , que realmente hace el trabajo una vez que se invoca un método en el reader En esa última línea. Básicamente, está pasando su event.dataTransfer.files[0] directamente a esa función de onload , por lo que puedo decir.

Entonces, el método onload llama a mi función processFile() (solo líneas aplicables):

processFile(theFile) { return function(e) { const bytes = e.target.result.split(''base64,'')[1]; } }

Y los bytes deben tener los bytes base64.

Funciones adicionales:

errorHandler(e){ switch (e.target.error.code) { case e.target.error.NOT_FOUND_ERR: alert(''File not found.''); break; case e.target.error.NOT_READABLE_ERR: alert(''File is not readable.''); break; case e.target.error.ABORT_ERR: break; // no operation default: alert(''An error occurred reading this file.''); break; } } progressHandler(e) { if (e.lengthComputable){ const loaded = Math.round((e.loaded / e.total) * 100); let zeros = ''''; // Percent loaded in string if (loaded >= 0 && loaded < 10) { zeros = ''00''; } else if (loaded < 100) { zeros = ''0''; } // Display progress in 3-digits and increase bar length document.getElementById("progress").textContent = zeros + loaded.toString(); document.getElementById("progressBar").style.width = loaded + ''%''; } }

Y marcado de indicador de progreso aplicable:

<table id="tblProgress"> <tbody> <tr> <td><b><span id="progress">000</span>%</b> <span className="progressBar"><span id="progressBar" /></span></td> </tr> </tbody> </table>

Y CSS:

.progressBar { background-color: rgba(255, 255, 255, .1); width: 100%; height: 26px; } #progressBar { background-color: rgba(87, 184, 208, .5); content: ''''; width: 0; height: 26px; }

EPÍLOGO:

Dentro de processFile() , por alguna razón, no pude agregar bytes a una variable tallada en this.state . Entonces, en cambio, lo configuré directamente en la variable, attachments , que estaba en mi objeto JSON, RequestForm , el mismo objeto que estaba usando this.state . attachments son una matriz, por lo que podría insertar varios archivos. Fue así:

const fileArray = []; // Collect any existing attachments if (RequestForm.state.attachments.length > 0) { for (let i=0; i < RequestForm.state.attachments.length; i++) { fileArray.push(RequestForm.state.attachments[i]); } } // Add the new one to this.state fileArray.push(bytes); // Update the state RequestForm.setState({ attachments: fileArray, });

Entonces, porque this.state ya contenía RequestForm :

this.stores = [ RequestForm, ]

Podría hacer referencia a esto como this.state.attachments de this.state.attachments partir de ahí. Función de reacción que no es aplicable en vanilla JS. Podría construir una construcción similar en JavaScript sin formato con una variable global y, en consecuencia, presionar mucho más fácilmente:

var fileArray = new Array(); // place at the top, before any functions // Within your processFile(): var newFileArray = []; if (fileArray.length > 0) { for (var i=0; i < fileArray.length; i++) { newFileArray.push(fileArray[i]); } } // Add the new one newFileArray.push(bytes); // Now update the global variable fileArray = newFileArray;

Luego, siempre debe hacer referencia a fileArray , enumerarlo para cualquier cadena de bytes de archivo, por ejemplo, var myBytes = fileArray[0]; para el primer archivo


$(document).ready(function(){ (function (document) { var input = document.getElementById("files"), output = document.getElementById("result"), fileData; // We need fileData to be visible to getBuffer. // Eventhandler for file input. function openfile(evt) { var files = input.files; // Pass the file to the blob, not the input[0]. fileData = new Blob([files[0]]); // Pass getBuffer to promise. var promise = new Promise(getBuffer); // Wait for promise to be resolved, or log error. promise.then(function(data) { // Here you can pass the bytes to another function. output.innerHTML = data.toString(); console.log(data); }).catch(function(err) { console.log(''Error: '',err); }); } /* Create a function which will be passed to the promise and resolve it when FileReader has finished loading the file. */ function getBuffer(resolve) { var reader = new FileReader(); reader.readAsArrayBuffer(fileData); reader.onload = function() { var arrayBuffer = reader.result var bytes = new Uint8Array(arrayBuffer); resolve(bytes); } } // Eventlistener for file input. input.addEventListener(''change'', openfile, false); }(document)); });

<!DOCTYPE html> <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> </head> <body> <input type="file" id="files"/> <div id="result"></div> </body> </html>