实际上,我已经在reactJs中制作了前端,在python Flask中制作了后端(使用cnn模型进行一些预测)。当我按照我的请求发送5到6张图像时,它工作正常,但是当我发送10到15张图像和一些时间时令人筋疲力尽,然后它给出了类似的错误,尽管我在下面给出的代码中设置了Cors:
192.168.151.24/:1 Access to XMLHttpRequest at 'http://192.168.151.24:2222/upload-images' from origin 'http://192.168.151.24:1111' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.Understand this error
App.js:177 Error uploading files: po {message: 'Network Error', name: 'AxiosError', code: 'ERR_NETWORK', config: {…}, request: XMLHttpRequest, …}code: "ERR_NETWORK"config: {transitional: {…}, adapter: Array(3), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}message: "Network Error"name: "AxiosError"request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}stack: "AxiosError: Network Error\n at u.onerror (http://192.168.151.24:1111/static/js/main.e4ed8eb8.js:2:404852)\n at La.request (http://192.168.151.24:1111/static/js/main.e4ed8eb8.js:2:412324)\n at async http://192.168.151.24:1111/static/js/main.e4ed8eb8.js:2:441003"[[Prototype]]: Error
overrideMethod @ console.js:288
(anonymous) @ App.js:177
Show 1 more frame
Show lessUnderstand this error
:2222/upload-images:1
Failed to load resource: net::ERR_FAILED
我的反应代码:
import React, { useEffect, useRef, useState } from 'react';
import { PrimeReactProvider } from 'primereact/api';
import { Toast } from 'primereact/toast';
import { FileUpload } from 'primereact/fileupload';
import { ProgressBar } from 'primereact/progressbar';
import { Button } from 'primereact/button';
import { Tooltip } from 'primereact/tooltip';
import { Tag } from 'primereact/tag';
import { InputSwitch } from 'primereact/inputswitch';
import './App.scss';
import axios from 'axios';
import Box from '@mui/material/Box';
import LinearProgress from '@mui/material/LinearProgress';
import { getCurrentTimestamp, mapRequest } from './UtilityFunctions/pathFunctions';
function App() {
const toast = useRef(null);
const [totalSize, setTotalSize] = useState(0);
const fileUploadRef = useRef(null);
const [folderPath, setFolderPath] = useState('');
const [uploadMethod, setUploadMethod] = useState(false);
const [loading,setLoading] =useState(false);
// true for "Upload Method", false for "Choose Folder Method"
const MAX_FILE_SIZE = 50 *1024 * 1024;
const URL = 'http://192.168.151.24:2222'
// const URL = 'http://localhost:5000'
const FIXED_PATH = 'D:\\V2_SourceCode\\attributesdetection\\similar-image-clustering-main\\random';
useEffect(()=>{
setUploadMethod(uploadMethod)
},[uploadMethod])
const onTemplateSelect = (e) => {
let _totalSize = totalSize;
let files = e.files;
let exceeded = false;
// Calculate total size of selected files
Object.keys(files).forEach((key) => {
_totalSize += files[key].size || 0;
if (_totalSize > MAX_FILE_SIZE) {
exceeded = true;
}
});
if (exceeded) {
toast.current.show({ severity: 'error', summary: 'Error', detail: 'Total file size exceeds 50 MB limit for upload feature, use choose folder method for bulk upload !!' });
fileUploadRef.current.clear(); // Clear selected files if size exceeds limit
} else {
setTotalSize(_totalSize);
}
};
const onTemplateUpload = (e) => {
let _totalSize = 0;
e.files.forEach((file) => {
_totalSize += file.size || 0;
});
setTotalSize(_totalSize);
toast.current.show({ severity: 'info', summary: 'Success', detail: 'File Uploaded' });
};
const onTemplateRemove = (file, callback) => {
setTotalSize(totalSize - file.size);
callback();
};
const onTemplateClear = () => {
setTotalSize(0);
};
const headerTemplate = (options) => {
const { className, chooseButton, uploadButton, cancelButton } = options;
const value = totalSize / 500000;
const formattedValue = fileUploadRef && fileUploadRef.current ? fileUploadRef.current.formatSize(totalSize) : '0 B';
return (
<div className={className} style={{ backgroundColor: 'transparent', display: 'flex', alignItems: 'center' }}>
{/* {chooseButton} */}
<label style={{ marginLeft: '10px' }}>{'Already fixed Folder Method'}</label>
<InputSwitch
checked={uploadMethod}
onChange={(e) => setUploadMethod(e.value)}
style={{ marginLeft: '10px' }}
/>
<label style={{ marginLeft: '10px' }}>{'Upload Method'}</label>
{uploadMethod ? (
chooseButton
) : null}
{uploadMethod && cancelButton}
{uploadMethod && <div className="flex align-items-center gap-3 ml-auto">
<span>{formattedValue} / 50 MB</span>
<ProgressBar value={value} showValue={false} style={{ width: '10rem', height: '12px' }}></ProgressBar>
</div>}
</div>
);
};
const itemTemplate = (file, props) => {
return (
uploadMethod && <div className="flex align-items-center flex-wrap pb-5">
<div className="flex align-items-center" style={{ width: '40%' }}>
<img alt={file.name} role="presentation" src={file.objectURL} width={100} />
<span className="flex flex-column text-left ml-3">
{file.name}
<small>{new Date().toLocaleDateString()}</small>
</span>
</div>
<Tag value={props.formatSize} severity="warning" className="px-3 py-2" />
<Button type="button" icon="pi pi-times" className="p-button-outlined p-button-rounded p-button-danger ml-auto" onClick={() => onTemplateRemove(file, props.onRemove)} />
</div>
);
};
const emptyTemplate = () => {
console.log(uploadMethod)
return uploadMethod ? (
<div className="flex align-items-center flex-column">
<i className="pi pi-image mt-3 p-5" style={{ fontSize: '5em', borderRadius: '50%', backgroundColor: 'var(--surface-b)', color: 'var(--surface-d)' }}></i>
<span style={{ fontSize: '1.2em', color: 'var(--text-color-secondary)' }} className="my-5">
Drag and Drop Image Here
</span>
</div>
) : (
<div style={{'display' : 'flex','flexDirection':'column','flexWrap':'nowrap','alignItems':'center','justifyContent':'space-around','gap' : '5rem'}}>
<div>
Fixed Path is : {FIXED_PATH}
</div>
<input
type="button"
className='btn btn-success ms-5'
defaultValue={'Click here to recieve result for fixed folder path'}
onClick={()=>handlePathSubmit()}
disabled={loading}
/>
</div>
); // If not uploadMethod, return null or alternative content
};
const handleImagesSubmit = async () => {
if (fileUploadRef.current) {
const files = fileUploadRef.current.getFiles();
if(files.length<1){
return;
}
const formData = new FormData();
if (!uploadMethod) {
formData.append('folderPath', folderPath); // Add folder path to form data if using "Choose Folder Method"
}
for (let i = 0; i < files.length; i++) {
formData.append('images', files[i]);
}
try {
setLoading(true);
// mapRequest('/upload-images')
const response = await axios.post(`${URL}/upload-images`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
maxContentLength: 1000 * 1024 * 1024, // 1000 MB
maxBodyLength: 1000 * 1024 * 1024, // 1000 MB
responseType: 'blob', // Set response type to blob to handle file download
});
fileUploadRef.current.clear();
setTotalSize(0);
// Create a URL for the downloaded file
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `predictions_${getCurrentTimestamp()}.xlsx`);
document.body.appendChild(link);
link.click();
} catch (error) {
console.error('Error uploading files:', error);
} finally{
setLoading(false)
}
}
};
const handlePathSubmit = async ()=>{
try {
setLoading(true);
const formData = new FormData();
formData.append('path',FIXED_PATH);
const response = await axios.post(`${URL}/uploadpath`,formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
responseType: 'blob',
});
// Create a URL for the downloaded file
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `predictions_${getCurrentTimestamp()}.xlsx`);
document.body.appendChild(link);
link.click();
} catch (error) {
console.error('Error uploading files:', error);
} finally{
setLoading(false)
}
}
const chooseOptions = { icon: 'pi pi-fw pi-images', iconOnly: true, className: 'custom-choose-btn p-button-rounded p-button-outlined' };
const uploadOptions = { icon: 'pi pi-fw pi-cloud-upload', iconOnly: true, className: 'custom-upload-btn p-button-success p-button-rounded p-button-outlined' };
const cancelOptions = { icon: 'pi pi-fw pi-times', iconOnly: true, className: 'custom-cancel-btn p-button-danger p-button-rounded p-button-outlined' };
return (
<PrimeReactProvider>
<div className="App">
{loading &&
<div className='loadingScreen'>
<div className='loader'>
<Box sx={{ width: '100%' }}>
<LinearProgress color="success" />
<LinearProgress color="success" />
</Box>
</div>
<div className='blockArea'>
<span>
Predicting the Values...
</span>
</div>
</div>}
<Toast ref={toast}></Toast>
<Tooltip target=".custom-choose-btn" content="Choose" position="bottom" />
<Tooltip target=".custom-upload-btn" content="Upload" position="bottom" />
<Tooltip target=".custom-cancel-btn" content="Clear" position="bottom" />
<FileUpload
ref={fileUploadRef}
name="demo[]"
url="/api/upload"
multiple
accept="image/*"
maxFileSize={10000000}
onUpload={onTemplateUpload}
onSelect={onTemplateSelect}
one rror={onTemplateClear}
onClear={onTemplateClear}
headerTemplate={headerTemplate}
itemTemplate={itemTemplate}
emptyTemplate={emptyTemplate}
chooseOptions={chooseOptions}
uploadOptions={uploadOptions}
cancelOptions={cancelOptions}
/>
{uploadMethod && <div className='footer'>
<input className='btn btn-success' type='button' defaultValue={'Submit'} onClick={() => handleImagesSubmit()} disabled={loading} />
</div>}
</div>
</PrimeReactProvider>
);
}
export default App;
我的烧瓶代码:
import os
import io
import json
import numpy as np
import pandas as pd
import tensorflow as tf
from flask import Flask, request, send_file, jsonify
from flask_cors import CORS
from tensorflow.keras.models import load_model
from PIL import Image
from openpyxl import Workbook
from openpyxl.drawing.image import Image as ExcelImage
app = Flask(__name__)
CORS(app)
app.config['MAX_CONTENT_LENGTH'] = 1000 * 1024 * 1024 # 1 GB
# Load the saved models
model_paths = {
'weave': './saved_model/model.h5',
'shade': './saved_model/model_shade.h5',
'majcat': './saved_model/model_majcat.h5',
'lycra': './saved_model/model_lycra.h5',
'fabweight': './saved_model/model_fabweight.h5',
'collar': './saved_model/model_collar.h5',
'yarn': './saved_model/model_yarn.h5',
'fab': './saved_model/model_fab.h5',
'fab2': './saved_model/model_fab2.h5',
'weave1': './saved_model/model_weave1.h5',
'weave3': './saved_model/model_weave3.h5',
'placketchanging': './saved_model/model_placketchanging.h5',
'bltmainstyle': './saved_model/model_bltmainstyle.h5',
'substylebelt': './saved_model/model_substylebelt.h5',
'sleevesmainstyle': './saved_model/model_sleevesmainstyle.h5',
'btfold': './saved_model/model_btfold.h5',
'set': './saved_model/model_set.h5',
'neckband': './saved_model/model_neckband.h5',
'pocket': './saved_model/model_pocket.h5',
'fit': './saved_model/model_fit.h5',
'pattern': './saved_model/model_pattern.h5',
'length': './saved_model/model_length.h5',
'mainstyle': './saved_model/model_mainstyle.h5',
'dcsubstyle': './saved_model/model_dcsubstyle.h5',
'dcedgeloop': './saved_model/model_dcedgeloop.h5',
'btnmainmvgr': './saved_model/model_btnmainmvgr.h5',
'substylebtnclr': './saved_model/model_substylebtnclr.h5',
'zip': './saved_model/model_zip.h5',
'zipcol': './saved_model/model_zipcol.h5',
'addacc': './saved_model/model_addacc.h5',
'acccol': './saved_model/model_acccol.h5',
'printtype': './saved_model/model_printtype.h5',
'printplacement': './saved_model/model_printplacement.h5',
'printstyle': './saved_model/model_printstyle.h5',
'patches': './saved_model/model_patches.h5',
'patchtype': './saved_model/model_patchtype.h5',
'embroidery': './saved_model/model_embroidery.h5',
'embtype': './saved_model/model_embtype.h5',
'placement': './saved_model/model_placement.h5',
'addacc1': './saved_model/model_addacc1.h5',
'addwash': './saved_model/model_addwash.h5',
'addwashcolor': './saved_model/model_addwashcolor.h5'
}
models = {key: load_model(path) for key, path in model_paths.items()}
# Load the mappings
def load_mappings(directory):
mappings = {}
for filename in os.listdir(directory):
if filename.endswith('.json'):
with open(os.path.join(directory, filename), 'r') as file:
mapping_name = filename.split('.')[0]
mappings[mapping_name] = json.load(file)
return mappings
mappings = load_mappings('mappings')
# Function to preprocess image
def preprocess_image(image):
image = image.resize((224, 224))
image_array = np.array(image)
image_array = np.expand_dims(image_array, axis=0)
image_array = tf.keras.applications.resnet50.preprocess_input(image_array)
return image_array
# Function to get image features using ResNet-50
def get_image_features(image_array):
resnet_model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False, pooling='avg')
features = resnet_model.predict(image_array)
return features
# Function to map the predicted indices to labels
def mapToLabels(index, mappings):
return mappings.get(str(index), "Unknown")
# Function to predict attributes for a given image
def predict_attributes(image):
image_array = preprocess_image(image)
image_features = get_image_features(image_array)
predictions = {}
for attr, model in models.items():
prediction = model.predict([image_array, image_features])
label = mapToLabels(np.argmax(prediction, axis=1)[0], mappings.get(attr, {}))
predictions[attr] = label
return predictions
# Function to save images and predictions to Excel
def save_predictions_to_excel(predictions, images):
columns = ['Filename'] + [f'{attr.capitalize()} Label' for attr in models.keys()]
df = pd.DataFrame(predictions, columns=columns)
output = io.BytesIO()
with pd.ExcelWriter(output, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name='Predictions')
workbook = writer.book
worksheet = writer.sheets['Predictions']
# Set column width to fit content including heading length
for col in worksheet.columns:
max_length = 0
column = col[0].column_letter # Get the column name
for cell in col:
try:
if cell.row == 1: # Include header length
length = len(str(cell.value))
else:
length = len(str(cell.value))
if length > max_length:
max_length = length
except:
pass
adjusted_width = (max_length + 2)
worksheet.column_dimensions[column].width = adjusted_width
for idx, (filename, img) in enumerate(images.items()):
try:
img.thumbnail((100, 100))
image_stream = io.BytesIO()
img.save(image_stream, format='PNG')
image_stream.seek(0)
excel_img = ExcelImage(image_stream)
cell_location = f'A{idx + 2}'
worksheet.add_image(excel_img, cell_location)
worksheet.row_dimensions[idx + 2].height = 100
except Exception as e:
print(f"Error processing image {filename}: {e}")
output.seek(0)
return output
@app.before_request
def before_request():
if request.content_length > app.config['MAX_CONTENT_LENGTH']:
return jsonify({'error': 'File too large'}), 413
@app.route('/upload-images', methods=['POST'])
def upload_images():
images = request.files.getlist('images')
predictions = []
image_data = {}
for image in images:
try:
img = Image.open(image)
pred_labels = predict_attributes(img)
predictions.append((image.filename, *pred_labels.values()))
image_data[image.filename] = img
except Exception as e:
print(f"Error processing image {image.filename}: {e}")
predictions.append((image.filename, *["Unknown"] * len(models)))
image_data[image.filename] = None
excel_file = save_predictions_to_excel(predictions, image_data)
return send_file(excel_file, download_name='predictions.xlsx', as_attachment=True)
@app.route('/uploadpath', methods=['POST'])
def uploadPath():
folder_path = request.form.get('path')
if not folder_path:
return jsonify({'error': 'No folder path provided'}), 400
if not os.path.isdir(folder_path):
return jsonify({'error': 'Provided path is not a directory'}), 400
image_files = [f for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))]
predictions = []
image_data = {}
for image_file in image_files:
if image_file.lower().endswith(('png', 'jpg', 'jpeg', 'gif')):
try:
with open(os.path.join(folder_path, image_file), 'rb') as img_file:
img = Image.open(img_file)
pred_labels = predict_attributes(img)
predictions.append((image_file, *pred_labels.values()))
image_data[image_file] = img
except Exception as e:
print(f"Error processing image {image_file}: {e}")
predictions.append((image_file, *["Unknown"] * len(models)))
image_data[image_file] = None
excel_file = save_predictions_to_excel(predictions, image_data)
output_folder = 'output'
if not os.path.exists(output_folder):
os.makedirs(output_folder)
backup_file_path = os.path.join(output_folder, 'predictions.xlsx')
with open(backup_file_path, 'wb') as f:
f.write(excel_file.getvalue())
return send_file(excel_file, download_name='predictions.xlsx', as_attachment=True)
@app.route('/', methods=['GET'])
def home():
return "This is an API for similar detection"
@app.errorhandler(413)
def request_entity_too_large(error):
return jsonify({'error': 'File too large'}), 413
if __name__ == '__main__':
app.config['MAX_CONTENT_LENGTH'] = 1000 * 1024 * 1024 # 1 GB
CORS(app)
app.run(debug=True)
CORS 错误(跨源资源共享)发生在的 Web 应用程序(React 前端)尝试从与其来源不同的域(Flask 后端)获取资源时。在的情况下,即使已在 Flask 代码中启用了 CORS,当上传大量图像(例如 10 到 15 张)时,问题仍然存在,这表明问题的根源可能是其他地方。
让我们解决潜在的原因和解决方案:
-
Flask CORS 配置:
-
特定来源: 确保的 Flask CORS 配置允许来自的 React 应用程序的确切来源。而不是使用通配符(
*
),请明确指定来源:```python from flask_cors import CORS
app = Flask( name ) cors = CORS(app, resources={r"/*": {"origins": "http://192.168.151.24:1111"}}) # ... 的代码 ... ```
-
凭据: 如果的 React 应用程序发送 cookie 或 HTTP 身份验证,则需要启用凭据:
python cors = CORS(app, resources={r"/*": {"origins": "http://192.168.151.24:1111", "supports_credentials": True}})
-
请求超时:
-
Flask 超时: 上传大量图像可能会导致请求超时,尤其是在网络连接速度较慢或图像文件较大的情况下。增加 Flask 服务器的超时设置。可以探索使用 Flask 扩展(如
flask-timeout
)或通过配置 WSGI 服务器(如 Gunicorn 或 uWSGI)来实现此目的。 -
Axios 超时: 同样,增加的 Axios 客户端的超时以匹配服务器端调整:
javascript axios.post(`${URL}/upload-images`, formData, { // ... 的配置 ... timeout: 60000, // 例如,超时设置为 60 秒 });
-
图像大小和处理:
-
图像压缩: 在将图像发送到服务器之前压缩图像可以显着减少上传时间和带宽消耗。请考虑在的 React 应用程序中使用客户端库来压缩图像。
-
后端优化: 分析的 Flask 后端以了解潜在的瓶颈。在处理和保存预测时优化图像处理、模型预测和 Excel 生成步骤。
-
分块上传:
-
对于大量图像,请实现分块上传,将图像分成更小的块,并单独上传。此方法可以在很大程度上防止超时,并提供更可靠的上传体验。
-
浏览器限制:
-
浏览器对最大上传文件大小和并发请求数有限制。检查的浏览器设置,并根据需要进行调整。
调试步骤:
-
网络检查: 使用浏览器的开发人员工具(网络选项卡)检查请求和响应标头。查找任何 CORS 错误或超时指示。
-
服务器日志: 检查的 Flask 服务器日志以获取有关请求处理和任何潜在错误的详细信息。
-
逐步测试: 从少量图像开始,然后逐渐增加数量以确定问题是何时开始出现的。这可以帮助查明是否存在性能瓶颈或特定阈值。
通过仔细检查的 CORS 配置、优化请求超时、考虑图像大小和必要时实现分块上传,可以有效地解决上传大量图像时遇到的 CORS 错误,并确保 React 前端和 Flask 后端之间的无缝集成。请记住彻底测试和分析以查明问题的根本原因并实施适当的解决方案。
标签:python,reactjs,flask,cors,ml From: 78809866