Получение байтового массива через тип ввода = файл
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" />
Я использую вызов ajax, чтобы опубликовать byte[] значение типа ввода = ввод файла в web api, который получает в формате byte[]. Однако я испытываю трудности с получением байтового массива. Я ожидаю, что мы можем получить байтовый массив через File API.
Примечание: мне нужно сначала сохранить байтовый массив в переменной, прежде чем проходить через вызов ajax
7 ответов
[Редактировать]
Как отмечено в комментариях выше, хотя все еще на некоторых реализациях UA, readAsBinaryString
Метод не попал в спецификации и не должен использоваться в производстве. Вместо этого используйте readAsArrayBuffer
и пройти через это buffer
чтобы вернуть двоичную строку:
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>
Для более надежного способа преобразования вашего arrayBuffer в двоичную строку, вы можете обратиться к этому ответу.
[старый ответ] (изменено)
Да, файловый API предоставляет способ для преобразования вашего файла в <input type="file"/>
в двоичную строку, благодаря объекту FileReader и его метод readAsBinaryString
,
[ Но не используйте это в производстве! ]
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>
Если вы хотите буфер массива, то вы можете использовать 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>
В современных браузерах теперь есть arrayBuffer
метод на
Blob
s:
document.querySelector('input').addEventListener('change', async (event) => {
const buffer = await event.target.files[0].arrayBuffer()
console.log(buffer)
}, false)
🎉 🎉
$(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>
Это длинный пост, но я устал от всех этих примеров, которые не работают для меня, потому что они использовали объекты Promise или ошибочные this
это имеет другое значение, когда вы используете Reactjs. Моя реализация использовала DropZone с actjs, и я получил байты, используя фреймворк, похожий на тот, что опубликован на следующем сайте, когда ничего другого не сработало бы: https://www.mokuji.me/article/drop-upload-tutorial-1. Для меня было 2 ключа:
- Вы должны получить байты из объекта события, используя и во время функции загрузки FileReader.
Я пробовал разные комбинации, но в итоге получилось:
const bytes = e.target.result.split('base64,')[1];
куда e
это событие. Реагировать требует const
Вы могли бы использовать var
в простом Javascript. Но это дало мне байтовую строку в кодировке base64.
Так что я просто собираюсь включить соответствующие строки для интеграции этого, как если бы вы использовали React, потому что именно так я его собирал, но попробуйте также обобщить это и добавить комментарии, где это необходимо, чтобы сделать его применимым к ванильному Javascript реализация - подтолкнуло к тому, что я не использовал это в такой конструкции для тестирования.
Это будут ваши привязки вверху, в вашем конструкторе, в платформе React (не относящейся к ванильной реализации Javascript):
this.uploadFile = this.uploadFile.bind(this);
this.processFile = this.processFile.bind(this);
this.errorHandler = this.errorHandler.bind(this);
this.progressHandler = this.progressHandler.bind(this);
И вы бы onDrop={this.uploadFile}
в вашем элементе DropZone. Если вы делали это без React, это эквивалентно добавлению обработчика события onclick, который вы хотите запустить при нажатии кнопки "Загрузить файл".
<button onclick="uploadFile(event);" value="Upload File" />
Затем функция (применимые строки... Я опущу сброс индикатора прогресса загрузки и т. Д.):
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);
}
}
}
Был вопрос о том синтаксисе, для vanilla JS, о том, как получить этот файловый объект:
Обратите внимание, что DropZone от React уже поместит объект File в this.state.files
для вас, пока вы добавляете files: [],
на ваш this.state = { .... }
в вашем конструкторе. Я добавил синтаксис из ответа на этот пост о том, как получить объект File. Это должно работать, или есть другие сообщения, которые могут помочь. Но все, что Q/A сказал мне, было, как получить File
сам объект, а не данные BLOB. И даже если бы я сделал fileData = new Blob([files[0]]);
как в ответе Себу, который не включал var
по какой-то причине он не сказал мне, как читать содержимое этого BLOB-объекта и как это сделать без объекта Promise. Так вот где пришел FileReader, хотя я на самом деле пытался и обнаружил, что не могу использовать их readAsArrayBuffer
безрезультатно.
Вы должны будете иметь другие функции, которые идут вместе с этой конструкцией - одну для обработки onerror
один для onprogress
(оба показаны ниже), а затем основной, onload
, что на самом деле делает работу, когда метод на reader
вызывается в этой последней строке. В основном вы передаете свой event.dataTransfer.files[0]
прямо в это onload
функция, из того, что я могу сказать.
Итак onload
метод вызывает мой processFile()
функция (только применимые строки):
processFile(theFile) {
return function(e) {
const bytes = e.target.result.split('base64,')[1];
}
}
А также bytes
должен иметь base64 байта.
Дополнительные функции:
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 + '%';
}
}
И применимая разметка индикатора прогресса:
<table id="tblProgress">
<tbody>
<tr>
<td><b><span id="progress">000</span>%</b> <span className="progressBar"><span id="progressBar" /></span></td>
</tr>
</tbody>
</table>
И 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;
}
Эпилог:
внутри processFile()
почему-то не смог добавить bytes
к переменной, которую я вырезал в this.state
, Так что вместо этого я установил его непосредственно в переменную, attachments
это было в моем объекте JSON, RequestForm
- тот же объект, что и мой this.state
использовал. attachments
это массив, чтобы я мог нажать несколько файлов. Это пошло так:
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,
});
Тогда, потому что this.state
уже содержится RequestForm
:
this.stores = [
RequestForm,
]
Я мог бы сослаться на это как this.state.attachments
оттуда Реактивная функция, которая не применима в vanilla JS. Вы можете создать аналогичную конструкцию в обычном JavaScript с глобальной переменной и, соответственно, выполнить ее намного проще:
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;
Тогда вы всегда просто ссылка fileArray
, перечислите его для любых файловых байтовых строк, например var myBytes = fileArray[0];
для первого файла.
This is simple way to convert files to Base64 and avoid "maximum call stack size exceeded at FileReader.reader.onload" with the file has big size.
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" />
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);
console.log(arrayBuffer);
document.querySelector('#result').innerHTML = arrayBuffer + ' '+arrayBuffer.byteLength;
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
<input type="file"/>
<div id="result"></div>
Вот один ответ, чтобы получить фактический окончательный массив байтов, просто используя
FileReader
а также
ArrayBuffer
:
const test_function = async () => {
... ... ...
const get_file_array = (file) => {
return new Promise((acc, err) => {
const reader = new FileReader();
reader.onload = (event) => { acc(event.target.result) };
reader.onerror = (err) => { err(err) };
reader.readAsArrayBuffer(file);
});
}
const temp = await get_file_array(files[0])
console.log('here we finally ve the file as a ArrayBuffer : ',temp);
const fileb = new Uint8Array(fileb)
... ... ...
}
куда
file
является непосредственно
File
объект, который вы хотите прочитать, это должно быть сделано в
async
функция...