2017-09-21 84 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

它是異步的,不阻止主線程。這可能會導致頁面無法響應,具體取決於文件大小。所以它不允許在頁面中。但是(FileReaderSync)[https://developer.mozilla.org/en-US/docs/Web/API/FileReaderSync]存在,可以在'workers'中訪問。 – Panther

+0

如果已經讀取了所有文件,您可以在'onLoad'回調和'set previews'中添加一個支票。 – Panther

+0

如何在'onLoad'回調中瞭解是否讀取了所有文件。我沒有辦法將'i'或'e.target.files.length'傳遞給'onLoad'回調函數。即使我找到了通過索引和總長的方法,我也無法可靠地知道所有文件都被讀取了。讀取的第5個文件可能會在讀取第3個文件之前完成,或者還有其他方法嗎? –

回答

0

您需要將setState移到onLoad事件中。由於文件閱讀器是異步的,因此在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

這不是線程不安全嗎?兩個不同的線程可以同時將預覽設置爲錯誤值? –

+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在瀏覽器中是單線程的時候纔會起作用)。 –