Загрузите несколько локальных файлов в React и перейдите на сервер Flask без загрузки в каталог flask - PullRequest
0 голосов
/ 27 марта 2020

Я работаю над проектом по созданию приложения React для загрузки локально сохраненных обучающих и тестовых файлов из React для обслуживания в Flask с целью обучения моделей машинного обучения. Я хочу иметь возможность использовать файлы напрямую, не сохраняя файлы локально (т.е. создавая копию файлов в каталоге flask). Основная причина в том, что я хочу работать с большими файлами, такими как MovieLens 25M DataSet [https://grouplens.org/datasets/movielens/25m/] , который содержит файлы размером ~ 650 МБ, которые без необходимости загружать в другое место .

Самое простое решение - отправить абсолютный путь к этим файлам, однако это невозможно, потому что современные браузеры предотвращают это как функцию безопасности. Следовательно, вместо этого я попробовал:

  1. Чтение в виде двоичной строки и JSON stringify для передачи на сервер:
import React from "react";
import axios from "axios";

class App extends React.Component {
constructor(props){
 super(props);
 this.state = {
   itemType: '',
   filePaths: [],
   fileStreams: [],
 };
 this.buttonChangeFiles = this.buttonChange.bind(this, "filePaths", "fileStreams");
 this.file_upload = React.createRef();
}

buttonChange(keyName_1, keyName_2){
 var paths = [];
 var streams = [];

 function readNewFile(new_file) {
   if(/\.json$/gi.test(new_file.name)){ //Tests for JSON files 
     var reader = new FileReader();
     reader.onload = function(e){
       var read_output = e.target.result;
       streams.push(read_output);
     }
     reader.readAsBinaryString(new_file);
     paths.push(new_file.name);
   }
   else{
     try{ //Read supported Excel file formats 
       var reader = new FileReader();
       reader.onload = function(e){
         var read_output = event.target.result;
         console.log("Read_Excel_Output: " + read_output);
         var read_xlsx = XLSX.read(read_output, {type: "binary"});
         var json_output = [];

         read_xlsx.SheetNames.forEach(function(sheet_name){
           var row_object = XLSX.utils.sheet_to_json(read_xlsx.Sheets[sheet_name], {header: 0, raw: true, blankrows: false});
           var new_robj = [];
           if(row_object.length){
             Object.keys(row_object).forEach(function (row) {
             new_robj = new_robj.concat(row_object[row]);
             });
             json_output = json_output.concat(new_robj);
           }
         });
         json_output = JSON.stringify(json_output, 2, 2)
         streams.push(json_output);
         paths.push(new_file.name);
       }
       reader.readAsBinaryString(new_file);
     }
     catch{
       alert("This is not a supported file type!");
     }
   }
 }

 if(this.file_upload.current.files.length >= 1){
   for (const new_file of this.file_upload.current.files){
     readNewFile(new_file);
   };
 }
 else{
   alert("There are no files selected!");
 }

 this.setState({[keyName_1]: paths});
 this.setState({[keyName_2]: streams});
}

fetchFormData(event){
 const formData = {
   itemType: this.state.itemType,
   filePaths: this.state.filePaths,
   fileStreams: this.state.fileStreams,
 };
 event.preventDefault();

 axios.post("http://localhost:5000/", formData).then(
   response => {return response}
 );
}

render () {
return (
 <div className="NewApp">
     <form onSubmit={this.fetchFormData}>
       <div>
         <label style={{fontSize: "0.9375rem", fontWeight: "bold"}}>Files</label>
         <div>
           <input id="file-upload" ref={this.file_upload} type="file" onChange={this.buttonChangeFiles} multiple/>
         </div>
       </div>
       <button id='submit-button' type='submit'/>
     </form>
 </div>
);
}
};

export default App;

Это полезно для загрузки небольшие файлы (~ 5 МБ или меньше), но они не могут обрабатывать чтение больших файлов. Это приводит к тому, что браузер обрабатывает sh, поскольку ему требуется место в памяти для хранения файла. Кроме того, это не выглядит элегантным решением.

Используйте тег webkitdirectory, чтобы вместо этого передать относительный путь к каталогу, содержащему файлы:

Код реагирования:

import React from "react";
import axios from "axios";

class App extends React.Component {
constructor(props){
 super(props);
 this.state = {
   itemType: '',
   filePaths: [],
 };
 this.fileOpen = this.fileOpen.bind(this);
 this.file_upload = React.createRef();
}

fileOpen(event){
 var paths = [];

 let files = this.file_upload.current.files;

 for (let i = 0; i < files.length; i++) {
   let file = files[i];
   paths.push(file.webkitRelativePath);
 }

 this.setState({"filePaths": paths});
}

fetchFormData(event){
 const formData = {
   itemType: this.state.itemType,
   filePaths: this.state.filePaths,
 };
 event.preventDefault();

 axios.post("http://localhost:5000/", formData).then(
   response => {return response}
 );
}

render () {
return (
 <div className="NewApp">
     <form onSubmit={this.fetchFormData}>
       <div>
         <label style={{fontSize: "0.9375rem", fontWeight: "bold"}}>Files</label>
         <div>
           <input id="file-upload" ref={this.file_upload} type="file" webkitdirectory="true" multiple onChange={this.fileOpen}/>
         </div>
       </div>
       <button id='submit-button' type='submit'/>
     </form>
 </div>
);
}
};

export default App;

Flask код:

py_path = Path(sys.executable)
root = py_path.root

@app.route("/", methods=["POST"])
def info():
    response = request.json
    file_search = [file_path.split(os.sep) for file_path in response["filePaths"]]
    read_files = [os.sep.join([search_path, new_file]) for search_path, _, files in os.walk(root) for name in files for directory, new_file  if name == new_file and root.endswith(directory)]

Это также неэффективное решение, поскольку оно требует поиска из папки root. Обойти это можно было бы путем жесткого кодирования пути к каталогу, из которого будут считываться все файлы. Тем не менее, я хотел бы знать, можно ли читать из любого места.

Сводка: Чтение набора файлов из React и передача в Flask бэкэнд без либо локального сохранения файлов в каталоге flask или жесткое кодирование папки каталога загрузки в бэкэнде Flask.

...