Получение массива байтов через input type = file

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

Ответ 1

[Редактировать]

Как отмечено выше в комментариях, в то время как все еще в некоторых реализациях UA, метод readAsBinaryString не readAsBinaryString в спецификации и не должен использоваться в производстве. Вместо этого используйте readAsArrayBuffer и 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>

Ответ 2

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

Ответ 3

Это длинный пост, но я устал от всех этих примеров, которые не работали для меня, потому что они использовали объекты Promise или странствующий this, что имеет другое значение, когда вы используете Reactjs. Моя реализация использовала DropZone с responsejs, и я получил байты, используя фреймворк, подобный тому, что размещено на этом следующем сайте, когда ничего больше не будет работать: https://www.mokuji.me/article/drop-upload- учебник-1. Для меня было 2 ключа:

  1. Вы должны получить байты от объекта события, используя и во время функции onload FileReader.
  2. Я пробовал различные комбинации, но в итоге работала:

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

Где e - событие. React требует const, вы можете использовать var в простом Javascript. Но это дало мне байтовую строку с кодировкой base64.

Поэтому я просто собираюсь включить применимые строки для интеграции этого, как если бы вы использовали React, потому что это то, как я его создавал, но попробуйте также обобщить это и добавить комментарии там, где это необходимо, чтобы применить его к ванильному Javascript реализация - предостерег, что я не использовал его так, как в такой конструкции, чтобы проверить его.

Это будут ваши привязки в верхней части, в вашем конструкторе, в структуре React (не относящейся к реализации JavaScript 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 о том, как получить этот файловый объект:

JavaScript/HTML5/jQuery Drag-And-Drop Upload - "Uncaught TypeError: Невозможно прочитать файлы свойств" неопределенного "

Обратите внимание, что React DropZone уже помещает объект File в файл this.state.files для вас, если вы добавляете files: [], в ваш this.state = {.... } в своем конструкторе. Я добавил синтаксис ответа на этот пост о том, как ваш объект File. Он должен работать, или есть другие сообщения, которые могут помочь. Но все, что Q/A сказал мне, это как получить объект File, а не данные blob. И даже если я сделал fileData = new Blob([files[0]]); как в ответе sebu, который по какой-то причине не включал 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 в переменную I, вырезанную в 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 оттуда на. Функция React, которая не применима в 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]; для первого файла.