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í:
- Debe obtener los bytes del objeto de evento, utilizando y durante la función de carga de FileReader.
-
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:
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>