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