web-dev-qa-db-ja.com

入力タイプ=ファイルからバイト配列を取得する

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 []値を、byte []形式で受け取るWeb APIへのファイル入力を投稿します。ただし、バイト配列を取得するのは困難です。 File APIを介してバイト配列を取得できると期待しています。

注:ajax呼び出しを通過する前に、まず変数にバイト配列を格納する必要があります

20
stacknist

[編集]

上記のコメントで述べたように、一部の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をバイナリ文字列に変換するより堅牢な方法については、 this answer を参照してください。


[古い答え] (変更)

はい。ファイルAPIは、 FileReader オブジェクトとそのメソッド readAsBinaryStringのおかげで、<input type="file"/>内のファイルをバイナリ文字列に変換する方法を提供します
[ただし、本番環境では使用しないでください!]

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>
39
Kaiido
$(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>
5
sebu

これは長い投稿ですが、Reactjsを使用しているときに異なる意味を持つPromiseオブジェクトまたは誤ったthisを使用したため、私にとって役に立たなかったこれらすべての例にうんざりしていました。私の実装はreactjsでDropZoneを使用しており、上記のほかに何も機能しない場合、この次のサイトに投稿されているものと同様のフレームワークを使用してバイトを得ました: https://www.mokuji.me/article/ drop-upload-tutorial-1 。私には2つの鍵がありました:

  1. FileReaderのonload関数を使用して、その最中に、イベントオブジェクトからバイトを取得する必要があります。
  2. さまざまな組み合わせを試しましたが、最終的には何が効果的でしたか:

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

ここで、eはイベントです。 Reactにはconstが必要です。プレーンJavaScriptでvarを使用できます。しかし、それはbase64でエンコードされたバイト文字列を私に与えました。

だから、Reactを使用しているかのようにこれを統合するための適切な行を含めるつもりです、それが私がそれを構築していた方法だからです、しかしこれを一般化し、必要に応じてコメントを追加して、Vanilla Javascriptに適用できるようにしてください実装-私はそれをテストするためにそのような構造でそのように使用しなかったことを警告しました。

これらは、コンストラクターのReactフレームワーク(Vanilla 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);

そして、DropZone要素にonDrop={this.uploadFile}が含まれます。 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ドラッグアンドドロップアップロード-"不明なTypeError:未定義のプロパティ 'files'を読み取れません"

コンストラクタでthis.state.filesfiles: [],を追加する限り、ReactのDropZoneは既にthis.state = { .... }にFileオブジェクトを配置することに注意してください。 Fileオブジェクトの取得方法に関するその投稿の回答から構文を追加しました。動作するはずです。または、役立つ他の投稿があります。しかし、Q/Aが私に言ったことは、blobデータ自体ではなく、Fileオブジェクトを取得する方法だけでした。また、何らかの理由でvarが含まれていないsebuの回答のようにfileData = new Blob([files[0]]);を実行した場合でも、そのblobのコンテンツの読み方、Promiseオブジェクトなしで実行する方法は教えてくれませんでした。そこでFileReaderが登場しましたが、実際に試してみて、readAsArrayBufferを使用できないことがわかりました。

このコンストラクトに沿って機能する他の関数が必要です。1つはonerrorを処理し、1つは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()の内部では、何らかの理由で、this.stateで作成した変数にbytesを追加できませんでした。そのため、代わりに、JSONオブジェクトattachments-this.stateが使用していたオブジェクトと同じ変数RequestFormに直接設定しました。 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];最初のファイル。

3
vapcguy

これは、ファイルをBase64に変換し、「FileReader.reader.onloadで最大呼び出しスタックサイズを超える」ことを回避する簡単な方法です。ファイルのサイズが大きくなります。

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