2017-09-21 13 views
0

次のファイルセレクタダイアログがあります。私は複数のファイルの選択を許可したい。ファイルが選択されたら、選択したファイルのプレビューをレンダリングします。私のコードは次のとおりです。ただしJavascriptで読み込んだSynchronosファイル

<input 
      type="file" 
      multiple 
      accept=".png, .jpeg" 

      onChange={e => { 
      this.setState({ previews: [] }); 

      if (!e.target.files) { 
       return; 
      } 

      var previews = []; 

      for (var i = 0; i < e.target.files.length; i++) { 
       var file = e.target.files[i]; 
       (function(file, previews) { 
       var reader = new FileReader(); 

       reader.onload = function(e) { 
        // console.log(e.target.result); 
        console.log("Just pushed a new preview"); 
        previews.push(e.target.result); 
       }; 
       reader.readAsDataURL(file); 
       })(file, previews); 
      } 

      console.log("All files read"); 
      // I want this to be called only after 
      // all the file contents are read 
      this.setState({ previews }); 
      }} 
     /> 

、上記のコードで、All files readがコンソールに印刷された後、私のJust pushed a new previewが印刷されます。

FileReaderループ(クロージャ)を終了してからthis.setState({previews})を呼び出す方法はありますか?

すべてのコードは重要な場合は反応ファイルにあります。

+0

メインスレッドをブロックしないように非同期です。これは、ファイルサイズに応じて応答しないページにつながる可能性があります。したがって、そのページでは許可されていません。しかし、(ワイルドカードリーダー)[https://developer.mozilla.org/en-US/docs/Web/API/FileReaderSync]は、 'ワーカー 'でアクセス可能です。 – Panther

+0

すべてのファイルが読み込まれている場合、 'onLoad'コールバックと' set previews'にチェックを追加できます。 – Panther

+0

'onLoad'コールバックで、すべてのファイルが読み込まれたかどうかを知る方法を教えてください。私は 'on'または' e.target.files.length'を 'onLoad'コールバックに渡す方法がありません。インデックスと全長を渡す方法が見つかったとしても、すべてのファイルが読み込まれていることを確実に知ることはできません。 5番目のファイルの読み込みは、3番目のファイルの読み込みなどの前に終了することがあります。 –

答えて

0

onLoadイベント内でsetStateを移動する必要があります。ファイルリーダーは非同期なので、onLoad以外の状態を設定するとイベントが完了する前にトリガーされ、空または不完全なpreviews配列になります。

var previews = []; 
var s = this; 

for (var i = 0; i < e.target.files.length; i++) { 
    var file = e.target.files[i]; 
     var reader = new FileReader(); 

     reader.onload = function(e) { 
     previews.push(e.target.result); 
     s.setState({ previews }); 
     }; 

     reader.readAsDataURL(file); 
} 
+0

スレッドセーフではありませんか? 2つの異なるスレッドでプレビューを並行して悪い値に設定できますか? –

+0

@SankarPいいえ、メソッド内のローカルプレビュー配列が追加されていて、そのコピーが状態で維持されているからです。さらに、スレッド処理は、jsで作業する場合にはあまり問題になりません。 – Fawaz

+0

@SankarP fyr:https://stackoverflow.com/questions/12163175/push-to-array-safely-how-to-worry-or-not – Fawaz

1

Promise.allを使用して、すべてのファイルが処理されるかどうかを知るためにすべての約束を待ってください。また、ファイルを読み終えたらすぐに状態を更新する必要があります。それを更新するための進捗バーとsetStateを追加する方がいいです。

var Sample = React.createClass({ 
    getInitialState: function() { return { previews: [], allfilesdone:false }; }, 
    render: function() { 
    var p = this.state.previews.map((p) => <div>{p}</div>); 
    return (
     <div style = {this.styler}> 
     <input id="upload" multiple 
      accept=".txt" type="file" 
      onClick={(e) => this.setState({allfilesdone: false})} 
      onChange={(event)=> { 
       var s = this; 
       this.setState({previews:[], allfilesdone: false}); 
       var fs = event.target.files; 
       Promise.all([...fs].map((file) => { 
        var reader = new FileReader(); 
        return new Promise((resolve,reject) => { 
        reader.onload = (ev) => { 
         resolve(ev.target.result); 
         var pt = this.state.previews; 
         pt.push(ev.target.result); 
         s.setState({previews: pt}); 
        } 
        reader.readAsText(file); 
        }); 
       })) 
        .then(ft => { 
         this.setState({allfilesdone: true}); 
         }); 
      }}/> 
     <div>all processed: {this.state.allfilesdone?"true":"false"}</div> 
     <div>number of files: {this.state.previews.length}</div> 
     <div>{p}</div> 
     </div> 
    ); 
    } 
}); 

React.render(
    <Sample />, 
    document.getElementById('mount-point')); 
+0

私は概念を示すためにテキストファイルを使用しました。あなたは画像のためにそれを変えることができます。 readAsTextを必要なものに変更してください。 –

+0

ありがとうございます。これは私がやろうとしたことです。しかし、私は解決を見逃していたので、私は約束を得ることができなかった、コールを拒否します。ありがとう。これは、受け入れられた回答よりもスレッドセーフなアプローチです(JSがブラウザでシングルスレッドの場合に限り有効です)。 –

関連する問題